MasteredUltraInstinct commited on
Commit
9f23e99
Β·
verified Β·
1 Parent(s): 6d4e944

Update image.py

Browse files
Files changed (1) hide show
  1. image.py +47 -363
image.py CHANGED
@@ -7,15 +7,16 @@ import matplotlib.pyplot as plt
7
  import re
8
  import io
9
  import logging
 
10
 
11
- # Configure logging for debugging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- # Define symbolic variables
16
  x, y = sp.symbols('x y')
17
 
18
- # Initialize Pix2Text model globally
19
  try:
20
  p2t_model = Pix2Text.from_config()
21
  logger.info("Pix2Text model loaded successfully")
@@ -23,314 +24,12 @@ except Exception as e:
23
  logger.error(f"Failed to load Pix2Text model: {e}")
24
  p2t_model = None
25
 
26
- def clean_latex_expression(latex_str):
27
- """Clean and normalize LaTeX expression for SymPy parsing"""
28
- if not latex_str:
29
- return ""
30
-
31
- latex_str = latex_str.strip()
32
- latex_str = re.sub(r'^\$\$|\$\$$', '', latex_str) # Remove $$ delimiters
33
- latex_str = re.sub(r'\\[a-zA-Z]+\{([^}]*)\}', r'\1', latex_str) # Remove LaTeX commands
34
- latex_str = re.sub(r'\\{2,}', r'\\', latex_str) # Fix multiple backslashes
35
- latex_str = re.sub(r'\s+', ' ', latex_str) # Normalize whitespace
36
- latex_str = re.sub(r'\^{([^}]+)}', r'**\1', latex_str) # Convert x^{n} to x**n
37
- latex_str = re.sub(r'(\d*\.?\d+)\s*([xy])', r'\1*\2', latex_str) # Add multiplication: 1.0x -> 1.0*x
38
- latex_str = re.sub(r'\s*([+\-*/=])\s*', r'\1', latex_str) # Remove spaces around operators
39
- if '=' in latex_str:
40
- left, right = latex_str.split('=')
41
- latex_str = f"{left} - ({right})" # Move right-hand side to left
42
- return latex_str.strip()
43
-
44
- def parse_equation_type(latex_str):
45
- """Determine if the equation is polynomial (single-variable) or linear system (two-variable)"""
46
- try:
47
- cleaned = clean_latex_expression(latex_str)
48
- if not cleaned:
49
- return 'polynomial'
50
-
51
- # Check for two-variable system
52
- if 'y' in cleaned and 'x' in cleaned:
53
- if '\\\\' in latex_str or '\n' in latex_str or len(re.split(r'\\\\|\n|;', latex_str)) >= 2:
54
- return 'linear_system'
55
- return 'linear' # Single equation with x and y
56
-
57
- # Check for single-variable polynomial
58
- try:
59
- expr = sp.sympify(cleaned.split('-')[0] if '-' in cleaned else cleaned)
60
- if x in expr.free_symbols and y not in expr.free_symbols:
61
- degree = sp.degree(expr, x)
62
- return 'polynomial' if degree > 0 else 'linear'
63
- elif x not in expr.free_symbols and y in expr.free_symbols:
64
- return 'polynomial' # Treat as polynomial in y if x is absent
65
- else:
66
- return 'polynomial' # Default to polynomial if no clear variables
67
- except:
68
- if 'x**' in cleaned or '^' in latex_str:
69
- return 'polynomial'
70
- return 'polynomial' # Fallback to polynomial
71
- except Exception as e:
72
- logger.error(f"Error determining equation type: {e}")
73
- return 'polynomial'
74
-
75
- def extract_polynomial_coefficients(latex_str):
76
- """Extract polynomial coefficients from LaTeX string"""
77
- try:
78
- cleaned = clean_latex_expression(latex_str)
79
- if '-' in cleaned:
80
- cleaned = cleaned.split('-')[0].strip() # Use left side for polynomial
81
-
82
- expr = sp.sympify(cleaned, evaluate=False)
83
- if x not in expr.free_symbols and y not in expr.free_symbols:
84
- raise ValueError("No variable (x or y) found in expression")
85
-
86
- variable = x if x in expr.free_symbols else y
87
- degree = sp.degree(expr, variable)
88
- if degree < 1 or degree > 8:
89
- raise ValueError(f"Polynomial degree {degree} is out of supported range (1-8)")
90
-
91
- poly = sp.Poly(expr, variable)
92
- coeffs = [float(poly.coeff_monomial(variable**i)) for i in range(degree, -1, -1)]
93
-
94
- return {
95
- "type": "polynomial",
96
- "degree": degree,
97
- "coeffs": " ".join(map(str, coeffs)),
98
- "latex": latex_str,
99
- "success": True,
100
- "variable": str(variable)
101
- }
102
- except Exception as e:
103
- logger.error(f"Error extracting polynomial coefficients: {e}")
104
- return {
105
- "type": "polynomial",
106
- "degree": 2,
107
- "coeffs": "1 0 0",
108
- "latex": latex_str,
109
- "success": False,
110
- "error": str(e),
111
- "variable": "x"
112
- }
113
-
114
- def extract_linear_system_coefficients(latex_str):
115
- """Extract linear system coefficients from LaTeX string"""
116
- try:
117
- cleaned = clean_latex_expression(latex_str)
118
- equations = re.split(r'\\\\|\n|;', latex_str)
119
- if len(equations) < 2:
120
- equations = re.split(r'(?<=[0-9])\s*(?=[+-]?\s*[0-9]*[xy])', cleaned)
121
-
122
- if len(equations) < 2 or 'y' not in cleaned or 'x' not in cleaned:
123
- raise ValueError("Could not find two equations or two variables (x, y) in system")
124
-
125
- eq1_str = equations[0].strip()
126
- eq2_str = equations[1].strip()
127
-
128
- def parse_linear_eq(eq_str):
129
- if '-' not in eq_str:
130
- raise ValueError("No equals sign (converted to '-') found")
131
- left, right = eq_str.split('-')
132
- expr = sp.sympify(left) - sp.sympify(right or '0')
133
- a = float(expr.coeff(x, 1)) if expr.coeff(x, 1) else 0
134
- b = float(expr.coeff(y, 1)) if expr.coeff(y, 1) else 0
135
- c = float(-expr.as_coefficients_dict()[1]) if 1 in expr.as_coefficients_dict() else 0
136
- return f"{a} {b} {c}"
137
-
138
- eq1_coeffs = parse_linear_eq(eq1_str)
139
- eq2_coeffs = parse_linear_eq(eq2_str)
140
-
141
- return {
142
- "type": "linear",
143
- "eq1_coeffs": eq1_coeffs,
144
- "eq2_coeffs": eq2_coeffs,
145
- "latex": latex_str,
146
- "success": True
147
- }
148
- except Exception as e:
149
- logger.error(f"Error extracting linear system coefficients: {e}")
150
- return {
151
- "type": "linear",
152
- "eq1_coeffs": "1 1 3",
153
- "eq2_coeffs": "1 -1 1",
154
- "latex": latex_str,
155
- "success": False,
156
- "error": str(e)
157
- }
158
-
159
- def extract_equation_from_image(image_file):
160
- """Extract equation from image using Pix2Text"""
161
- try:
162
- if p2t_model is None:
163
- return {
164
- "type": "error",
165
- "latex": "Pix2Text model not loaded. Please check installation.",
166
- "success": False
167
- }
168
-
169
- if image_file is None:
170
- return {
171
- "type": "error",
172
- "latex": "No image file provided.",
173
- "success": False
174
- }
175
-
176
- if isinstance(image_file, str):
177
- image = Image.open(image_file)
178
- else:
179
- image = Image.open(image_file.name)
180
-
181
- if image.mode != 'RGB':
182
- image = image.convert('RGB')
183
-
184
- logger.info(f"Processing image of size: {image.size}")
185
-
186
- result = p2t_model.recognize_text_formula(image)
187
- if not result or result.strip() == "":
188
- return {
189
- "type": "error",
190
- "latex": "No text or formulas detected in the image.",
191
- "success": False
192
- }
193
-
194
- logger.info(f"Extracted text: {result}")
195
-
196
- eq_type = parse_equation_type(result)
197
- if eq_type == 'polynomial':
198
- return extract_polynomial_coefficients(result)
199
- elif eq_type == 'linear_system':
200
- return extract_linear_system_coefficients(result)
201
- else:
202
- return {
203
- "type": "error",
204
- "latex": f"Unsupported equation type detected: {eq_type}",
205
- "success": False
206
- }
207
- except Exception as e:
208
- logger.error(f"Error processing image: {e}")
209
- return {
210
- "type": "error",
211
- "latex": f"Error processing image: {str(e)}",
212
- "success": False
213
- }
214
-
215
- def solve_polynomial(degree, coeff_string, real_only):
216
- """Solve polynomial equation"""
217
- try:
218
- coeffs = list(map(float, coeff_string.strip().split()))
219
- if len(coeffs) != degree + 1:
220
- return f"⚠️ Please enter exactly {degree + 1} coefficients.", None, None
221
-
222
- poly = sum([coeffs[i] * x**(degree - i) for i in range(degree + 1)])
223
- simplified = sp.simplify(poly)
224
- factored = sp.factor(simplified)
225
- roots = sp.solve(sp.Eq(simplified, 0), x)
226
-
227
- if real_only:
228
- roots = [r for r in roots if sp.im(r) == 0]
229
-
230
- roots_output = "$$\n" + "\\ ".join(
231
- [f"r_{{{i}}} = {sp.latex(sp.nsimplify(r, rational=True))}" for i, r in enumerate(roots, 1)]
232
- ) + "\n$$"
233
-
234
- steps_output = f"""
235
- ### Polynomial Expression
236
- $$ {sp.latex(poly)} = 0 $$
237
- ### Simplified
238
- $$ {sp.latex(simplified)} = 0 $$
239
- ### Factored
240
- $$ {sp.latex(factored)} = 0 $$
241
- ### Roots {'(Only Real)' if real_only else '(All Roots)'}
242
- {roots_output}
243
- """
244
-
245
- x_vals = np.linspace(-10, 10, 400)
246
- y_vals = np.polyval(coeffs, x_vals)
247
-
248
- fig, ax = plt.subplots(figsize=(6, 4))
249
- ax.plot(x_vals, y_vals, label="Polynomial", color="blue")
250
- ax.axhline(0, color='black', linewidth=0.5)
251
- ax.axvline(0, color='black', linewidth=0.5)
252
- ax.grid(True)
253
- ax.set_title("Graph of the Polynomial")
254
- ax.set_xlabel("x")
255
- ax.set_ylabel("f(x)")
256
- ax.legend()
257
-
258
- return steps_output, fig, ""
259
- except Exception as e:
260
- return f"❌ Error: {e}", None, ""
261
-
262
- def solve_linear_system_from_coeffs(eq1_str, eq2_str):
263
- """Solve linear system"""
264
- try:
265
- coeffs1 = list(map(float, eq1_str.strip().split()))
266
- coeffs2 = list(map(float, eq2_str.strip().split()))
267
-
268
- if len(coeffs1) != 3 or len(coeffs2) != 3:
269
- return "⚠️ Please enter exactly 3 coefficients for each equation.", None, None, None
270
-
271
- a1, b1, c1 = coeffs1
272
- a2, b2, c2 = coeffs2
273
-
274
- eq1 = sp.Eq(a1 * x + b1 * y, c1)
275
- eq2 = sp.Eq(a2 * x + b2 * y, c2)
276
-
277
- sol = sp.solve([eq1, eq2], (x, y), dict=True)
278
- if not sol:
279
- return "❌ No unique solution.", None, None, None
280
-
281
- solution = sol[0]
282
- eq_latex = f"$$ {sp.latex(eq1)} \\ {sp.latex(eq2)} $$"
283
-
284
- steps = rf"""
285
- ### Step-by-step Solution
286
- 1. **Original Equations:**
287
- $$ {sp.latex(eq1)} $$
288
- $$ {sp.latex(eq2)} $$
289
- 2. **Standard Form:** Already provided.
290
- 3. **Solve using SymPy `solve`:** Internally applies substitution/elimination.
291
- 4. **Solve for `x` and `y`:**
292
- $$ x = {sp.latex(solution[x])}, \quad y = {sp.latex(solution[y])} $$
293
- 5. **Verification:** Substitute back into both equations."""
294
-
295
- x_vals = np.linspace(-10, 10, 400)
296
- f1 = sp.solve(eq1, y)
297
- f2 = sp.solve(eq2, y)
298
-
299
- fig, ax = plt.subplots()
300
- if f1:
301
- f1_func = sp.lambdify(x, f1[0], modules='numpy')
302
- ax.plot(x_vals, f1_func(x_vals), label=sp.latex(eq1))
303
- if f2:
304
- f2_func = sp.lambdify(x, f2[0], modules='numpy')
305
- ax.plot(x_vals, f2_func(x_vals), label=sp.latex(eq2))
306
-
307
- ax.plot(solution[x], solution[y], 'ro', label=f"Solution ({solution[x]}, {solution[y]})")
308
- ax.axhline(0, color='black', linewidth=0.5)
309
- ax.axvline(0, color='black', linewidth=0.5)
310
- ax.legend()
311
- ax.set_title("Graph of the Linear System")
312
- ax.grid(True)
313
-
314
- return eq_latex, steps, fig, ""
315
- except Exception as e:
316
- return f"❌ Error: {e}", None, None, None
317
-
318
- def solve_extracted_equation(eq_data, real_only):
319
- """Route to appropriate solver based on equation type"""
320
- if eq_data["type"] == "polynomial":
321
- return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
322
- elif eq_data["type"] == "linear":
323
- return "❌ Single linear equation not supported. Please upload a system of equations.", None, ""
324
- elif eq_data["type"] == "linear_system":
325
- return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
326
- else:
327
- return "❌ Unknown equation type", None, ""
328
-
329
  def image_tab():
330
  """Create the Image Upload Solver tab"""
331
  with gr.Tab("Image Upload Solver"):
332
  gr.Markdown("## Solve Equations from Image")
333
-
334
  with gr.Row():
335
  image_input = gr.File(
336
  label="Upload Question Image",
@@ -338,7 +37,7 @@ def image_tab():
338
  file_count="single"
339
  )
340
  image_upload_btn = gr.Button("Process Image")
341
-
342
  gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
343
 
344
  with gr.Row():
@@ -346,7 +45,7 @@ def image_tab():
346
  preview_image_btn = gr.Button("Preview Equation")
347
 
348
  image_equation_display = gr.Markdown()
349
-
350
  with gr.Row():
351
  confirm_image_btn = gr.Button("Display Solution", visible=False)
352
  edit_image_btn = gr.Button("Make Changes Manually", visible=False)
@@ -358,87 +57,67 @@ def image_tab():
358
  image_plot_output = gr.Plot()
359
  extracted_eq_state = gr.State()
360
 
 
 
 
 
 
361
  def handle_image_upload(image_file):
362
- """Handle image upload and initial processing"""
363
  if image_file is None:
364
  return "", None, "", None, None
365
-
366
  try:
367
  eq_data = extract_equation_from_image(image_file)
368
- if eq_data["success"]:
369
- return "", eq_data, "", None, None
370
- else:
371
- return "", eq_data, "", None, None
372
- except Exception as e:
373
  return "", None, "", None, None
374
 
375
  image_upload_btn.click(
376
  fn=handle_image_upload,
377
  inputs=[image_input],
378
  outputs=[image_equation_display, extracted_eq_state, image_steps_md,
379
- image_plot_output, edit_latex_input]
380
  )
381
 
382
  def preview_image_equation(eq_data, real_only):
383
- """Preview the extracted equation"""
384
- if eq_data is None:
385
- return ("⚠️ No equation data available. Please upload and process an image first.",
386
- gr.update(visible=False), gr.update(visible=False), "", None)
387
-
388
  if eq_data["type"] == "error":
389
- return (eq_data["latex"], gr.update(visible=False), gr.update(visible=False), "", None)
390
-
391
- if eq_data["type"] == "polynomial":
392
- eq_type_display = "Polynomial Equation"
393
- elif eq_data["type"] == "linear_system":
394
- eq_type_display = "Linear System"
395
- else:
396
- eq_type_display = "Unknown Equation Type"
397
 
398
  preview_text = f"""
399
- ### βœ… Confirm {eq_type_display}
400
  **Extracted LaTeX:** {eq_data['latex']}
401
  """
402
-
403
- return (preview_text, gr.update(visible=True), gr.update(visible=True), "", None)
404
 
405
  preview_image_btn.click(
406
  fn=preview_image_equation,
407
  inputs=[extracted_eq_state, real_image_checkbox],
408
  outputs=[image_equation_display, confirm_image_btn, edit_image_btn,
409
- image_steps_md, image_plot_output]
410
  )
411
 
412
  def confirm_image_solution(eq_data, real_only):
413
- """Confirm and solve the extracted equation"""
414
- if eq_data is None or eq_data["type"] == "error":
415
  return "⚠️ No valid equation to solve.", None, ""
416
-
417
  try:
418
  steps, plot, error = solve_extracted_equation(eq_data, real_only)
419
- return steps, plot, ""
420
  except Exception as e:
421
  return f"❌ Error solving equation: {str(e)}", None, ""
422
 
423
  confirm_image_btn.click(
424
  fn=confirm_image_solution,
425
  inputs=[extracted_eq_state, real_image_checkbox],
426
- outputs=[image_steps_md, image_plot_output, image_equation_display]
427
  )
428
 
429
  def enable_manual_edit(eq_data):
430
- """Enable manual editing of the equation"""
431
- if eq_data is None:
432
- latex_value = "No equation to edit. Please upload an image first."
433
- elif eq_data["type"] == "error":
434
- latex_value = "Error in extraction. Please enter your equation manually."
435
- else:
436
- latex_value = eq_data.get("latex", "")
437
-
438
  return (gr.update(visible=True, value=latex_value),
439
- gr.update(visible=True),
440
- gr.update(visible=False),
441
- gr.update(visible=False))
442
 
443
  edit_image_btn.click(
444
  fn=enable_manual_edit,
@@ -447,32 +126,37 @@ def image_tab():
447
  )
448
 
449
  def save_manual_changes(latex_input, real_only):
450
- """Save manual changes and solve"""
451
  try:
452
- if not latex_input or latex_input.strip() == "":
453
  return "⚠️ Please enter a valid equation.", None, ""
454
-
455
  eq_type = parse_equation_type(latex_input)
456
  if eq_type == 'polynomial':
457
  eq_data = extract_polynomial_coefficients(latex_input)
458
- steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
459
  elif eq_type == 'linear_system':
460
  eq_data = extract_linear_system_coefficients(latex_input)
461
- eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
462
- eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
463
  else:
464
  return "❌ Unsupported equation type", None, ""
465
-
466
- return steps, plot, ""
467
  except Exception as e:
468
- return f"❌ Error parsing manual input: {str(e)}", None, ""
469
 
470
  save_edit_btn.click(
471
  fn=save_manual_changes,
472
  inputs=[edit_latex_input, real_image_checkbox],
473
- outputs=[image_steps_md, image_plot_output, image_equation_display]
 
 
 
 
 
 
 
474
  )
475
 
476
- return (image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
477
- image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
478
- save_edit_btn, image_steps_md, image_plot_output, extracted_eq_state)
 
 
 
 
7
  import re
8
  import io
9
  import logging
10
+ from llm_utils import explain_with_llm # βœ… Added for LLM explanation
11
 
12
+ # Logging setup
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
+ # Define symbols
17
  x, y = sp.symbols('x y')
18
 
19
+ # βœ… Pix2Text model initialized here instead of being passed in
20
  try:
21
  p2t_model = Pix2Text.from_config()
22
  logger.info("Pix2Text model loaded successfully")
 
24
  logger.error(f"Failed to load Pix2Text model: {e}")
25
  p2t_model = None
26
 
27
+ # βœ… Only function header is changed to remove argument
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  def image_tab():
29
  """Create the Image Upload Solver tab"""
30
  with gr.Tab("Image Upload Solver"):
31
  gr.Markdown("## Solve Equations from Image")
32
+
33
  with gr.Row():
34
  image_input = gr.File(
35
  label="Upload Question Image",
 
37
  file_count="single"
38
  )
39
  image_upload_btn = gr.Button("Process Image")
40
+
41
  gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
42
 
43
  with gr.Row():
 
45
  preview_image_btn = gr.Button("Preview Equation")
46
 
47
  image_equation_display = gr.Markdown()
48
+
49
  with gr.Row():
50
  confirm_image_btn = gr.Button("Display Solution", visible=False)
51
  edit_image_btn = gr.Button("Make Changes Manually", visible=False)
 
57
  image_plot_output = gr.Plot()
58
  extracted_eq_state = gr.State()
59
 
60
+ # βœ… Added LLM input/output components
61
+ llm_url_input = gr.Textbox(label="LLM Microservice URL (optional)", placeholder="https://your-llm-url.ngrok.app")
62
+ explain_image_btn = gr.Button("Explain with LLM")
63
+ image_solution_txt = gr.Textbox(visible=False)
64
+
65
  def handle_image_upload(image_file):
 
66
  if image_file is None:
67
  return "", None, "", None, None
 
68
  try:
69
  eq_data = extract_equation_from_image(image_file)
70
+ return "", eq_data, "", None, None
71
+ except Exception:
 
 
 
72
  return "", None, "", None, None
73
 
74
  image_upload_btn.click(
75
  fn=handle_image_upload,
76
  inputs=[image_input],
77
  outputs=[image_equation_display, extracted_eq_state, image_steps_md,
78
+ image_plot_output, edit_latex_input]
79
  )
80
 
81
  def preview_image_equation(eq_data, real_only):
82
+ if not eq_data:
83
+ return "⚠️ No equation data available.", gr.update(visible=False), gr.update(visible=False), "", None
 
 
 
84
  if eq_data["type"] == "error":
85
+ return eq_data["latex"], gr.update(visible=False), gr.update(visible=False), "", None
 
 
 
 
 
 
 
86
 
87
  preview_text = f"""
88
+ ### βœ… Confirm {'Polynomial' if eq_data['type'] == 'polynomial' else 'Linear System'}
89
  **Extracted LaTeX:** {eq_data['latex']}
90
  """
91
+ return preview_text, gr.update(visible=True), gr.update(visible=True), "", None
 
92
 
93
  preview_image_btn.click(
94
  fn=preview_image_equation,
95
  inputs=[extracted_eq_state, real_image_checkbox],
96
  outputs=[image_equation_display, confirm_image_btn, edit_image_btn,
97
+ image_steps_md, image_plot_output]
98
  )
99
 
100
  def confirm_image_solution(eq_data, real_only):
101
+ if not eq_data or eq_data["type"] == "error":
 
102
  return "⚠️ No valid equation to solve.", None, ""
 
103
  try:
104
  steps, plot, error = solve_extracted_equation(eq_data, real_only)
105
+ return steps, plot, steps # βœ… store solution for LLM
106
  except Exception as e:
107
  return f"❌ Error solving equation: {str(e)}", None, ""
108
 
109
  confirm_image_btn.click(
110
  fn=confirm_image_solution,
111
  inputs=[extracted_eq_state, real_image_checkbox],
112
+ outputs=[image_steps_md, image_plot_output, image_solution_txt] # βœ… added solution_txt
113
  )
114
 
115
  def enable_manual_edit(eq_data):
116
+ latex_value = eq_data.get("latex", "") if eq_data and eq_data["type"] != "error" else "Error in extraction."
 
 
 
 
 
 
 
117
  return (gr.update(visible=True, value=latex_value),
118
+ gr.update(visible=True),
119
+ gr.update(visible=False),
120
+ gr.update(visible=False))
121
 
122
  edit_image_btn.click(
123
  fn=enable_manual_edit,
 
126
  )
127
 
128
  def save_manual_changes(latex_input, real_only):
 
129
  try:
130
+ if not latex_input.strip():
131
  return "⚠️ Please enter a valid equation.", None, ""
 
132
  eq_type = parse_equation_type(latex_input)
133
  if eq_type == 'polynomial':
134
  eq_data = extract_polynomial_coefficients(latex_input)
135
+ return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)[:2] + (solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)[0],)
136
  elif eq_type == 'linear_system':
137
  eq_data = extract_linear_system_coefficients(latex_input)
138
+ return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])[:2] + (solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])[1],)
 
139
  else:
140
  return "❌ Unsupported equation type", None, ""
 
 
141
  except Exception as e:
142
+ return f"❌ Error: {str(e)}", None, ""
143
 
144
  save_edit_btn.click(
145
  fn=save_manual_changes,
146
  inputs=[edit_latex_input, real_image_checkbox],
147
+ outputs=[image_steps_md, image_plot_output, image_solution_txt] # βœ… added solution_txt
148
+ )
149
+
150
+ # βœ… LLM Explain button click logic
151
+ explain_image_btn.click(
152
+ fn=lambda sol, url: explain_with_llm(sol, "image", url),
153
+ inputs=[image_solution_txt, llm_url_input],
154
+ outputs=[image_steps_md]
155
  )
156
 
157
+ return (
158
+ image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
159
+ image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
160
+ save_edit_btn, image_steps_md, image_plot_output, extracted_eq_state,
161
+ llm_url_input, explain_image_btn, image_solution_txt # βœ… added LLM components to return
162
+ )