tenet/math-olympiad-solver
0
1import gradio as gr2 3from dataclasses import dataclass4from concurrent.futures import ThreadPoolExecutor, TimeoutError5 6import os7import re8import subprocess9import tempfile10import json11import datasets12import random13import time14from typing import Tuple, Dict, Any, List15from sympy import N, simplify16from sympy.parsing.latex import parse_latex17from openai import OpenAI18 19import base6420 21 22client = OpenAI(23 base_url=os.environ.get("SERVER_URL"),24 api_key=os.environ.get("HF_TOKEN"),25)26 27 28@dataclass29class Config:30 model_id: str # SELECT MODEL31 revision: str # SELECT REVISION32 33 # Append an optional system prompt to each problem34 system_prompt: str35 36 # Number of samples to generate per problem37 num_samples: int38 num_generations: int39 # Generation parameters40 do_sample: bool41 temperature: float42 top_p: float43 top_k: int44 max_new_tokens: int45 restart_on_fail: bool46 47 # Enable 4-bit quantization48 is_quantized: bool49 50 # Run on train or test data?51 is_submission: bool = True if os.getenv("KAGGLE_IS_COMPETITION_RERUN") else False52 validation_set: str = "kaggle-validation-set-medium"53 54 notebook_time_limit: int = 9 * 60 * 60 - 15 * 60 # 9 hours - 15 minute buffer55 56 # Debug by solving only the first problem57 debug: bool = False58 59 # Push solutions to the Hub60 push_to_hub: bool = False61 62 63class PythonREPL:64 def __init__(self, timeout=5):65 self.timeout = timeout66 67 def execute(self, query: str) -> Tuple[bool, str]:68 query = "import math\nimport numpy as np\nimport sympy as sp\n" + query69 query = query.strip().split("\n")70 if "print(" not in query[-1]:71 if "#" in query[-1]:72 query[-1] = query[-1].split("#")[0]73 query[-1] = "print(" + query[-1] + ")"74 query = "\n".join(query)75 76 with tempfile.TemporaryDirectory() as temp_dir:77 temp_file_path = os.path.join(temp_dir, "tmp.py")78 79 with open(temp_file_path, "w") as f:80 f.write(query)81 82 result = subprocess.run(83 ["python3", temp_file_path],84 capture_output=True,85 check=False,86 text=True,87 timeout=self.timeout,88 )89 90 if result.returncode == 0:91 output = result.stdout92 return True, output.strip()93 else:94 error_msg = result.stderr.strip()95 msgs = error_msg.split("\n")96 new_msgs = []97 want_next = False98 for m in msgs:99 if "Traceback" in m:100 new_msgs.append(m)101 elif m == msgs[-1]:102 new_msgs.append(m)103 elif temp_file_path in m:104 st = m.index('"/') + 1 if '"/' in m else 0105 ed = m.index(temp_file_path) + 1 if temp_file_path in m else None106 clr = m[st:ed] if not ed else m[st:]107 m = m.replace(clr, "")108 new_msgs.append(m)109 want_next = True110 elif want_next:111 new_msgs.append(m)112 want_next = False113 error_msg = "\n".join(new_msgs)114 return False, error_msg.strip()115 116 def __call__(self, query: str) -> Tuple[bool, str]:117 with ThreadPoolExecutor() as executor:118 future = executor.submit(self.execute, query)119 try:120 return future.result(timeout=self.timeout)121 except TimeoutError:122 return False, f"Timed out after {self.timeout} seconds."123 124 125def execute_completion(126 executor: PythonREPL,127 completion: str,128 return_status: bool = False,129 last_code_block: bool = False,130) -> str | Tuple[str, bool]:131 # executions = ["!" + code for code in re.findall(r"```bash(.*?)```", completion, re.DOTALL) if "!" not in code]132 executions = re.findall(r"```python(.*?)```", completion, re.DOTALL)133 134 if len(executions) == 0: # directly return cot result135 return completion, False if return_status else completion136 else:137 if last_code_block:138 executions = [executions[-1]]139 140 # Python141 execution_outputs = []142 successes = []143 for code in executions:144 success = False145 146 if "subprocess" in code:147 output = "subprocess is not allowed"148 execution_outputs.append(output)149 successes.append(success)150 continue151 152 if "venv" in code:153 output = "venv is not allowed"154 execution_outputs.append(output)155 successes.append(success)156 continue157 158 try:159 success, output = executor(code)160 except TimeoutError as e:161 print("time out")162 output = e163 164 if not success and not return_status:165 output = ""166 167 execution_outputs.append(output)168 successes.append(success)169 170 output = str(execution_outputs[-1]).strip()171 success = successes[-1]172 173 if return_status:174 return output, success175 else:176 return output177 178 179def postprocess_completion(180 text: str, return_status: bool = False, last_code_block=False, timeout=5181) -> str | Tuple[str, bool]:182 executor = PythonREPL(timeout=timeout)183 184 result = execute_completion(executor, text, return_status=return_status, last_code_block=last_code_block)185 del executor186 187 return result188 189 190def apply_template(example: Dict[str, Any], prompt: str) -> Dict[str, Any]:191 return prompt.format(example["prompt"], "{}")192 193 194def last_boxed_only_string(string):195 """196 Extracts the last LaTeX boxed or framed expression from a string.197 Args:198 string (str): The input string containing LaTeX expressions.199 Returns:200 str or None: The last boxed or framed expression, if found;201 otherwise, None.202 """203 204 idx = string.rfind("\\boxed")205 if idx < 0:206 idx = string.rfind("\\fbox")207 if idx < 0:208 return None209 210 i = idx211 right_brace_idx = None212 num_left_braces_open = 0213 while i < len(string):214 if string[i] == "{":215 num_left_braces_open += 1216 if string[i] == "}":217 num_left_braces_open -= 1218 if num_left_braces_open == 0:219 right_brace_idx = i220 break221 i += 1222 223 if right_brace_idx is None:224 retval = None225 else:226 retval = string[idx : right_brace_idx + 1]227 228 return retval229 230 231def remove_boxed(s):232 """233 Removes the LaTeX boxed command, returning the content inside the braces.234 Args:235 s (str): The string containing a LaTeX boxed expression.236 Returns:237 str or None: The content inside the boxed command, if valid;238 otherwise, None.239 """240 241 left = "\\boxed{"242 try:243 assert s[: len(left)] == left244 assert s[-1] == "}"245 length = len(left)246 return s[length:-1]247 except Exception:248 return None249 250 251def extract_boxed_answer(pred_str, strip_double_curly_brace=False):252 """253 Extracts the answer from a LaTeX boxed expression within254 a prediction string.255 Args:256 pred_str (str): The string containing one or more LaTeX257 boxed expressions.258 strip_double_curly_brace (bool): If True, removes an additional259 layer of braces.260 Returns:261 str or None: The extracted answer, if any; otherwise, None.262 """263 264 boxed_str = last_boxed_only_string(pred_str)265 if boxed_str is None:266 return None267 answer = remove_boxed(boxed_str)268 if answer is None:269 return None270 if strip_double_curly_brace:271 match = re.match("^\{(.*)\}$", answer) # noqa: W605272 if match:273 answer = match.group(1)274 return answer275 276 277def normalize_final_answer(final_answer: str) -> str:278 """279 Normalizes a final answer string by removing or replacing various LaTeX280 and text elements.281 Args:282 final_answer (str): The answer string to normalize.283 Returns:284 str: The normalized answer string.285 """286 287 match = re.search(r"(.*?)Problem:", final_answer, flags=re.S)288 if match:289 final_answer = match.group(1) # 返回匹配的第一部分,即"Problem"之前的所有文本290 """Normalize a final answer to a quantitative reasoning question."""291 # final_answer = final_answer.split('=')[-1]292 SUBSTITUTIONS = [293 ("an ", ""),294 ("a ", ""),295 (".$", "$"),296 ("\\$", ""),297 (r"\ ", ""),298 (" ", ""),299 ("mbox", "text"),300 (",\\text{and}", ","),301 ("\\text{and}", ","),302 ("\\text{m}", "\\text{}"),303 ("\\le", "<"),304 ]305 REMOVED_EXPRESSIONS = [306 "square",307 "ways",308 "integers",309 "dollars",310 "mph",311 "inches",312 "ft",313 "hours",314 "km",315 "units",316 "\\ldots",317 "sue",318 "points",319 "feet",320 "minutes",321 "digits",322 "cents",323 "degrees",324 "cm",325 "gm",326 "pounds",327 "meters",328 "meals",329 "edges",330 "students",331 "childrentickets",332 "multiples",333 "\\text{s}",334 "\\text{.}",335 "\\text{\ns}",336 "\\text{}^2",337 "\\text{}^3",338 "\\text{\n}",339 "\\text{}",340 r"\mathrm{th}",341 r"^\circ",342 r"^{\circ}",343 r"\;",344 r",\!",345 "{,}",346 '"',347 "\\dots",348 "\n",349 "\r",350 "\f",351 "\%",352 ]353 for before, after in SUBSTITUTIONS:354 final_answer = final_answer.replace(before, after)355 for expr in REMOVED_EXPRESSIONS:356 final_answer = final_answer.replace(expr, "")357 358 # Extract answer that is in LaTeX math, is bold,359 # is surrounded by a box, etc.360 final_answer = re.sub(r"(\\text\{)(.*?)(\})", "\\2", final_answer)361 final_answer = re.sub(r"(\\textbf\{)(.*?)(\})", "\\2", final_answer)362 final_answer = re.sub(r"(\\overline\{)(.*?)(\})", "\\2", final_answer)363 final_answer = re.sub(r"(\\boxed\{)(.*)(\})", "\\2", final_answer)364 assert "\n" not in final_answer365 assert "\r" not in final_answer366 assert "\f" not in final_answer367 if len(re.findall(r"finalansweris(.*)", final_answer)) > 0:368 final_answer = re.findall(r"finalansweris(.*)", final_answer)[-1]369 370 if len(re.findall(r"answer?is:?(.*)", final_answer)) > 0:371 final_answer = re.findall(r"answer?is:?(.*)", final_answer)[-1]372 373 if len(re.findall(r"oxed\{(.*?)\}", final_answer)) > 0:374 final_answer = re.findall(r"oxed\{(.*?)\}", final_answer)[-1]375 376 if len(re.findall(r"\$(.*?)\$", final_answer)) > 0:377 final_answer = re.findall(r"\$(.*?)\$", final_answer)[-1]378 final_answer = final_answer.strip()379 if "rac" in final_answer and "\\frac" not in final_answer:380 final_answer = final_answer.replace("rac", "\\frac")381 382 final_answer = re.sub(r"(frac)([^{])(.)", "frac{\\2}{\\3}", final_answer)383 final_answer = re.sub(r"(sqrt)([^{])", "sqrt{\\2}", final_answer)384 final_answer = final_answer.replace("$", "")385 386 if final_answer.replace(",", "").isdigit():387 final_answer = final_answer.replace(",", "")388 389 return final_answer390 391 392def naive_parse(answer: str) -> str:393 """394 Extracts and returns the numeric digits from the input string, processing them in reverse order395 until a non-numeric character is encountered after encountering the first numeric character.396 397 Args:398 answer (str): The input string to parse.399 400 Returns:401 str: A string consisting of the numeric digits extracted from the input, in their original order.402 403 Example:404 >>> naive_parse("abc123def")405 '123'406 >>> naive_parse("def456ghi")407 '456'408 >>> naive_parse("no numbers here")409 ''410 """411 out = []412 start = False413 end = False414 for l in reversed(list(answer)):415 if l in "0123456789" and not end:416 start = True417 out.append(l)418 else:419 if start:420 end = True421 422 out = reversed(out)423 return "".join(out)424 425 426def validate_answer_is_numeric(x: str | int | float) -> int:427 FLOAT_TOLERANCE = 0.2428 try:429 x = round(float(x))430 f = float(x)431 if abs(x - f) > FLOAT_TOLERANCE:432 x = -1433 except Exception:434 x = -1435 return x436 437 438def filter_answers(answers: List[str]) -> List[int]:439 formatted_answers = [validate_answer_is_numeric(a) for a in answers]440 441 # Filter for non-negative answers442 formatted_answers = [a for a in formatted_answers if a >= 0]443 # Compute modulo444 formatted_answers = [a % 1_000 for a in formatted_answers]445 # less than 2.1 billion or cannot convert to C int (32-bit)446 formatted_answers = [a for a in formatted_answers if a <= 999]447 return formatted_answers448 449 450def check_sympy_equivalence(ref_answer: str, model_answer: str) -> bool:451 def do_answers_match(ref_answer: str, model_answer: str) -> bool:452 ref_sympy = parse_latex(ref_answer)453 model_sympy = parse_latex(model_answer)454 diff = simplify(ref_sympy - model_sympy)455 return True if -1e-12 < N(diff) < 1e-12 or diff.is_zero else False456 457 try:458 result = do_answers_match(ref_answer, model_answer)459 return result460 except Exception as e:461 print(e)462 return False463 464 465def check_string_match(ref_answer: str, model_answer: str) -> bool:466 try:467 return ref_answer == model_answer468 except Exception as e:469 print(e)470 return False471 472 473def check_answer(ref_answer: str, model_answer: str) -> bool:474 # check if strings are the same475 correct = check_string_match(ref_answer, model_answer)476 if correct:477 return True478 479 # use the sympy library to check if the expressions are the same480 correct = check_sympy_equivalence(ref_answer, model_answer)481 if correct:482 return True483 484 return False485 486 487debug = False488model_id = "Numina-Math-7B"489revision = "main"490system_prompt = "{}"491validation_set = "kaggle-validation-set-medium"492is_submission = True493num_samples = 4494num_generations = 4495temperature = 0.8496is_quantized = False497restart_on_fail = False498top_p = 1.0499top_k = 0500max_new_tokens = 2048501# Papermill related variables502push_to_hub = False503notebook_name = ""504 505config = Config(506 debug=debug,507 push_to_hub=push_to_hub,508 model_id=model_id,509 revision=revision,510 system_prompt=system_prompt,511 validation_set=validation_set,512 is_quantized=is_quantized,513 restart_on_fail=restart_on_fail,514 is_submission=is_submission,515 num_samples=num_samples,516 num_generations=num_generations,517 do_sample=True,518 temperature=temperature,519 top_p=top_p,520 top_k=top_k,521 max_new_tokens=max_new_tokens,522)523print(f"=== Running submission with config ===\n\n{config}")524 525 526def generate(message, temperature):527 """528 Generates a chat completion response by streaming data from the client chat model.529 530 This function streams the response from the client chat model and yields the content531 of the response chunk by chunk. If an error occurs, it yields the error message.532 533 Parameters:534 message (str): The input message to be sent to the chat model.535 temperature (float): The sampling temperature to use. Higher values mean the model will take more risks.536 537 Yields:538 tuple: A tuple containing the content of the response and a boolean flag indicating if an error occurred.539 If no error occurred, the boolean flag will be False and the content will be the response text.540 If an error occurred, the boolean flag will be True and the content will be the error message.541 """542 stream = client.chat.completions.create(543 model="tgi",544 messages=message,545 stream=True,546 max_tokens=1024,547 stop=["```output\n"],548 temperature=temperature,549 timeout=30,550 )551 552 response = stream.response553 554 # The reason why the library method is not used here is that if an error occurs,555 # the returned data will not be a stream, and using the official library will result in an error.556 for chunk in response.iter_bytes():557 chunk = chunk.decode("utf-8")558 chune_json = json.loads(chunk.replace("data:", ""))559 try:560 if "error" in chune_json and chune_json["error"]:561 yield chune_json["error"], True562 break563 564 content = chune_json["choices"][0]["delta"]["content"]565 if content is not None:566 yield content, False567 except Exception as e:568 print(f"func: generate error occurred\njson:{chune_json}\nerror:{e}")569 yield "", True570 571 572def get_majority_text(data):573 from collections import Counter574 575 # Count the frequency of each answer in model_answers576 answer_counts = Counter(data["model_answers"])577 578 # Find the majority response579 majority_response = answer_counts.most_common(1)[0][0]580 581 # Find the index of the first occurrence of the majority response582 majority_index = data["model_answers"].index(majority_response)583 584 # Return the corresponding text in gen_texts585 return data["gen_texts"][majority_index]586 587 588def extract_solution(text):589 # Split the text at "### Solution:"590 parts = text.split("### Solution:", 1)591 if len(parts) > 1:592 # Return everything after "### Solution:"593 return parts[1].strip()594 else:595 # Return an empty string if "### Solution:" is not found596 return ""597 598 599def process_code(600 example: Dict[str, Any],601 config: Config,602 restart_on_fail: bool = False,603 last_step: bool = False,604) -> Dict[str, Any]:605 gen_text = example["gen_texts"]606 num_python_blocks = len(re.findall(r"```python(.*?)```", gen_text, re.DOTALL))607 608 if num_python_blocks == 0:609 if restart_on_fail:610 print("no code has ever been generated, RESTARTING")611 # reset the text to the original612 example["gen_texts"] = example["text"]613 else:614 print("no code has ever been generated, STOP")615 example["should_prune"] = True616 example["has_code"] = False617 return example618 619 if gen_text[-10:] != "```output\n" and ("answer is" in gen_text[-100:] or "\\boxed" in gen_text[-100:]):620 num_output_blocks = len(re.findall(r"```output(.*?)```", gen_text, re.DOTALL))621 if num_output_blocks == 0:622 print("the model hallucinated the code answer")623 example["should_prune"] = True624 return example625 626 if "boxed" in gen_text[-100:]:627 try:628 answer = normalize_final_answer(extract_boxed_answer(gen_text[-100:]))629 except Exception:630 answer = "-1"631 else:632 answer = normalize_final_answer(gen_text[-100:])633 634 example["model_answers"] = answer635 if not config.is_submission:636 example["corrects"] = check_answer(example["ground_truth"], answer)637 example["should_prune"] = True638 print("Answer is: ", answer, example["ground_truth"], example["corrects"])639 return example640 641 if last_step:642 # no point in continuing if we are at the last step643 return example644 645 if gen_text[-10:] != "```output\n":646 # something else has gone wrong with the generation647 print("warning: output block not found: ", gen_text[-40:])648 if restart_on_fail:649 example["gen_texts"] = example["text"]650 else:651 example["should_prune"] = True652 return example653 654 code_result, status = postprocess_completion(gen_text, return_status=True, last_code_block=True)655 # add the code result for the next round of generation656 TRUNCATION_LIMIT = 200657 if len(code_result) > TRUNCATION_LIMIT:658 code_result = code_result[:TRUNCATION_LIMIT] + " ... (output truncated)"659 example["gen_texts"] = gen_text + f"{code_result}\n```"660 661 return example662 663 664def solve_problem(problem, temperature, progress=gr.Progress()):665 """666 yield token: string, stop: bool667 """668 problem = apply_template({"prompt": problem}, prompt=config.system_prompt)669 print(f"Problem: {problem}")670 671 sample = {672 "problem": problem, # not used for the submission TODO Remove673 "ground_truth": "unknown", # not used for the submission TODO Remove674 "text": "## Solution:\n",675 "gen_texts": "## Solution:\n", # used to store all the generated text676 "should_prune": False,677 "problem_index": -1, # not used for the submission TODO Remove678 "model_answers": "-1",679 "has_code": True,680 "corrects": False, # not used for the submission TODO Remove681 }682 683 for step in progress.tqdm(684 range(config.num_generations), desc="Generating candidates"685 ): # Depth of the tree (e.g. 6 steps = 5 code blocks)686 687 step_reponse = sample["gen_texts"]688 689 messages = [690 {"role": "user", "content": sample["problem"]},691 {"role": "assistant", "content": sample["gen_texts"]},692 ]693 694 for reponse_message, error in generate(messages, temperature):695 if reponse_message is not None:696 step_reponse += reponse_message697 yield step_reponse, False698 699 if error:700 yield step_reponse, True701 return702 703 sample["gen_texts"] = step_reponse704 705 # TODO: Maybe it should just return the result of running the code706 sample = process_code(707 sample,708 config=config,709 restart_on_fail=config.restart_on_fail,710 last_step=(step == (config.num_generations - 1)),711 )712 sample["gen_texts"] = sample["gen_texts"] + "\n"713 714 run_code_reponse = sample["gen_texts"].replace(step_reponse, "")715 716 for output_mseeage in run_code_reponse:717 if output_mseeage is not None:718 step_reponse += output_mseeage719 yield step_reponse, False720 721 if sample["should_prune"]:722 break723 724 yield sample["gen_texts"], True725 726 727example_data = datasets.load_dataset(728 "AI-MO/kaggle-validation-set-medium-extended",729 split="train",730 use_auth_token=os.environ.get("HF_DATASET_TOKEN", None),731)732 733 734with open("app.css", "r") as f:735 css = f.read()736 737 738latex_delimiters = [739 {"left": "[", "right": "]", "display": True},740]741 742 743def get_random_problem():744 example = random.choice(list(example_data))745 problem = example["problem"]746 return problem747 748 749def update_example_problem():750 problem_example_text = get_random_problem()751 return problem_example_text, problem_example_text752 753 754def clear():755 problem_example_text = get_random_problem()756 return "", 0.1, "", problem_example_text, problem_example_text757 758 759def preprocess_output(text):760 return text.replace(r"\(", r"\\(").replace(r"\)", r"\\)")761 762 763with gr.Blocks(css=css, title="Math Olympiad Solver") as demo:764 running_done = False765 btn_list = []766 problem_input_ele_list = []767 768 problem_example_text = get_random_problem()769 770 with gr.Row(elem_classes="title"):771 gr.HTML("Math Olympiad Solver", elem_classes="title-content")772 773 with gr.Row(elem_classes="sub-title"):774 gr.HTML(775 "<div>Demo of the <a href='https://huggingface.co/AI-MO/NuminaMath-7B-TIR'>Numina-Math-7B-TIR</a>. Example data are drawn randomly from AMC12, year 2022-2023.</div>",776 elem_classes="sub-title-content",777 )778 779 with gr.Row(elem_classes="main-area"):780 with gr.Column(scale=1, elem_classes="left"):781 with gr.Row(elem_classes="probelm-example-container"):782 with gr.Blocks(elem_classes="probelm-example-title"):783 gr.HTML("Problem example", elem_classes="probelm-example-title-content")784 785 with gr.Blocks(elem_classes="action-container"):786 another_btn = gr.Button(787 "",788 elem_classes="probelm-example-another",789 icon="./static/images/reset.png",790 )791 copy_btn = gr.Button("Copy", elem_classes="probelm-example-copy")792 793 problem_example = gr.HTML(794 problem_example_text,795 elem_classes="probelm-example-content",796 )797 798 with gr.Row(elem_classes="probelm-input-container"):799 inp = gr.Textbox(placeholder="Problem", label="Problem input", lines=5, visible=True)800 problem_markdown = gr.Markdown(801 visible=False,802 latex_delimiters=[803 {"left": "[", "right": "]", "display": True},804 {"left": "$", "right": "$", "display": False},805 {"left": r"\(", "right": r"\)", "display": False},806 ],807 )808 809 inp.change(fn=lambda text: text, inputs=[inp], outputs=[problem_markdown])810 problem_input_ele_list.append(inp)811 problem_input_ele_list.append(problem_markdown)812 813 with gr.Accordion("Advanced Options", open=False):814 temperature = gr.Slider(minimum=0.0, maximum=1.0, value=0.1, step=0.1, label="Temperature")815 816 with gr.Row() as btn_area:817 btn_clear = gr.Button("Clear", elem_classes="clear-btn")818 btn_run = gr.Button("Run", elem_classes="run-btn")819 btn_list.append(btn_clear)820 btn_list.append(btn_run)821 822 with gr.Column(scale=1, elem_classes="right"):823 gr.HTML("Solution", elem_classes="solution-title-content")824 out = gr.Markdown(825 elem_classes="solution-content",826 latex_delimiters=[827 {"left": "[", "right": "]", "display": True},828 {"left": "$", "right": "$", "display": False},829 {"left": r"\(", "right": r"\)", "display": False},830 ],831 )832 833 problem_example_text_hidden = gr.Markdown(value=problem_example_text, visible=False)834 835 def solve_problem_wrapper(inp_text, temperature):836 global running_done837 try:838 for after_tokens, stop in solve_problem(inp_text, temperature):839 yield preprocess_output(after_tokens)840 841 if stop:842 running_done = True843 except Exception as e:844 running_done = True845 raise e846 847 def mount_run_btn(btn):848 btn.click(fn=solve_problem_wrapper, inputs=[inp, temperature], outputs=out)849 btn.click(get_running_btns, None, outputs=btn_list)850 btn.click(get_run_after_problem_input, None, outputs=problem_input_ele_list)851 852 def get_run_after_problem_input():853 return gr.Textbox(placeholder="Problem", label="Problem input", lines=5, visible=False), gr.Markdown(854 visible=True,855 latex_delimiters=[856 {"left": "[", "right": "]", "display": True},857 {"left": "$", "right": "$", "display": False},858 ],859 elem_classes="problem-input-markdown",860 )861 862 def get_init_problem_input():863 return gr.Textbox(placeholder="Problem", label="Problem input", lines=5, visible=True), gr.Markdown(864 visible=False,865 latex_delimiters=[866 {"left": "[", "right": "]", "display": True},867 {"left": "$", "right": "$", "display": False},868 ],869 )870 871 def get_running_btns():872 global running_done873 874 btn_clear = gr.Button("Clear")875 btn_run = gr.Button("", elem_classes="run-btn running-btn")876 yield [btn_clear, btn_run]877 878 time.sleep(3)879 880 btn_clear = gr.Button("Clear")881 btn_run = gr.Button("Run", elem_classes="run-btn")882 883 while True:884 if running_done:885 running_done = False886 yield [btn_clear, btn_run]887 time.sleep(1)888 mount_run_btn(btn_run)889 break890 891 time.sleep(1)892 893 copy_btn.click(fn=lambda example: example, inputs=[problem_example_text_hidden], outputs=[inp])894 895 btn_clear.click(896 fn=clear,897 inputs=[],898 outputs=[899 inp,900 temperature,901 out,902 problem_example,903 problem_example_text_hidden,904 ],905 )906 907 btn_clear.click(get_init_problem_input, None, outputs=problem_input_ele_list)908 909 mount_run_btn(btn_run)910 911 demo.load(912 update_example_problem,913 inputs=None,914 outputs=[915 problem_example,916 problem_example_text_hidden,917 ],918 )919 920 another_btn.click(921 fn=update_example_problem,922 inputs=[],923 outputs=[924 problem_example,925 problem_example_text_hidden,926 ],927 )928 929if __name__ == "__main__":930 demo.queue(default_concurrency_limit=5).launch()931 