Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import sympy as sp | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForCausalLM | |
| MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct" | |
| SYSTEM_PROMPT = "You are a helpful tutor. Match the user's level." | |
| tok = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, | |
| dtype=torch.float32, # CPU | |
| device_map=None | |
| ) | |
| model.eval() | |
| # SymPy check | |
| def verify_math(expr_str: str) -> str: | |
| try: | |
| expr = sp.sympify(expr_str) | |
| simplified = sp.simplify(expr) | |
| return f"Simplified: {simplified}" | |
| except Exception as e: | |
| return f"Could not verify with SymPy: {e}" | |
| # Main function which processes the question | |
| def generate(question: str, level: str, step_by_step: bool) -> str: | |
| if not question.strip(): | |
| return "Please enter a question." | |
| # style forming | |
| style = f"Level: {level}. {'Explain step-by-step.' if step_by_step else 'Be concise.'}" | |
| # dynamic amount of tokens | |
| max_tokens = 128 if level == "Beginner" else 192 if level == "Intermediate" else 256 | |
| # final prompt | |
| prompt = ( | |
| f"{SYSTEM_PROMPT}\n" | |
| f"{style}\n" | |
| f"User question: {question}\n" | |
| f"Assistant:" | |
| ) | |
| inputs = tok(prompt, return_tensors="pt") | |
| with torch.no_grad(): | |
| out = model.generate( | |
| **inputs, | |
| max_new_tokens=max_tokens, | |
| do_sample=False, | |
| pad_token_id=tok.eos_token_id | |
| ) | |
| # decode the answer | |
| text = tok.decode(out[0], skip_special_tokens=True) | |
| if "Assistant:" in text: | |
| text = text.split("Assistant:", 1)[1].strip() | |
| # check if it is a math task | |
| is_math = any(ch in question for ch in "+-*/=^") or question.lower().startswith(( | |
| "simplify", "derive", "integrate", "laske", "sievennä", "derivoi", "integroi" | |
| )) | |
| sympy_note = verify_math(question) if is_math else "No math verification needed." | |
| return f"{text}\n\n---\n**SymPy check:** {sympy_note}\n\n_Status: Transformers CPU_" | |
| # Building app and IU | |
| def build_app(): | |
| with gr.Blocks(title="LearnLoop — CPU Space") as demo: | |
| # CSS styles and adding colours | |
| gr.HTML(""" | |
| <style> | |
| .gradio-container { | |
| background-color: #EDF6FA !important; /* haalea sininen */ | |
| padding: 24px; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.05); | |
| } | |
| /* buttons */ | |
| button { | |
| border-radius: 8px; | |
| transition: all 0.2s ease-in-out; | |
| font-weight: 500; | |
| letter-spacing: 0.5px; | |
| } | |
| button:hover { | |
| opacity: 0.9; | |
| transform: translateY(-1px); | |
| } | |
| button:active { | |
| filter: brightness(85%); | |
| transform: scale(0.98); | |
| } | |
| /* Explain ja Reset buttons */ | |
| #explain-btn { | |
| background-color: #5499C7; | |
| color: white; | |
| border: 2px solid #2E86C1; | |
| } | |
| #reset-btn { | |
| background-color: #EC7063; | |
| color: white; | |
| border: 2px solid #CB4335; | |
| } | |
| #explain-btn:hover, #reset-btn:hover { | |
| opacity: 0.85; | |
| } | |
| #explain-btn:active, #reset-btn:active { | |
| filter: brightness(85%); | |
| transform: scale(0.98); | |
| } | |
| </style> | |
| """) | |
| # prints using instructions | |
| gr.Markdown(""" | |
| # **LearnL**<span style="font-size:1.2em; color: #21618C">∞</span>**p — AI Tutor** | |
| This app uses the [Qwen 2.5 model](https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct) | |
| to explain questions at different skill levels. It can also verify | |
| mathematical expressions using the SymPy library. | |
| **How to use:** | |
| 1️⃣ Type your question or a mathematical expression. | |
| 2️⃣ Select your level (Beginner, Intermediate, Advanced). | |
| 3️⃣ Choose whether you want a step-by-step explanation. | |
| 4️⃣ Press **"Explain"** or **Enter** on your keyboard. | |
| 5️⃣ If you want to enter a new question, you can press **"Reset"** or simply **type a new question**. | |
| 💬 You can ask your question in **English**. | |
| """) | |
| # User's feed | |
| q = gr.Textbox(label="Your question", placeholder="e.g., simplify (x^2 - 1)/(x - 1)", elem_id="question-box") | |
| level = gr.Dropdown(choices=["Beginner", "Intermediate", "Advanced"], value="Beginner", label="Level") | |
| step = gr.Checkbox(value=True, label="Step-by-step") | |
| # Results | |
| loading = gr.Markdown(visible=False) # spinner hided at first | |
| out = gr.Markdown() | |
| # Buttons next to each other | |
| with gr.Row(): | |
| btn = gr.Button("Explain", elem_id="explain-btn") | |
| reset_btn = gr.ClearButton([q, out, loading], value="Reset", elem_id="reset-btn") | |
| # connect to generate function with spinner | |
| def wrapped_generate(q_val, level_val, step_val): | |
| # Näytetään spinner ensin | |
| loading_text = "⏳ Generating explanation..." | |
| result = generate(q_val, level_val, step_val) | |
| # hide spinner when ready | |
| return "", result | |
| btn.click(fn=wrapped_generate, inputs=[q, level, step], outputs=[loading, out]) | |
| q.submit(fn=wrapped_generate, inputs=[q, level, step], outputs=[loading, out]) | |
| return demo | |
| # start the app | |
| if __name__ == "__main__": | |
| build_app().launch() | |