Update app.py
Browse files
app.py
CHANGED
|
@@ -2,12 +2,14 @@ import gradio as gr
|
|
| 2 |
import os
|
| 3 |
import requests
|
| 4 |
import json
|
| 5 |
-
import
|
| 6 |
-
from PIL import Image
|
|
|
|
| 7 |
|
| 8 |
GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
|
| 9 |
SPACE_GROQ_KEY = os.environ.get("GROQ_API_KEY", "")
|
| 10 |
-
|
|
|
|
| 11 |
|
| 12 |
SYSTEM_PROMPT = """You are a warm, patient math tutor for students from Grade 1 through High School.
|
| 13 |
Rules you ALWAYS follow:
|
|
@@ -36,22 +38,10 @@ Always format your response exactly like this:
|
|
| 36 |
π‘ [One encouraging sentence]"""
|
| 37 |
|
| 38 |
|
| 39 |
-
def
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
img = pil_image.resize((w * scale, h * scale), Image.LANCZOS)
|
| 44 |
-
img = img.convert("L")
|
| 45 |
-
img = ImageEnhance.Contrast(img).enhance(3.0)
|
| 46 |
-
img = ImageEnhance.Sharpness(img).enhance(2.0)
|
| 47 |
-
img = img.filter(ImageFilter.SHARPEN)
|
| 48 |
-
text = pytesseract.image_to_string(
|
| 49 |
-
img,
|
| 50 |
-
config="--psm 6 --oem 3 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.+=-/() "
|
| 51 |
-
).strip()
|
| 52 |
-
return text if len(text) > 10 else None
|
| 53 |
-
except Exception:
|
| 54 |
-
return None
|
| 55 |
|
| 56 |
|
| 57 |
def solve_math(image_input, text_input, grade_level, user_key):
|
|
@@ -75,35 +65,31 @@ def solve_math(image_input, text_input, grade_level, user_key):
|
|
| 75 |
return
|
| 76 |
|
| 77 |
grade_note = f"Grade level: {grade_level}."
|
| 78 |
-
ocr_text = ""
|
| 79 |
-
|
| 80 |
-
if has_image:
|
| 81 |
-
yield "π Reading text from your worksheet image..."
|
| 82 |
-
ocr_text = ocr_image(image_input)
|
| 83 |
-
if ocr_text:
|
| 84 |
-
yield f"π **Extracted from image:**\n```\n{ocr_text}\n```\n\nβ‘ Solving step by step..."
|
| 85 |
-
else:
|
| 86 |
-
if not has_text:
|
| 87 |
-
yield (
|
| 88 |
-
"β οΈ **Could not read text from the image clearly.**\n\n"
|
| 89 |
-
"Please type the problem in the text box below and try again."
|
| 90 |
-
)
|
| 91 |
-
return
|
| 92 |
-
yield "β‘ Solving step by step..."
|
| 93 |
-
else:
|
| 94 |
-
yield "β‘ Solving step by step..."
|
| 95 |
|
| 96 |
-
|
| 97 |
-
if ocr_text:
|
| 98 |
-
parts.append(f"Text extracted from worksheet image:\n{ocr_text}")
|
| 99 |
-
if has_text:
|
| 100 |
-
parts.append(f"Student's note: {text_input.strip()}")
|
| 101 |
-
if not parts:
|
| 102 |
-
parts.append(f"Problem: {text_input.strip()}")
|
| 103 |
-
|
| 104 |
-
user_msg = f"{grade_note}\n\n" + "\n\n".join(parts) + "\n\nPlease teach me how to solve this step by step."
|
| 105 |
|
| 106 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
response = requests.post(
|
| 108 |
GROQ_API_URL,
|
| 109 |
headers={
|
|
@@ -111,10 +97,10 @@ def solve_math(image_input, text_input, grade_level, user_key):
|
|
| 111 |
"Content-Type": "application/json",
|
| 112 |
},
|
| 113 |
json={
|
| 114 |
-
"model":
|
| 115 |
"messages": [
|
| 116 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 117 |
-
{"role": "user", "content":
|
| 118 |
],
|
| 119 |
"max_tokens": 1200,
|
| 120 |
"temperature": 0,
|
|
@@ -134,11 +120,7 @@ def solve_math(image_input, text_input, grade_level, user_key):
|
|
| 134 |
yield f"β API error {response.status_code}: {response.text[:200]}"
|
| 135 |
return
|
| 136 |
|
| 137 |
-
|
| 138 |
-
if ocr_text:
|
| 139 |
-
prefix = f"π **Extracted from image:**\n```\n{ocr_text}\n```\n\n"
|
| 140 |
-
|
| 141 |
-
output = prefix
|
| 142 |
for raw in response.iter_lines():
|
| 143 |
if not raw:
|
| 144 |
continue
|
|
@@ -155,8 +137,8 @@ def solve_math(image_input, text_input, grade_level, user_key):
|
|
| 155 |
except Exception:
|
| 156 |
continue
|
| 157 |
|
| 158 |
-
if
|
| 159 |
-
yield
|
| 160 |
|
| 161 |
except requests.exceptions.Timeout:
|
| 162 |
yield "β Request timed out. Please try again."
|
|
@@ -249,7 +231,7 @@ with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
|
|
| 249 |
|
| 250 |
gr.HTML('<div class="panel-label" style="margin-top:16px">π Upload Worksheet</div>')
|
| 251 |
image_input = gr.Image(type="pil", label="", height=180)
|
| 252 |
-
gr.HTML('<div style="font-size:.78rem;color:var(--muted);margin-top:4px">
|
| 253 |
|
| 254 |
gr.HTML('<div class="panel-label" style="margin-top:14px">βοΈ Or Type Your Problem</div>')
|
| 255 |
text_input = gr.Textbox(label="", placeholder="e.g. Solve for x: 5x β 3 = 22", lines=3)
|
|
|
|
| 2 |
import os
|
| 3 |
import requests
|
| 4 |
import json
|
| 5 |
+
import base64
|
| 6 |
+
from PIL import Image
|
| 7 |
+
from io import BytesIO
|
| 8 |
|
| 9 |
GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
|
| 10 |
SPACE_GROQ_KEY = os.environ.get("GROQ_API_KEY", "")
|
| 11 |
+
TEXT_MODEL = "llama-3.3-70b-versatile"
|
| 12 |
+
VISION_MODEL = "llama-3.2-90b-vision-preview"
|
| 13 |
|
| 14 |
SYSTEM_PROMPT = """You are a warm, patient math tutor for students from Grade 1 through High School.
|
| 15 |
Rules you ALWAYS follow:
|
|
|
|
| 38 |
π‘ [One encouraging sentence]"""
|
| 39 |
|
| 40 |
|
| 41 |
+
def image_to_base64(pil_image):
|
| 42 |
+
buffered = BytesIO()
|
| 43 |
+
pil_image.save(buffered, format="PNG")
|
| 44 |
+
return base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
|
| 47 |
def solve_math(image_input, text_input, grade_level, user_key):
|
|
|
|
| 65 |
return
|
| 66 |
|
| 67 |
grade_note = f"Grade level: {grade_level}."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
+
yield "β‘ Solving step by step..."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
try:
|
| 72 |
+
if has_image:
|
| 73 |
+
# Use vision model to read the image directly
|
| 74 |
+
img_b64 = image_to_base64(image_input)
|
| 75 |
+
text_note = f"\nStudent's additional note: {text_input.strip()}" if has_text else ""
|
| 76 |
+
user_content = [
|
| 77 |
+
{
|
| 78 |
+
"type": "image_url",
|
| 79 |
+
"image_url": {
|
| 80 |
+
"url": f"data:image/png;base64,{img_b64}"
|
| 81 |
+
}
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"type": "text",
|
| 85 |
+
"text": f"{grade_note}\n\nPlease read this math problem from the image carefully, identify all numbers and labels, then teach me how to solve it step by step.{text_note}"
|
| 86 |
+
}
|
| 87 |
+
]
|
| 88 |
+
model = VISION_MODEL
|
| 89 |
+
else:
|
| 90 |
+
user_content = f"{grade_note}\n\nProblem: {text_input.strip()}\n\nPlease teach me how to solve this step by step."
|
| 91 |
+
model = TEXT_MODEL
|
| 92 |
+
|
| 93 |
response = requests.post(
|
| 94 |
GROQ_API_URL,
|
| 95 |
headers={
|
|
|
|
| 97 |
"Content-Type": "application/json",
|
| 98 |
},
|
| 99 |
json={
|
| 100 |
+
"model": model,
|
| 101 |
"messages": [
|
| 102 |
{"role": "system", "content": SYSTEM_PROMPT},
|
| 103 |
+
{"role": "user", "content": user_content},
|
| 104 |
],
|
| 105 |
"max_tokens": 1200,
|
| 106 |
"temperature": 0,
|
|
|
|
| 120 |
yield f"β API error {response.status_code}: {response.text[:200]}"
|
| 121 |
return
|
| 122 |
|
| 123 |
+
output = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
for raw in response.iter_lines():
|
| 125 |
if not raw:
|
| 126 |
continue
|
|
|
|
| 137 |
except Exception:
|
| 138 |
continue
|
| 139 |
|
| 140 |
+
if not output:
|
| 141 |
+
yield "β οΈ No response received. Please try again."
|
| 142 |
|
| 143 |
except requests.exceptions.Timeout:
|
| 144 |
yield "β Request timed out. Please try again."
|
|
|
|
| 231 |
|
| 232 |
gr.HTML('<div class="panel-label" style="margin-top:16px">π Upload Worksheet</div>')
|
| 233 |
image_input = gr.Image(type="pil", label="", height=180)
|
| 234 |
+
gr.HTML('<div style="font-size:.78rem;color:var(--muted);margin-top:4px">πΈ Images are read directly by AI β diagrams, triangles, and graphs all work!</div>')
|
| 235 |
|
| 236 |
gr.HTML('<div class="panel-label" style="margin-top:14px">βοΈ Or Type Your Problem</div>')
|
| 237 |
text_input = gr.Textbox(label="", placeholder="e.g. Solve for x: 5x β 3 = 22", lines=3)
|