vishwakayireddi1234 commited on
Commit
c6be2bd
Β·
verified Β·
1 Parent(s): 87fa1e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -100
app.py CHANGED
@@ -4,12 +4,12 @@ import os
4
  from io import BytesIO
5
  from huggingface_hub import InferenceClient
6
 
7
- HF_TOKEN = os.environ.get("HF_TOKEN", "")
 
 
8
 
9
- # These models are fully open, no license gate, confirmed free on hf-inference
10
- TEXT_MODEL = "HuggingFaceH4/zephyr-7b-beta"
11
- VISION_MODEL = "Salesforce/blip2-opt-2.7b" # for OCR/description of image
12
- FALLBACK_TEXT = "microsoft/Phi-3-mini-4k-instruct"
13
 
14
  SYSTEM_PROMPT = """You are a warm, patient math tutor for students from Grade 1 through High School.
15
 
@@ -53,26 +53,21 @@ def image_to_base64(pil_image):
53
  return None
54
 
55
 
56
- def get_client(model):
57
- """Return an InferenceClient, with token if available."""
58
- if HF_TOKEN:
59
- return InferenceClient(model=model, token=HF_TOKEN, provider="hf-inference")
60
- return InferenceClient(model=model, provider="hf-inference")
61
-
62
-
63
- def describe_image(pil_image):
64
- """Use image-to-text to extract math problem text from a worksheet image."""
65
- try:
66
- client = get_client(VISION_MODEL)
67
- b64 = image_to_base64(pil_image)
68
- # Use image-to-text (captioning) to describe what's in the image
69
- result = client.image_to_text(pil_image)
70
- return result if result else None
71
- except Exception as e:
72
- return None
73
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- def solve_math(image_input, text_input, grade_level):
76
  has_image = image_input is not None
77
  has_text = text_input and text_input.strip()
78
 
@@ -83,19 +78,20 @@ def solve_math(image_input, text_input, grade_level):
83
  yield "πŸ” Reading your problem..."
84
 
85
  grade_note = f"Grade level: {grade_level}."
86
- problem_text = ""
87
-
88
- # If image provided, first extract text from it
89
- if has_image:
90
- yield "πŸ–ΌοΈ Extracting problem from image..."
91
- description = describe_image(image_input)
92
- if description:
93
- problem_text = f"The worksheet image shows: {description}"
94
- else:
95
- problem_text = "A math problem from a worksheet image (could not auto-read β€” student may clarify below)"
96
 
97
- if has_text:
98
- problem_text += f"\n\nStudent's note: {text_input.strip()}"
 
 
 
 
 
 
 
 
 
 
 
99
  else:
100
  problem_text = text_input.strip()
101
 
@@ -103,54 +99,46 @@ def solve_math(image_input, text_input, grade_level):
103
 
104
  yield "⚑ Solving step by step..."
105
 
106
- # Try primary model, fall back if needed
107
- for model in [TEXT_MODEL, FALLBACK_TEXT]:
108
- try:
109
- client = get_client(model)
110
- stream = client.chat.completions.create(
111
- messages=[
112
- {"role": "system", "content": SYSTEM_PROMPT},
113
- {"role": "user", "content": full_prompt},
114
- ],
115
- max_tokens=1200,
116
- temperature=0.3,
117
- stream=True,
118
- )
119
-
120
- output = ""
121
- for chunk in stream:
122
- token = chunk.choices[0].delta.content or ""
123
- if token:
124
- output += token
125
- yield output
126
-
127
- if output:
128
- return # success β€” stop trying fallbacks
129
-
130
- # empty response β€” try next model
131
- yield f"⚠️ Model `{model}` returned empty. Trying backup...\n\n"
132
-
133
- except Exception as e:
134
- err = str(e)
135
- if "401" in err:
136
- yield (
137
- f"⚠️ Model `{model}` requires authorization. Trying backup model...\n\n"
138
- )
139
- continue
140
- elif "503" in err or "loading" in err.lower():
141
- yield "⏳ **Model is warming up.** Please wait ~30 seconds and try again."
142
- return
143
- elif "429" in err:
144
- yield "⚠️ HuggingFace is busy. Please wait a few seconds and try again."
145
- return
146
- else:
147
- yield f"⚠️ Model `{model}` failed: {err}\n\nTrying backup...\n\n"
148
- continue
149
 
150
- yield (
151
- "❌ All models failed. This is likely a temporary HuggingFace issue.\n\n"
152
- "Please try again in a minute."
153
- )
 
 
 
 
 
 
 
 
 
154
 
155
 
156
  CSS = """
@@ -163,6 +151,7 @@ CSS = """
163
  --accent: #58a6ff;
164
  --accent2: #bc8cff;
165
  --green: #3fb950;
 
166
  --text: #e6edf3;
167
  --muted: #7d8590;
168
  --border: #30363d;
@@ -180,6 +169,12 @@ body, .gradio-container { background: var(--bg) !important; font-family: 'Nunito
180
 
181
  .panel-label { font-size:.72rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin-bottom:6px; }
182
 
 
 
 
 
 
 
183
  .solve-btn { background:linear-gradient(135deg,var(--accent),var(--accent2)) !important; color:#fff !important; font-family:'Nunito',sans-serif !important; font-weight:900 !important; font-size:1.05rem !important; border:none !important; border-radius:12px !important; padding:14px 0 !important; width:100% !important; cursor:pointer !important; transition:all .2s !important; }
184
  .solve-btn:hover { transform:translateY(-2px) !important; box-shadow:0 8px 24px rgba(88,166,255,.35) !important; }
185
 
@@ -189,7 +184,11 @@ body, .gradio-container { background: var(--bg) !important; font-family: 'Nunito
189
  .output-box { background:var(--surface) !important; border:1px solid var(--border) !important; border-radius:var(--radius) !important; padding:24px !important; min-height:420px !important; font-family:'Nunito',sans-serif !important; font-size:.97rem !important; line-height:1.8 !important; color:var(--text) !important; }
190
  .output-box code { font-family:'JetBrains Mono',monospace !important; background:var(--surface2) !important; border-radius:6px !important; padding:2px 8px !important; }
191
 
192
- .info-box { background:var(--surface2); border:1px solid var(--border); border-left:3px solid var(--green); border-radius:10px; padding:12px 16px; margin-top:16px; font-size:.85rem; color:var(--muted); line-height:1.6; }
 
 
 
 
193
  .info-box strong { color:var(--green); }
194
 
195
  footer { display:none !important; }
@@ -213,12 +212,15 @@ EXAMPLES = [
213
  [None, "Differentiate f(x) = 3xΒ³ βˆ’ 2x + 7", "Grade 11–12 (Pre-Calculus, Statistics, Calculus)"],
214
  ]
215
 
 
 
 
216
  with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
217
 
218
  gr.HTML("""
219
  <div class="math-header">
220
  <h1>πŸ“ Interactive Math Tutor</h1>
221
- <p>Free Β· No sign-up Β· Step-by-step guidance from Grade 1 to High School</p>
222
  <div class="badge-row">
223
  <span class="badge">✏️ Arithmetic</span>
224
  <span class="badge">Β½ Fractions</span>
@@ -232,8 +234,36 @@ with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
232
 
233
  with gr.Row(equal_height=False):
234
  with gr.Column(scale=4, min_width=300):
235
- gr.HTML('<div class="panel-label">πŸ“Ž Upload Worksheet (optional)</div>')
236
- image_input = gr.Image(type="pil", label="", height=200)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
  gr.HTML('<div class="panel-label" style="margin-top:14px">✏️ Or Type Your Problem</div>')
239
  text_input = gr.Textbox(
@@ -249,18 +279,12 @@ with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
249
  solve_btn = gr.Button("πŸš€ Solve & Explain", elem_classes=["solve-btn"])
250
  clear_btn = gr.Button("πŸ—‘ Clear", elem_classes=["clear-btn"])
251
 
252
- gr.HTML("""
253
- <div class="info-box">
254
- <strong>βœ… 100% Free β€” no API key, no sign-up</strong><br>
255
- Powered by open-source AI on HuggingFace servers.<br>
256
- Share this page with anyone!<br><br>
257
- <strong>⏳ First response</strong> may take ~20–30s if the model just woke up.
258
- After that, answers stream in quickly.
259
- </div>
260
- """)
261
-
262
  gr.HTML('<div class="panel-label" style="margin-top:16px">πŸ’‘ Try an example</div>')
263
- gr.Examples(examples=EXAMPLES, inputs=[image_input, text_input, grade_dropdown], label="")
 
 
 
 
264
 
265
  with gr.Column(scale=6, min_width=360):
266
  gr.HTML('<div class="panel-label">πŸŽ“ Step-by-Step Solution</div>')
@@ -271,14 +295,14 @@ with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
271
 
272
  solve_btn.click(
273
  fn=solve_math,
274
- inputs=[image_input, text_input, grade_dropdown],
275
  outputs=output_display,
276
  )
277
  clear_btn.click(
278
- fn=lambda: (None, "", "Auto-detect from problem",
279
  "*Your solution will appear here.*"),
280
  inputs=[],
281
- outputs=[image_input, text_input, grade_dropdown, output_display],
282
  )
283
 
284
  if __name__ == "__main__":
 
4
  from io import BytesIO
5
  from huggingface_hub import InferenceClient
6
 
7
+ # Space owner can pre-set a token as a Secret named HF_TOKEN
8
+ # If not set, users provide their own below
9
+ SPACE_TOKEN = os.environ.get("HF_TOKEN", "")
10
 
11
+ # Mistral is open-licensed, no gate, works with any valid HF token
12
+ TEXT_MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
 
 
13
 
14
  SYSTEM_PROMPT = """You are a warm, patient math tutor for students from Grade 1 through High School.
15
 
 
53
  return None
54
 
55
 
56
+ def solve_math(image_input, text_input, grade_level, user_token):
57
+ # Decide which token to use
58
+ token = user_token.strip() if user_token and user_token.strip() else SPACE_TOKEN
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ if not token:
61
+ yield (
62
+ "πŸ”‘ **A HuggingFace token is required to use this app.**\n\n"
63
+ "It's free and takes 30 seconds:\n"
64
+ "1. Go to [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)\n"
65
+ "2. Click **New token** β†’ choose **Read** access β†’ copy it\n"
66
+ "3. Paste it in the **HuggingFace Token** box on the left\n\n"
67
+ "Your token is never stored β€” it's only used for this session."
68
+ )
69
+ return
70
 
 
71
  has_image = image_input is not None
72
  has_text = text_input and text_input.strip()
73
 
 
78
  yield "πŸ” Reading your problem..."
79
 
80
  grade_note = f"Grade level: {grade_level}."
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # Build problem text
83
+ if has_image and has_text:
84
+ problem_text = (
85
+ f"I have a worksheet image with a math problem. "
86
+ f"Here is what it says (typed by me): {text_input.strip()}\n"
87
+ f"Please teach me how to solve it step by step."
88
+ )
89
+ elif has_image:
90
+ problem_text = (
91
+ "I have a worksheet image with a math problem. "
92
+ "Please describe what kind of problem it likely is and teach me how to solve it step by step. "
93
+ "If you cannot see the image content, ask me to type the problem."
94
+ )
95
  else:
96
  problem_text = text_input.strip()
97
 
 
99
 
100
  yield "⚑ Solving step by step..."
101
 
102
+ try:
103
+ client = InferenceClient(
104
+ model=TEXT_MODEL,
105
+ token=token,
106
+ provider="hf-inference",
107
+ )
108
+
109
+ stream = client.chat.completions.create(
110
+ messages=[
111
+ {"role": "system", "content": SYSTEM_PROMPT},
112
+ {"role": "user", "content": full_prompt},
113
+ ],
114
+ max_tokens=1200,
115
+ temperature=0.3,
116
+ stream=True,
117
+ )
118
+
119
+ output = ""
120
+ for chunk in stream:
121
+ token_text = chunk.choices[0].delta.content or ""
122
+ if token_text:
123
+ output += token_text
124
+ yield output
125
+
126
+ if not output:
127
+ yield "⚠️ No response received. Please try again in a moment."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+ except Exception as e:
130
+ err = str(e)
131
+ if "401" in err:
132
+ yield (
133
+ "❌ **Invalid token.** Please check your HuggingFace token and try again.\n\n"
134
+ "Get a free token at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)"
135
+ )
136
+ elif "503" in err or "loading" in err.lower():
137
+ yield "⏳ **Model is warming up.** Please wait ~30 seconds and try again."
138
+ elif "429" in err:
139
+ yield "⚠️ HuggingFace is busy. Please wait a few seconds and try again."
140
+ else:
141
+ yield f"❌ Error: {err}"
142
 
143
 
144
  CSS = """
 
151
  --accent: #58a6ff;
152
  --accent2: #bc8cff;
153
  --green: #3fb950;
154
+ --red: #f85149;
155
  --text: #e6edf3;
156
  --muted: #7d8590;
157
  --border: #30363d;
 
169
 
170
  .panel-label { font-size:.72rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin-bottom:6px; }
171
 
172
+ .token-box textarea, .token-box input { background:var(--surface2) !important; border:1px solid var(--border) !important; border-radius:10px !important; color:var(--text) !important; font-family:'JetBrains Mono',monospace !important; font-size:.85rem !important; }
173
+ .token-box textarea:focus, .token-box input:focus { border-color:var(--accent) !important; box-shadow:0 0 0 3px rgba(88,166,255,.15) !important; }
174
+
175
+ .gr-textbox textarea, .gr-textbox input { background:var(--surface2) !important; border:1px solid var(--border) !important; border-radius:10px !important; color:var(--text) !important; font-family:'Nunito',sans-serif !important; font-size:.95rem !important; }
176
+ .gr-textbox textarea:focus { border-color:var(--accent) !important; box-shadow:0 0 0 3px rgba(88,166,255,.15) !important; }
177
+
178
  .solve-btn { background:linear-gradient(135deg,var(--accent),var(--accent2)) !important; color:#fff !important; font-family:'Nunito',sans-serif !important; font-weight:900 !important; font-size:1.05rem !important; border:none !important; border-radius:12px !important; padding:14px 0 !important; width:100% !important; cursor:pointer !important; transition:all .2s !important; }
179
  .solve-btn:hover { transform:translateY(-2px) !important; box-shadow:0 8px 24px rgba(88,166,255,.35) !important; }
180
 
 
184
  .output-box { background:var(--surface) !important; border:1px solid var(--border) !important; border-radius:var(--radius) !important; padding:24px !important; min-height:420px !important; font-family:'Nunito',sans-serif !important; font-size:.97rem !important; line-height:1.8 !important; color:var(--text) !important; }
185
  .output-box code { font-family:'JetBrains Mono',monospace !important; background:var(--surface2) !important; border-radius:6px !important; padding:2px 8px !important; }
186
 
187
+ .token-info { background:var(--surface2); border:1px solid var(--border); border-left:3px solid var(--accent); border-radius:10px; padding:12px 16px; margin-top:8px; font-size:.82rem; color:var(--muted); line-height:1.7; }
188
+ .token-info a { color:var(--accent); text-decoration:none; }
189
+ .token-info strong { color:var(--text); }
190
+
191
+ .info-box { background:var(--surface2); border:1px solid var(--border); border-left:3px solid var(--green); border-radius:10px; padding:12px 16px; margin-top:16px; font-size:.82rem; color:var(--muted); line-height:1.6; }
192
  .info-box strong { color:var(--green); }
193
 
194
  footer { display:none !important; }
 
212
  [None, "Differentiate f(x) = 3xΒ³ βˆ’ 2x + 7", "Grade 11–12 (Pre-Calculus, Statistics, Calculus)"],
213
  ]
214
 
215
+ # Whether space owner has pre-configured a token
216
+ OWNER_TOKEN_SET = bool(SPACE_TOKEN)
217
+
218
  with gr.Blocks(css=CSS, title="Interactive Math Tutor") as demo:
219
 
220
  gr.HTML("""
221
  <div class="math-header">
222
  <h1>πŸ“ Interactive Math Tutor</h1>
223
+ <p>Step-by-step guidance Β· Grade 1 to High School Β· Powered by open-source AI</p>
224
  <div class="badge-row">
225
  <span class="badge">✏️ Arithmetic</span>
226
  <span class="badge">Β½ Fractions</span>
 
234
 
235
  with gr.Row(equal_height=False):
236
  with gr.Column(scale=4, min_width=300):
237
+
238
+ # Only show token box if owner hasn't pre-set one
239
+ if not OWNER_TOKEN_SET:
240
+ gr.HTML('<div class="panel-label">πŸ”‘ HuggingFace Token (free)</div>')
241
+ user_token = gr.Textbox(
242
+ label="",
243
+ placeholder="hf_xxxxxxxxxxxxxxxxxxxx",
244
+ type="password",
245
+ lines=1,
246
+ elem_classes=["token-box"],
247
+ )
248
+ gr.HTML("""
249
+ <div class="token-info">
250
+ <strong>Get your free token in 30 seconds:</strong><br>
251
+ 1. Go to <a href="https://huggingface.co/settings/tokens" target="_blank">huggingface.co/settings/tokens</a><br>
252
+ 2. Click <strong>New token</strong> β†’ <strong>Read</strong> access β†’ Copy<br>
253
+ 3. Paste it above Β· Never stored, session-only
254
+ </div>
255
+ """)
256
+ else:
257
+ user_token = gr.Textbox(value="", visible=False)
258
+ gr.HTML("""
259
+ <div class="info-box">
260
+ <strong>βœ… Ready to use β€” no setup needed!</strong><br>
261
+ Token pre-configured by the Space owner.
262
+ </div>
263
+ """)
264
+
265
+ gr.HTML('<div class="panel-label" style="margin-top:16px">πŸ“Ž Upload Worksheet (optional)</div>')
266
+ image_input = gr.Image(type="pil", label="", height=180)
267
 
268
  gr.HTML('<div class="panel-label" style="margin-top:14px">✏️ Or Type Your Problem</div>')
269
  text_input = gr.Textbox(
 
279
  solve_btn = gr.Button("πŸš€ Solve & Explain", elem_classes=["solve-btn"])
280
  clear_btn = gr.Button("πŸ—‘ Clear", elem_classes=["clear-btn"])
281
 
 
 
 
 
 
 
 
 
 
 
282
  gr.HTML('<div class="panel-label" style="margin-top:16px">πŸ’‘ Try an example</div>')
283
+ gr.Examples(
284
+ examples=[[None, p, g, ""] for (_, p, g) in EXAMPLES],
285
+ inputs=[image_input, text_input, grade_dropdown, user_token],
286
+ label=""
287
+ )
288
 
289
  with gr.Column(scale=6, min_width=360):
290
  gr.HTML('<div class="panel-label">πŸŽ“ Step-by-Step Solution</div>')
 
295
 
296
  solve_btn.click(
297
  fn=solve_math,
298
+ inputs=[image_input, text_input, grade_dropdown, user_token],
299
  outputs=output_display,
300
  )
301
  clear_btn.click(
302
+ fn=lambda: (None, "", "Auto-detect from problem", "",
303
  "*Your solution will appear here.*"),
304
  inputs=[],
305
+ outputs=[image_input, text_input, grade_dropdown, user_token, output_display],
306
  )
307
 
308
  if __name__ == "__main__":