CoolFace
Apppublic

tenet/math-olympiad-solver

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py931 linesDownload Raw Back to root
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