shukdevdattaEX commited on
Commit
431b5dc
Β·
verified Β·
1 Parent(s): c682e48

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +391 -606
app.py CHANGED
@@ -10,362 +10,234 @@ import io
10
  import markdown
11
  from datetime import datetime
12
  import tempfile
 
13
  from pathlib import Path
14
- import subprocess
15
- import sys
16
 
17
- # Function to install playwright if not available
18
- def ensure_playwright():
19
- try:
20
- from playwright.sync_api import sync_playwright
21
- return True
22
- except ImportError:
23
- print("Installing playwright for PDF generation...")
24
- subprocess.check_call([sys.executable, "-m", "pip", "install", "playwright"])
25
- subprocess.check_call([sys.executable, "-m", "playwright", "install", "chromium"])
26
- subprocess.check_call([sys.executable, "-m", "playwright", "install", "install-deps"])
27
- from playwright.sync_api import sync_playwright
28
- return True
29
-
30
- # Function to convert markdown to HTML with styling and MathJax
31
  def markdown_to_html(markdown_text, problem_text="", include_problem=True):
32
- """Convert markdown to styled HTML with proper math rendering"""
33
-
34
  # Convert markdown to HTML
35
  html_content = markdown.markdown(markdown_text, extensions=['tables', 'fenced_code'])
36
-
37
  # Get current timestamp
38
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
39
-
40
- # Create styled HTML document with MathJax
41
- styled_html = f"""
42
- <!DOCTYPE html>
43
- <html lang="en">
44
- <head>
45
- <meta charset="UTF-8">
46
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
47
- <title>Math Solution - Advanced Math Tutor</title>
48
-
49
- <!-- MathJax for rendering mathematical notation -->
50
- <script>
51
- window.MathJax = {{
52
- tex: {{
53
- inlineMath: [['\\\\(', '\\\\)'], ['$', '$']],
54
- displayMath: [['\\\\[', '\\\\]'], ['$$', '$$']],
55
- processEscapes: true,
56
- processEnvironments: true,
57
- packages: {{'[+]': ['ams', 'newcommand', 'configmacros']}}
58
- }},
59
- options: {{
60
- skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
61
- ignoreHtmlClass: 'tex2jax_ignore',
62
- processHtmlClass: 'tex2jax_process'
63
- }},
64
- startup: {{
65
- ready: () => {{
66
- MathJax.startup.defaultReady();
67
- MathJax.startup.promise.then(() => {{
68
- console.log('MathJax rendering complete');
69
- document.body.classList.add('mathjax-ready');
70
- }});
71
- }}
72
- }}
73
- }};
74
- </script>
75
- <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" id="MathJax-script" async></script>
76
-
77
- <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  body {{
79
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
80
- line-height: 1.8;
81
- color: #1a1a1a;
82
- max-width: 900px;
83
- margin: 0 auto;
84
- padding: 40px 20px;
85
- background-color: #f5f7fa;
86
- }}
87
-
88
- body:not(.mathjax-ready) {{
89
- opacity: 0;
90
- }}
91
-
92
- body.mathjax-ready {{
93
- opacity: 1;
94
- transition: opacity 0.3s ease-in;
95
- }}
96
-
97
- .container {{
98
  background-color: white;
99
- padding: 50px;
100
- border-radius: 12px;
101
- box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
102
- }}
103
-
104
- .header {{
105
- text-align: center;
106
- border-bottom: 4px solid #4CAF50;
107
- padding-bottom: 25px;
108
- margin-bottom: 40px;
109
- }}
110
-
111
- .header h1 {{
112
- color: #2c3e50;
113
- margin: 0;
114
- font-size: 2.8em;
115
- font-weight: 700;
116
- }}
117
-
118
- .header .subtitle {{
119
- color: #7f8c8d;
120
- font-style: italic;
121
- margin-top: 10px;
122
- font-size: 1.1em;
123
- }}
124
-
125
- .problem-section {{
126
- background-color: #e8f5e9;
127
- padding: 25px;
128
- border-radius: 10px;
129
- margin-bottom: 35px;
130
- border-left: 6px solid #4CAF50;
131
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
132
- }}
133
-
134
- .problem-section h2 {{
135
- color: #2c3e50;
136
- margin-top: 0;
137
- font-size: 1.6em;
138
- }}
139
-
140
- .problem-section p {{
141
- font-size: 1.15em;
142
- line-height: 1.6;
143
- margin: 10px 0;
144
- }}
145
-
146
- .solution-content {{
147
- background-color: #fafbfc;
148
- padding: 35px;
149
- border-radius: 10px;
150
- border-left: 6px solid #007bff;
151
- box-shadow: 0 2px 8px rgba(0,0,0,0.05);
152
- }}
153
-
154
- h1, h2, h3, h4, h5, h6 {{
155
- color: #2c3e50;
156
- margin-top: 30px;
157
- margin-bottom: 15px;
158
- font-weight: 600;
159
- line-height: 1.3;
160
- }}
161
-
162
- h2 {{
163
- border-bottom: 2px solid #e1e4e8;
164
- padding-bottom: 12px;
165
- font-size: 1.8em;
166
- }}
167
-
168
- h3 {{
169
- font-size: 1.5em;
170
- color: #34495e;
171
- margin-top: 25px;
172
- }}
173
-
174
- h4 {{
175
- font-size: 1.3em;
176
- color: #34495e;
177
- }}
178
-
179
- p {{
180
- margin: 15px 0;
181
- line-height: 1.8;
182
- }}
183
-
184
- code {{
185
- background-color: #f1f3f5;
186
- padding: 3px 8px;
187
- border-radius: 4px;
188
- font-family: 'Courier New', Consolas, monospace;
189
- color: #e83e8c;
190
- font-size: 0.9em;
191
- }}
192
-
193
- pre {{
194
- background-color: #f6f8fa;
195
- padding: 20px;
196
- border-radius: 6px;
197
- overflow-x: auto;
198
- border: 1px solid #d1d5da;
199
- line-height: 1.6;
200
- }}
201
-
202
- pre code {{
203
- background-color: transparent;
204
- padding: 0;
205
- color: #24292e;
206
- }}
207
-
208
- .step {{
209
- margin: 30px 0;
210
- padding: 25px;
211
- background-color: #ffffff;
212
- border-radius: 8px;
213
- border: 1px solid #dee2e6;
214
- box-shadow: 0 1px 4px rgba(0,0,0,0.05);
215
  }}
216
-
217
- .step h3, .step h4 {{
218
- color: #2c3e50;
219
- margin-top: 0;
220
- }}
221
-
222
- ul, ol {{
223
- padding-left: 30px;
224
- line-height: 1.8;
225
- }}
226
-
227
- li {{
228
- margin: 12px 0;
229
- }}
230
-
231
- table {{
232
- border-collapse: collapse;
233
- width: 100%;
234
- margin: 25px 0;
235
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
236
- }}
237
-
238
- th, td {{
239
- border: 1px solid #dee2e6;
240
- padding: 14px;
241
- text-align: left;
242
- }}
243
-
244
- th {{
245
- background-color: #f8f9fa;
246
- font-weight: 600;
247
- color: #495057;
248
- }}
249
-
250
- tr:nth-child(even) {{
251
- background-color: #f8f9fa;
252
- }}
253
-
254
- /* MathJax styling */
255
- mjx-container {{
256
- margin: 15px 0 !important;
257
- overflow-x: auto;
258
- overflow-y: hidden;
259
- }}
260
-
261
- mjx-container[display="true"] {{
262
- margin: 25px 0 !important;
263
- text-align: center;
264
- }}
265
-
266
- /* Ensure math displays properly */
267
- .MathJax, .MathJax_Display {{
268
- font-size: 1.1em !important;
269
- }}
270
-
271
- /* Final answer styling */
272
- .final-answer {{
273
- background-color: #d4edda;
274
- border: 3px solid #28a745;
275
- padding: 30px;
276
- border-radius: 10px;
277
- margin-top: 40px;
278
- text-align: center;
279
- }}
280
-
281
- .final-answer h2, .final-answer h3 {{
282
- color: #155724;
283
- margin-top: 0;
284
- margin-bottom: 20px;
285
- }}
286
-
287
- .final-answer mjx-container {{
288
- font-size: 1.3em !important;
289
- }}
290
-
291
- .timestamp {{
292
- text-align: right;
293
- color: #6c757d;
294
- font-size: 0.95em;
295
- margin-top: 40px;
296
- padding-top: 25px;
297
- border-top: 2px solid #e9ecef;
298
- }}
299
-
300
- .print-button {{
301
- background-color: #007bff;
302
- color: white;
303
  border: none;
304
- padding: 12px 24px;
305
- border-radius: 6px;
306
- cursor: pointer;
307
- font-size: 16px;
308
- margin: 10px 5px;
309
- display: inline-block;
310
- text-decoration: none;
311
- font-weight: 500;
312
- transition: background-color 0.2s;
313
- }}
314
-
315
- .print-button:hover {{
316
- background-color: #0056b3;
317
  }}
318
-
319
- @media print {{
320
- body {{
321
- background-color: white;
322
- padding: 20px;
323
- }}
324
- .container {{
325
- box-shadow: none;
326
- border: none;
327
- padding: 20px;
 
 
 
 
 
 
 
 
 
 
328
  }}
329
- .print-button {{
330
- display: none;
 
 
 
 
 
 
 
 
 
331
  }}
332
  }}
333
- </style>
334
- <script>
335
- function printPage() {{
336
- window.print();
337
- }}
338
- </script>
339
- </head>
340
- <body>
341
- <div class="container">
342
- <div class="header">
343
- <h1>πŸ“š Advanced Math Tutor</h1>
344
- <div class="subtitle">Step-by-Step Mathematical Solution</div>
345
- </div>
346
-
347
- <button class="print-button" onclick="printPage()">πŸ–¨οΈ Print to PDF</button>
348
-
349
- {f'''
350
- <div class="problem-section">
351
- <h2>πŸ“ Problem Statement</h2>
352
- <p><strong>{problem_text}</strong></p>
353
- </div>
354
- ''' if include_problem and problem_text.strip() else ''}
355
-
356
- <div class="solution-content">
357
- <h2>πŸ” Solution</h2>
358
- {html_content}
359
- </div>
360
-
361
- <div class="timestamp">
362
- Generated on: {timestamp}
363
- </div>
364
  </div>
365
- </body>
366
- </html>
367
- """
368
-
369
  return styled_html
370
 
371
  # Function to save HTML to file
@@ -373,162 +245,86 @@ def save_html_to_file(html_content, filename_prefix="math_solution"):
373
  """Save HTML content to a temporary file and return the file path"""
374
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
375
  filename = f"{filename_prefix}_{timestamp}.html"
376
-
377
  # Create a temporary file
378
  temp_dir = tempfile.gettempdir()
379
  file_path = os.path.join(temp_dir, filename)
380
-
381
  with open(file_path, 'w', encoding='utf-8') as f:
382
  f.write(html_content)
383
-
384
  return file_path
385
 
386
- # Function to convert HTML to PDF using Playwright (proper math rendering)
387
  def html_to_pdf(html_content, filename_prefix="math_solution"):
388
- """Convert HTML content to PDF with proper MathJax rendering using Playwright"""
389
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
390
  filename = f"{filename_prefix}_{timestamp}.pdf"
391
-
392
  temp_dir = tempfile.gettempdir()
393
  pdf_path = os.path.join(temp_dir, filename)
394
-
395
  try:
396
- # Ensure playwright is installed
397
- ensure_playwright()
398
- from playwright.sync_api import sync_playwright
399
-
400
- with sync_playwright() as p:
401
- # Launch browser
402
- browser = p.chromium.launch(headless=True)
403
- page = browser.new_page()
404
-
405
- # Set content and wait for MathJax to render
406
- page.set_content(html_content)
407
-
408
- # Wait for MathJax to be ready
409
- page.wait_for_selector('body.mathjax-ready', timeout=10000)
410
-
411
- # Additional wait to ensure everything is rendered
412
- page.wait_for_timeout(1000)
413
-
414
- # Generate PDF
415
- page.pdf(
416
- path=pdf_path,
417
- format='A4',
418
- margin={
419
- 'top': '20mm',
420
- 'right': '20mm',
421
- 'bottom': '20mm',
422
- 'left': '20mm'
423
- },
424
- print_background=True
425
- )
426
-
427
- browser.close()
428
-
429
  return pdf_path
430
  except Exception as e:
431
- print(f"Error converting to PDF with Playwright: {str(e)}")
432
- print("Falling back to basic PDF generation...")
433
-
434
- # Fallback to weasyprint if playwright fails
435
- try:
436
- import weasyprint
437
- weasyprint.HTML(string=html_content).write_pdf(pdf_path)
438
- return pdf_path
439
- except Exception as e2:
440
- print(f"Error with fallback PDF generation: {str(e2)}")
441
- return None
442
 
443
  # Enhanced function to generate math solution using OpenRouter with HTML output
444
  def generate_math_solution_openrouter(api_key, problem_text, history=None):
445
  if not api_key.strip():
446
  return "Please enter your OpenRouter API key.", None, None, history
447
-
448
  if not problem_text.strip():
449
  return "Please enter a math problem.", None, None, history
450
-
451
  try:
452
  client = OpenAI(
453
  base_url="https://openrouter.ai/api/v1",
454
  api_key=api_key,
455
  )
456
-
457
  messages = [
458
- {"role": "system", "content":
459
- """You are an expert math tutor who explains concepts clearly and thoroughly.
460
- Analyze the given math problem and provide a detailed step-by-step solution.
461
-
462
- CRITICAL: Use PROPER LaTeX notation for ALL mathematical expressions:
463
- - Use \\[ ... \\] for display (centered) equations
464
- - Use \\( ... \\) for inline math within text
465
- - Use \\boxed{...} for final answers
466
- - Use \\frac{numerator}{denominator} for fractions
467
- - Use ^ for superscripts: x^2
468
- - Use _ for subscripts: x_1
469
- - Use \\cdot for multiplication
470
- - Use \\pm for plus-minus
471
- - Use \\sqrt{...} for square roots
472
- - Greek letters: \\pi, \\alpha, \\beta, \\theta, etc.
473
-
474
- EXAMPLES:
475
- - Display equation: \\[ ax^2 + bx + c = 0 \\]
476
- - Inline math: The quadratic formula \\( x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} \\) gives us...
477
- - Final answer: \\[ \\boxed{x = \\frac{1}{3}} \\]
478
-
479
- For each step:
480
- 1. Show the mathematical operation in LaTeX
481
- 2. Explain why this step is necessary
482
- 3. Connect it to relevant mathematical concepts
483
-
484
- Format your response using markdown with clear section headers:
485
- - ## Initial Analysis
486
- - ## Step-by-Step Solution
487
- - ### Step 1: [description]
488
- - ### Step 2: [description]
489
- - ## Final Answer
490
-
491
- Use **bold** for important points and proper lists."""},
492
  ]
493
-
494
  # Add conversation history if it exists
495
  if history:
496
  for exchange in history:
497
  messages.append({"role": "user", "content": exchange[0]})
498
  if len(exchange) > 1 and exchange[1]:
 
499
  messages.append({"role": "assistant", "content": exchange[1]})
500
-
501
  # Add the current problem
502
  messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"})
503
-
504
  # Create the completion
505
  completion = client.chat.completions.create(
506
- model="meta-llama/llama-3.3-70b-instruct:free",
507
  messages=messages,
508
  extra_headers={
509
  "HTTP-Referer": "https://advancedmathtutor.edu",
510
  "X-Title": "Advanced Math Tutor",
511
  }
512
  )
513
-
514
  markdown_solution = completion.choices[0].message.content
515
-
516
  # Convert to HTML
517
  html_solution = markdown_to_html(markdown_solution, problem_text)
518
-
519
  # Save HTML file
520
  html_file_path = save_html_to_file(html_solution, "openrouter_solution")
521
-
522
- # Convert to PDF with proper math rendering
523
  pdf_file_path = html_to_pdf(html_solution, "openrouter_solution")
524
-
525
  # Update history
526
  if history is None:
527
  history = []
528
  history.append((problem_text, markdown_solution))
529
-
530
  return html_solution, html_file_path, pdf_file_path, history
531
-
532
  except Exception as e:
533
  error_message = f"Error: {str(e)}"
534
  return error_message, None, None, history
@@ -537,119 +333,90 @@ def generate_math_solution_openrouter(api_key, problem_text, history=None):
537
  def generate_math_solution_together(api_key, problem_text, image_path=None, history=None):
538
  if not api_key.strip():
539
  return "Please enter your Together AI API key.", None, None, history
540
-
541
  if not problem_text.strip() and image_path is None:
542
  return "Please enter a math problem or upload an image of a math problem.", None, None, history
543
-
544
  try:
545
  client = Together(api_key=api_key)
546
-
547
  # Create the base message structure
548
  messages = [
549
  {
550
  "role": "system",
551
- "content": """You are an expert math tutor who explains concepts clearly and thoroughly.
552
- Analyze the given math problem and provide a detailed step-by-step solution.
553
-
554
- CRITICAL: Use PROPER LaTeX notation for ALL mathematical expressions:
555
- - Use \\[ ... \\] for display (centered) equations
556
- - Use \\( ... \\) for inline math within text
557
- - Use \\boxed{...} for final answers
558
- - Use \\frac{numerator}{denominator} for fractions
559
- - Use ^ for superscripts: x^2
560
- - Use _ for subscripts: x_1
561
- - Use \\cdot for multiplication
562
- - Use \\pm for plus-minus
563
- - Use \\sqrt{...} for square roots
564
- - Greek letters: \\pi, \\alpha, \\beta, \\theta, etc.
565
-
566
- EXAMPLES:
567
- - Display equation: \\[ ax^2 + bx + c = 0 \\]
568
- - Inline math: The quadratic formula \\( x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a} \\) gives us...
569
- - Final answer: \\[ \\boxed{x = \\frac{1}{3}} \\]
570
-
571
- For each step:
572
- 1. Show the mathematical operation in LaTeX
573
- 2. Explain why this step is necessary
574
- 3. Connect it to relevant mathematical concepts
575
-
576
- Format your response using markdown with clear section headers:
577
- - ## Initial Analysis
578
- - ## Step-by-Step Solution
579
- - ### Step 1: [description]
580
- - ### Step 2: [description]
581
- - ## Final Answer
582
-
583
- Use **bold** for important points and proper lists."""
584
  }
585
  ]
586
-
587
  # Add conversation history if it exists
588
  if history:
589
  for exchange in history:
590
  messages.append({"role": "user", "content": exchange[0]})
591
  if len(exchange) > 1 and exchange[1]:
 
592
  messages.append({"role": "assistant", "content": exchange[1]})
593
-
594
  # Prepare the user message content
595
  user_message_content = []
596
-
597
  # Add text content if provided
598
  if problem_text.strip():
599
- user_message_content.append({
600
- "type": "text",
601
- "text": f"Solve this math problem: {problem_text}"
602
- })
 
 
603
  else:
604
- user_message_content.append({
605
- "type": "text",
606
- "text": "Solve this math problem from the image:"
607
- })
608
-
 
609
  # Add image if provided
610
  if image_path:
611
  # Convert image to base64
612
  base64_image = image_to_base64(image_path)
613
  if base64_image:
614
- user_message_content.append({
615
- "type": "image_url",
616
- "image_url": {
617
- "url": f"data:image/jpeg;base64,{base64_image}"
 
 
618
  }
619
- })
620
-
621
  # Add the user message with content
622
- messages.append({
623
- "role": "user",
624
- "content": user_message_content
625
- })
626
-
 
627
  # Create the completion
628
  response = client.chat.completions.create(
629
  model="ServiceNow-AI/Apriel-1.5-15b-Thinker",
630
  messages=messages,
631
  stream=False
632
  )
633
-
634
  markdown_solution = response.choices[0].message.content
635
-
636
  # Convert to HTML
637
  problem_display = problem_text if problem_text.strip() else "Image-based problem"
638
  html_solution = markdown_to_html(markdown_solution, problem_display)
639
-
640
  # Save HTML file
641
  html_file_path = save_html_to_file(html_solution, "together_solution")
642
-
643
- # Convert to PDF with proper math rendering
644
  pdf_file_path = html_to_pdf(html_solution, "together_solution")
645
-
646
- # Update history
647
  if history is None:
648
  history = []
649
  history.append((problem_display, markdown_solution))
650
-
651
  return html_solution, html_file_path, pdf_file_path, history
652
-
653
  except Exception as e:
654
  error_message = f"Error: {str(e)}"
655
  return error_message, None, None, history
@@ -658,7 +425,6 @@ def generate_math_solution_together(api_key, problem_text, image_path=None, hist
658
  def image_to_base64(image_path):
659
  if image_path is None:
660
  return None
661
-
662
  try:
663
  with open(image_path, "rb") as img_file:
664
  return base64.b64encode(img_file.read()).decode("utf-8")
@@ -670,12 +436,9 @@ def image_to_base64(image_path):
670
  def create_demo():
671
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
672
  gr.Markdown("# πŸ“š Advanced Math Tutor")
673
- gr.Markdown("""
674
- This application provides step-by-step solutions to math problems using advanced AI models.
675
- Solutions are generated in **HTML format** with proper mathematical notation rendering using MathJax.
676
- **PDFs are generated using headless browser** for perfect math rendering!
677
- """)
678
-
679
  # Main tabs
680
  with gr.Tabs():
681
  # Text-based problem solver (OpenRouter)
@@ -683,12 +446,12 @@ def create_demo():
683
  with gr.Row():
684
  with gr.Column(scale=1):
685
  openrouter_api_key = gr.Textbox(
686
- label="OpenRouter API Key",
687
  placeholder="Enter your OpenRouter API key (starts with sk-or-)",
688
  type="password"
689
  )
690
  text_problem_input = gr.Textbox(
691
- label="Math Problem",
692
  placeholder="Enter your math problem here...",
693
  lines=5
694
  )
@@ -697,53 +460,51 @@ def create_demo():
697
  ["Solve the quadratic equation: 3xΒ² + 5x - 2 = 0"],
698
  ["Find the derivative of f(x) = xΒ³ln(x)"],
699
  ["Calculate the area of a circle with radius 5 cm"],
700
- ["Integrate: ∫(2x + 3)dx from 0 to 5"]
701
  ],
702
  inputs=[text_problem_input],
703
  label="Example Problems"
704
  )
705
- with gr.Row():
706
- openrouter_submit_btn = gr.Button("Solve Problem", variant="primary")
707
- openrouter_clear_btn = gr.Button("Clear")
708
-
709
- with gr.Column(scale=2):
710
- openrouter_solution_output = gr.HTML(label="Solution (HTML with LaTeX)")
711
-
712
  with gr.Row():
713
- openrouter_html_download = gr.File(
714
- label="πŸ“„ Download HTML Solution",
715
- visible=False
716
- )
717
- openrouter_pdf_download = gr.File(
718
- label="πŸ“„ Download PDF Solution (Perfect Math Rendering)",
719
- visible=False
720
- )
721
-
 
 
 
 
 
722
  openrouter_conversation_history = gr.State(value=None)
723
-
724
  def handle_openrouter_submit(api_key, problem_text, history):
725
  html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter(
726
  api_key, problem_text, history
727
  )
728
-
729
  return (
730
  html_solution,
731
  updated_history,
732
  gr.update(value=html_file, visible=html_file is not None),
733
  gr.update(value=pdf_file, visible=pdf_file is not None)
734
  )
735
-
736
  openrouter_submit_btn.click(
737
  fn=handle_openrouter_submit,
738
  inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history],
739
  outputs=[
740
- openrouter_solution_output,
741
  openrouter_conversation_history,
742
  openrouter_html_download,
743
  openrouter_pdf_download
744
  ]
745
  )
746
-
747
  def clear_openrouter():
748
  return (
749
  "",
@@ -751,78 +512,76 @@ def create_demo():
751
  gr.update(value=None, visible=False),
752
  gr.update(value=None, visible=False)
753
  )
754
-
755
  openrouter_clear_btn.click(
756
  fn=clear_openrouter,
757
  inputs=[],
758
  outputs=[
759
- openrouter_solution_output,
760
  openrouter_conversation_history,
761
  openrouter_html_download,
762
  openrouter_pdf_download
763
  ]
764
  )
765
-
766
  # Image-based problem solver (Together AI)
767
  with gr.TabItem("Image Problem Solver (Together AI)"):
768
  with gr.Row():
769
  with gr.Column(scale=1):
770
  together_api_key = gr.Textbox(
771
- label="Together AI API Key",
772
  placeholder="Enter your Together AI API key",
773
  type="password"
774
  )
775
  together_problem_input = gr.Textbox(
776
- label="Problem Description (Optional)",
777
- placeholder="Enter additional context...",
778
  lines=3
779
  )
780
  together_image_input = gr.Image(
781
  label="Upload Math Problem Image",
782
  type="filepath"
783
  )
784
- with gr.Row():
785
- together_submit_btn = gr.Button("Solve Problem", variant="primary")
786
- together_clear_btn = gr.Button("Clear")
787
-
788
- with gr.Column(scale=2):
789
- together_solution_output = gr.HTML(label="Solution (HTML with LaTeX)")
790
-
791
  with gr.Row():
792
- together_html_download = gr.File(
793
- label="πŸ“„ Download HTML Solution",
794
- visible=False
795
- )
796
- together_pdf_download = gr.File(
797
- label="πŸ“„ Download PDF Solution (Perfect Math Rendering)",
798
- visible=False
799
- )
800
-
 
 
 
 
 
801
  together_conversation_history = gr.State(value=None)
802
-
803
  def handle_together_submit(api_key, problem_text, image_path, history):
804
  html_solution, html_file, pdf_file, updated_history = generate_math_solution_together(
805
  api_key, problem_text, image_path, history
806
  )
807
-
808
  return (
809
  html_solution,
810
  updated_history,
811
  gr.update(value=html_file, visible=html_file is not None),
812
  gr.update(value=pdf_file, visible=pdf_file is not None)
813
  )
814
-
815
  together_submit_btn.click(
816
  fn=handle_together_submit,
817
  inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history],
818
  outputs=[
819
- together_solution_output,
820
  together_conversation_history,
821
  together_html_download,
822
  together_pdf_download
823
  ]
824
  )
825
-
826
  def clear_together():
827
  return (
828
  "",
@@ -830,57 +589,83 @@ def create_demo():
830
  gr.update(value=None, visible=False),
831
  gr.update(value=None, visible=False)
832
  )
833
-
834
  together_clear_btn.click(
835
  fn=clear_together,
836
  inputs=[],
837
  outputs=[
838
- together_solution_output,
839
  together_conversation_history,
840
  together_html_download,
841
  together_pdf_download
842
  ]
843
  )
844
-
845
  # Help tab
846
- with gr.TabItem("Help & Installation"):
847
- gr.Markdown("""
848
- ## Installation Instructions
849
-
850
- This application requires the following packages:
851
-
852
- ```bash
853
- pip install gradio openai together pillow markdown playwright
854
- python -m playwright install chromium
855
- ```
856
-
857
- **Note:** Playwright is used for PDF generation with perfect math rendering!
858
-
859
- ## Key Features πŸŽ‰
860
- - βœ… **Perfect math rendering** in both HTML and PDF using MathJax
861
- - βœ… **HTML-formatted solutions** with professional styling
862
- - βœ… **Download HTML**: Save solutions as HTML files
863
- - βœ… **Download PDF**: PDFs with properly rendered mathematical notation (using headless browser)
864
- - βœ… **Print functionality**: Print or save directly to PDF from browser
865
- - βœ… **Image-to-Math capability** (via Together AI)
866
- - βœ… **Conversation memory** β€” keeps context of your previous problems
867
- - βœ… **Cross-model support** β€” OpenRouter and Together AI models
868
 
869
- ## Troubleshooting 🧩
870
- - If **PDF export fails**, ensure Playwright and Chromium are installed:
871
- ```bash
872
- python -m playwright install chromium
873
- ```
874
- - If **math symbols do not render**, reload the HTML file in a modern browser (MathJax will re-render).
875
- - If **you get API errors**, verify your API key is valid and has active quota.
876
-
877
- ## Usage Tips πŸ’‘
878
- - For **OpenRouter**, use an API key starting with `sk-or-`.
879
- - For **Together AI**, use your standard API key from [https://api.together.ai](https://api.together.ai).
880
- - You can **copy and share** generated HTML or PDF solutions.
881
- """)
882
-
883
- return demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884
 
885
  # Launch the app
886
  if __name__ == "__main__":
 
10
  import markdown
11
  from datetime import datetime
12
  import tempfile
13
+ import weasyprint
14
  from pathlib import Path
 
 
15
 
16
+ # Function to convert markdown to HTML with styling
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  def markdown_to_html(markdown_text, problem_text="", include_problem=True):
18
+ """Convert markdown to styled HTML"""
 
19
  # Convert markdown to HTML
20
  html_content = markdown.markdown(markdown_text, extensions=['tables', 'fenced_code'])
 
21
  # Get current timestamp
22
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
23
+ # Create styled HTML document
24
+ styled_html = f"""<!DOCTYPE html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="UTF-8">
28
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
29
+ <title>Math Solution - Advanced Math Tutor</title>
30
+ <style>
31
+ body {{
32
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
33
+ line-height: 1.6;
34
+ color: #333;
35
+ max-width: 800px;
36
+ margin: 0 auto;
37
+ padding: 20px;
38
+ background-color: #f9f9f9;
39
+ }}
40
+ .container {{
41
+ background-color: white;
42
+ padding: 40px;
43
+ border-radius: 10px;
44
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
45
+ }}
46
+ .header {{
47
+ text-align: center;
48
+ border-bottom: 3px solid #4CAF50;
49
+ padding-bottom: 20px;
50
+ margin-bottom: 30px;
51
+ }}
52
+ .header h1 {{
53
+ color: #2c3e50;
54
+ margin: 0;
55
+ font-size: 2.5em;
56
+ }}
57
+ .header .subtitle {{
58
+ color: #7f8c8d;
59
+ font-style: italic;
60
+ margin-top: 10px;
61
+ }}
62
+ .problem-section {{
63
+ background-color: #e8f5e8;
64
+ padding: 20px;
65
+ border-radius: 8px;
66
+ margin-bottom: 30px;
67
+ border-left: 5px solid #4CAF50;
68
+ }}
69
+ .problem-section h2 {{
70
+ color: #2c3e50;
71
+ margin-top: 0;
72
+ }}
73
+ .solution-content {{
74
+ background-color: #f8f9fa;
75
+ padding: 25px;
76
+ border-radius: 8px;
77
+ border-left: 5px solid #007bff;
78
+ }}
79
+ h1, h2, h3, h4, h5, h6 {{
80
+ color: #2c3e50;
81
+ margin-top: 25px;
82
+ margin-bottom: 15px;
83
+ }}
84
+ h2 {{
85
+ border-bottom: 2px solid #eee;
86
+ padding-bottom: 10px;
87
+ }}
88
+ code {{
89
+ background-color: #f1f1f1;
90
+ padding: 2px 6px;
91
+ border-radius: 3px;
92
+ font-family: 'Courier New', monospace;
93
+ color: #d63384;
94
+ }}
95
+ pre {{
96
+ background-color: #f8f8f8;
97
+ padding: 15px;
98
+ border-radius: 5px;
99
+ overflow-x: auto;
100
+ border: 1px solid #ddd;
101
+ }}
102
+ pre code {{
103
+ background-color: transparent;
104
+ padding: 0;
105
+ color: inherit;
106
+ }}
107
+ .math-expression {{
108
+ background-color: #fff3cd;
109
+ padding: 10px;
110
+ border-radius: 5px;
111
+ border: 1px solid #ffeaa7;
112
+ margin: 10px 0;
113
+ font-family: 'Times New Roman', serif;
114
+ font-size: 1.1em;
115
+ }}
116
+ .step {{
117
+ margin: 20px 0;
118
+ padding: 15px;
119
+ background-color: #ffffff;
120
+ border-radius: 8px;
121
+ border: 1px solid #dee2e6;
122
+ }}
123
+ .final-answer {{
124
+ background-color: #d4edda;
125
+ border: 2px solid #4CAF50;
126
+ padding: 20px;
127
+ border-radius: 8px;
128
+ margin-top: 30px;
129
+ text-align: center;
130
+ font-weight: bold;
131
+ font-size: 1.2em;
132
+ }}
133
+ .timestamp {{
134
+ text-align: right;
135
+ color: #6c757d;
136
+ font-size: 0.9em;
137
+ margin-top: 30px;
138
+ padding-top: 20px;
139
+ border-top: 1px solid #eee;
140
+ }}
141
+ ul, ol {{
142
+ padding-left: 25px;
143
+ }}
144
+ li {{
145
+ margin: 8px 0;
146
+ }}
147
+ table {{
148
+ border-collapse: collapse;
149
+ width: 100%;
150
+ margin: 20px 0;
151
+ }}
152
+ th, td {{
153
+ border: 1px solid #ddd;
154
+ padding: 12px;
155
+ text-align: left;
156
+ }}
157
+ th {{
158
+ background-color: #f8f9fa;
159
+ font-weight: bold;
160
+ }}
161
+ .print-button {{
162
+ background-color: #007bff;
163
+ color: white;
164
+ border: none;
165
+ padding: 10px 20px;
166
+ border-radius: 5px;
167
+ cursor: pointer;
168
+ font-size: 16px;
169
+ margin: 10px 5px;
170
+ display: inline-block;
171
+ text-decoration: none;
172
+ }}
173
+ .print-button:hover {{
174
+ background-color: #0056b3;
175
+ }}
176
+ @media print {{
177
  body {{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  background-color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  }}
180
+ .container {{
181
+ box-shadow: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  border: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }}
184
+ .print-button {{
185
+ display: none;
186
+ }}
187
+ }}
188
+ </style>
189
+ <script>
190
+ function printPage() {{
191
+ window.print();
192
+ }}
193
+ </script>
194
+ <!-- MathJax Configuration -->
195
+ <script>
196
+ window.MathJax = {{
197
+ tex: {{
198
+ inlineMath: [['$', '$'], ['\\\\(', '\\\\)']],
199
+ displayMath: [['$$', '$$'], ['\\\\[', '\\\\]']],
200
+ processEscapes: true,
201
+ tags: 'ams',
202
+ macros: {{
203
+ boxed: ['\\\\fbox{\\\\,#1}', 1]
204
  }}
205
+ }},
206
+ options: {{
207
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
208
+ ignoreHtmlClass: 'tex2jax_ignore',
209
+ processHtmlClass: 'tex2jax_process'
210
+ }},
211
+ loader: {{load: ['[tex]/ams', '[tex]/bbox']}},
212
+ startup: {{
213
+ ready: () => {{
214
+ MathJax.startup.defaultReady();
215
+ MathJax.typesetPromise();
216
  }}
217
  }}
218
+ }};
219
+ </script>
220
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
221
+ </head>
222
+ <body>
223
+ <div class="container">
224
+ <div class="header">
225
+ <h1>πŸ“š Advanced Math Tutor</h1>
226
+ <div class="subtitle">Step-by-Step Mathematical Solution</div>
227
+ </div>
228
+ <button class="print-button" onclick="printPage()">πŸ–¨οΈ Print to PDF</button>
229
+ {f'''<div class="problem-section">
230
+ <h2>πŸ“ Problem Statement</h2>
231
+ <p><strong>{problem_text}</strong></p>
232
+ </div>''' if include_problem and problem_text.strip() else ''}
233
+ <div class="solution-content">
234
+ <h2>πŸ” Solution</h2>
235
+ {html_content}
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  </div>
237
+ <div class="timestamp">Generated on: {timestamp}</div>
238
+ </div>
239
+ </body>
240
+ </html>"""
241
  return styled_html
242
 
243
  # Function to save HTML to file
 
245
  """Save HTML content to a temporary file and return the file path"""
246
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
247
  filename = f"{filename_prefix}_{timestamp}.html"
 
248
  # Create a temporary file
249
  temp_dir = tempfile.gettempdir()
250
  file_path = os.path.join(temp_dir, filename)
 
251
  with open(file_path, 'w', encoding='utf-8') as f:
252
  f.write(html_content)
 
253
  return file_path
254
 
255
+ # Function to convert HTML to PDF
256
  def html_to_pdf(html_content, filename_prefix="math_solution"):
257
+ """Convert HTML content to PDF and return the file path"""
258
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
259
  filename = f"{filename_prefix}_{timestamp}.pdf"
260
+ # Create a temporary file for PDF
261
  temp_dir = tempfile.gettempdir()
262
  pdf_path = os.path.join(temp_dir, filename)
 
263
  try:
264
+ # Convert HTML to PDF using WeasyPrint
265
+ weasyprint.HTML(string=html_content).write_pdf(pdf_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  return pdf_path
267
  except Exception as e:
268
+ print(f"Error converting to PDF: {str(e)}")
269
+ return None
 
 
 
 
 
 
 
 
 
270
 
271
  # Enhanced function to generate math solution using OpenRouter with HTML output
272
  def generate_math_solution_openrouter(api_key, problem_text, history=None):
273
  if not api_key.strip():
274
  return "Please enter your OpenRouter API key.", None, None, history
 
275
  if not problem_text.strip():
276
  return "Please enter a math problem.", None, None, history
 
277
  try:
278
  client = OpenAI(
279
  base_url="https://openrouter.ai/api/v1",
280
  api_key=api_key,
281
  )
 
282
  messages = [
283
+ {
284
+ "role": "system",
285
+ "content": """You are an expert math tutor who explains concepts clearly and thoroughly. Analyze the given math problem and provide a detailed step-by-step solution. For each step:
286
+ 1. Show the mathematical operation
287
+ 2. Explain why this step is necessary
288
+ 3. Connect it to relevant mathematical concepts
289
+ Format your response using markdown with clear section headers. Begin with an "Initial Analysis" section, follow with numbered steps, and conclude with a "Final Answer" section. Use proper markdown formatting including:
290
+ - Headers (##, ###)
291
+ - **Bold text** for important points
292
+ - Use LaTeX math delimiters: $...$ for inline math, $$...$$ for display math. Use LaTeX syntax inside (e.g., $$A = \\pi r^2$$, $\\boxed{25\\pi}$).
293
+ - Lists and numbered steps
294
+ - Tables if needed for comparisons or data
295
+ Do not use [ ] or code blocks for math expressions; use math delimiters instead."""
296
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  ]
 
298
  # Add conversation history if it exists
299
  if history:
300
  for exchange in history:
301
  messages.append({"role": "user", "content": exchange[0]})
302
  if len(exchange) > 1 and exchange[1]:
303
+ # Check if there's a response
304
  messages.append({"role": "assistant", "content": exchange[1]})
 
305
  # Add the current problem
306
  messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"})
 
307
  # Create the completion
308
  completion = client.chat.completions.create(
309
+ model="deepseek/deepseek-r1-0528:free",
310
  messages=messages,
311
  extra_headers={
312
  "HTTP-Referer": "https://advancedmathtutor.edu",
313
  "X-Title": "Advanced Math Tutor",
314
  }
315
  )
 
316
  markdown_solution = completion.choices[0].message.content
 
317
  # Convert to HTML
318
  html_solution = markdown_to_html(markdown_solution, problem_text)
 
319
  # Save HTML file
320
  html_file_path = save_html_to_file(html_solution, "openrouter_solution")
321
+ # Convert to PDF
 
322
  pdf_file_path = html_to_pdf(html_solution, "openrouter_solution")
 
323
  # Update history
324
  if history is None:
325
  history = []
326
  history.append((problem_text, markdown_solution))
 
327
  return html_solution, html_file_path, pdf_file_path, history
 
328
  except Exception as e:
329
  error_message = f"Error: {str(e)}"
330
  return error_message, None, None, history
 
333
  def generate_math_solution_together(api_key, problem_text, image_path=None, history=None):
334
  if not api_key.strip():
335
  return "Please enter your Together AI API key.", None, None, history
 
336
  if not problem_text.strip() and image_path is None:
337
  return "Please enter a math problem or upload an image of a math problem.", None, None, history
 
338
  try:
339
  client = Together(api_key=api_key)
 
340
  # Create the base message structure
341
  messages = [
342
  {
343
  "role": "system",
344
+ "content": """You are an expert math tutor who explains concepts clearly and thoroughly. Analyze the given math problem and provide a detailed step-by-step solution. For each step:
345
+ 1. Show the mathematical operation
346
+ 2. Explain why this step is necessary
347
+ 3. Connect it to relevant mathematical concepts
348
+ Format your response using markdown with clear section headers. Begin with an "Initial Analysis" section, follow with numbered steps, and conclude with a "Final Answer" section. Use proper markdown formatting including:
349
+ - Headers (##, ###)
350
+ - **Bold text** for important points
351
+ - Use LaTeX math delimiters: $...$ for inline math, $$...$$ for display math. Use LaTeX syntax inside (e.g., $$A = \\pi r^2$$, $\\boxed{25\\pi}$).
352
+ - Lists and numbered steps
353
+ - Tables if needed for comparisons or data
354
+ Do not use [ ] or code blocks for math expressions; use math delimiters instead."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  }
356
  ]
 
357
  # Add conversation history if it exists
358
  if history:
359
  for exchange in history:
360
  messages.append({"role": "user", "content": exchange[0]})
361
  if len(exchange) > 1 and exchange[1]:
362
+ # Check if there's a response
363
  messages.append({"role": "assistant", "content": exchange[1]})
 
364
  # Prepare the user message content
365
  user_message_content = []
 
366
  # Add text content if provided
367
  if problem_text.strip():
368
+ user_message_content.append(
369
+ {
370
+ "type": "text",
371
+ "text": f"Solve this math problem: {problem_text}"
372
+ }
373
+ )
374
  else:
375
+ user_message_content.append(
376
+ {
377
+ "type": "text",
378
+ "text": "Solve this math problem from the image:"
379
+ }
380
+ )
381
  # Add image if provided
382
  if image_path:
383
  # Convert image to base64
384
  base64_image = image_to_base64(image_path)
385
  if base64_image:
386
+ user_message_content.append(
387
+ {
388
+ "type": "image_url",
389
+ "image_url": {
390
+ "url": f"data:image/jpeg;base64,{base64_image}"
391
+ }
392
  }
393
+ )
 
394
  # Add the user message with content
395
+ messages.append(
396
+ {
397
+ "role": "user",
398
+ "content": user_message_content
399
+ }
400
+ )
401
  # Create the completion
402
  response = client.chat.completions.create(
403
  model="ServiceNow-AI/Apriel-1.5-15b-Thinker",
404
  messages=messages,
405
  stream=False
406
  )
 
407
  markdown_solution = response.choices[0].message.content
 
408
  # Convert to HTML
409
  problem_display = problem_text if problem_text.strip() else "Image-based problem"
410
  html_solution = markdown_to_html(markdown_solution, problem_display)
 
411
  # Save HTML file
412
  html_file_path = save_html_to_file(html_solution, "together_solution")
413
+ # Convert to PDF
 
414
  pdf_file_path = html_to_pdf(html_solution, "together_solution")
415
+ # Update history - for simplicity, just store the text problem
 
416
  if history is None:
417
  history = []
418
  history.append((problem_display, markdown_solution))
 
419
  return html_solution, html_file_path, pdf_file_path, history
 
420
  except Exception as e:
421
  error_message = f"Error: {str(e)}"
422
  return error_message, None, None, history
 
425
  def image_to_base64(image_path):
426
  if image_path is None:
427
  return None
 
428
  try:
429
  with open(image_path, "rb") as img_file:
430
  return base64.b64encode(img_file.read()).decode("utf-8")
 
436
  def create_demo():
437
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
438
  gr.Markdown("# πŸ“š Advanced Math Tutor")
439
+ gr.Markdown(
440
+ """This application provides step-by-step solutions to math problems using advanced AI models. Solutions are generated in **HTML format** with download and print-to-PDF capabilities. Choose between OpenRouter's Phi-4-reasoning-plus for text-based problems or Together AI's Llama-Vision for problems with images."""
441
+ )
 
 
 
442
  # Main tabs
443
  with gr.Tabs():
444
  # Text-based problem solver (OpenRouter)
 
446
  with gr.Row():
447
  with gr.Column(scale=1):
448
  openrouter_api_key = gr.Textbox(
449
+ label="OpenRouter API Key",
450
  placeholder="Enter your OpenRouter API key (starts with sk-or-)",
451
  type="password"
452
  )
453
  text_problem_input = gr.Textbox(
454
+ label="Math Problem",
455
  placeholder="Enter your math problem here...",
456
  lines=5
457
  )
 
460
  ["Solve the quadratic equation: 3xΒ² + 5x - 2 = 0"],
461
  ["Find the derivative of f(x) = xΒ³ln(x)"],
462
  ["Calculate the area of a circle with radius 5 cm"],
463
+ ["Find all values of x that satisfy the equation: logβ‚‚(x-1) + logβ‚‚(x+3) = 5"]
464
  ],
465
  inputs=[text_problem_input],
466
  label="Example Problems"
467
  )
 
 
 
 
 
 
 
468
  with gr.Row():
469
+ openrouter_submit_btn = gr.Button("Solve Problem", variant="primary")
470
+ openrouter_clear_btn = gr.Button("Clear")
471
+ with gr.Column(scale=2):
472
+ openrouter_solution_output = gr.HTML(label="Solution (HTML Format)")
473
+ with gr.Row():
474
+ openrouter_html_download = gr.File(
475
+ label="πŸ“„ Download HTML Solution",
476
+ visible=False
477
+ )
478
+ openrouter_pdf_download = gr.File(
479
+ label="πŸ“„ Download PDF Solution",
480
+ visible=False
481
+ )
482
+ # Store conversation history (invisible to user)
483
  openrouter_conversation_history = gr.State(value=None)
484
+ # Button actions
485
  def handle_openrouter_submit(api_key, problem_text, history):
486
  html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter(
487
  api_key, problem_text, history
488
  )
489
+ # Return outputs including file updates
490
  return (
491
  html_solution,
492
  updated_history,
493
  gr.update(value=html_file, visible=html_file is not None),
494
  gr.update(value=pdf_file, visible=pdf_file is not None)
495
  )
496
+
497
  openrouter_submit_btn.click(
498
  fn=handle_openrouter_submit,
499
  inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history],
500
  outputs=[
501
+ openrouter_solution_output,
502
  openrouter_conversation_history,
503
  openrouter_html_download,
504
  openrouter_pdf_download
505
  ]
506
  )
507
+
508
  def clear_openrouter():
509
  return (
510
  "",
 
512
  gr.update(value=None, visible=False),
513
  gr.update(value=None, visible=False)
514
  )
515
+
516
  openrouter_clear_btn.click(
517
  fn=clear_openrouter,
518
  inputs=[],
519
  outputs=[
520
+ openrouter_solution_output,
521
  openrouter_conversation_history,
522
  openrouter_html_download,
523
  openrouter_pdf_download
524
  ]
525
  )
526
+
527
  # Image-based problem solver (Together AI)
528
  with gr.TabItem("Image Problem Solver (Together AI)"):
529
  with gr.Row():
530
  with gr.Column(scale=1):
531
  together_api_key = gr.Textbox(
532
+ label="Together AI API Key",
533
  placeholder="Enter your Together AI API key",
534
  type="password"
535
  )
536
  together_problem_input = gr.Textbox(
537
+ label="Problem Description (Optional)",
538
+ placeholder="Enter additional context for the image problem...",
539
  lines=3
540
  )
541
  together_image_input = gr.Image(
542
  label="Upload Math Problem Image",
543
  type="filepath"
544
  )
 
 
 
 
 
 
 
545
  with gr.Row():
546
+ together_submit_btn = gr.Button("Solve Problem", variant="primary")
547
+ together_clear_btn = gr.Button("Clear")
548
+ with gr.Column(scale=2):
549
+ together_solution_output = gr.HTML(label="Solution (HTML Format)")
550
+ with gr.Row():
551
+ together_html_download = gr.File(
552
+ label="πŸ“„ Download HTML Solution",
553
+ visible=False
554
+ )
555
+ together_pdf_download = gr.File(
556
+ label="πŸ“„ Download PDF Solution",
557
+ visible=False
558
+ )
559
+ # Store conversation history (invisible to user)
560
  together_conversation_history = gr.State(value=None)
561
+ # Button actions
562
  def handle_together_submit(api_key, problem_text, image_path, history):
563
  html_solution, html_file, pdf_file, updated_history = generate_math_solution_together(
564
  api_key, problem_text, image_path, history
565
  )
566
+ # Return outputs including file updates
567
  return (
568
  html_solution,
569
  updated_history,
570
  gr.update(value=html_file, visible=html_file is not None),
571
  gr.update(value=pdf_file, visible=pdf_file is not None)
572
  )
573
+
574
  together_submit_btn.click(
575
  fn=handle_together_submit,
576
  inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history],
577
  outputs=[
578
+ together_solution_output,
579
  together_conversation_history,
580
  together_html_download,
581
  together_pdf_download
582
  ]
583
  )
584
+
585
  def clear_together():
586
  return (
587
  "",
 
589
  gr.update(value=None, visible=False),
590
  gr.update(value=None, visible=False)
591
  )
592
+
593
  together_clear_btn.click(
594
  fn=clear_together,
595
  inputs=[],
596
  outputs=[
597
+ together_solution_output,
598
  together_conversation_history,
599
  together_html_download,
600
  together_pdf_download
601
  ]
602
  )
603
+
604
  # Help tab
605
+ with gr.TabItem("Help"):
606
+ gr.Markdown(
607
+ """## How to Use the Advanced Math Tutor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
 
609
+ ### New Features πŸŽ‰
610
+ - **HTML-formatted solutions**: All responses are now generated in beautiful HTML format with MathJax for proper math rendering
611
+ - **Download HTML**: Download the complete solution as an HTML file (math renders perfectly)
612
+ - **Download PDF**: Convert and download solutions as PDF files (note: math may appear as raw LaTeX in server-generated PDF due to limitations; use browser print for best results)
613
+ - **Print functionality**: Use the "Print to PDF" button in the HTML output to print directly (renders math via MathJax)
614
+
615
+ ### Getting Started
616
+ #### For Text-Based Problems (OpenRouter)
617
+ 1. You'll need an API key from OpenRouter
618
+ 2. Sign up at [OpenRouter](https://openrouter.ai/) to get your API key
619
+ 3. Enter your API key in the designated field in the "Text Problem Solver" tab
620
+
621
+ #### For Image-Based Problems (Together AI)
622
+ 1. You'll need an API key from Together AI
623
+ 2. Sign up at [Together AI](https://www.together.ai/) to get your API key
624
+ 3. Enter your API key in the designated field in the "Image Problem Solver" tab
625
+ 4. Upload an image of your math problem
626
+ 5. Optionally add text to provide additional context
627
+
628
+ ### Solving Math Problems
629
+ - For text problems: Type or paste your math problem in the input field
630
+ - For image problems: Upload a clear image of the math problem
631
+ - Click "Solve Problem" to get a detailed step-by-step solution in HTML format
632
+ - Use the download buttons to save HTML or PDF versions
633
+ - Click "Print to PDF" within the solution to print directly from your browser
634
+
635
+ ### HTML Output Features
636
+ - **Professional styling**: Clean, readable format with proper typography
637
+ - **Mathematical expressions**: Rendered with MathJax for beautiful LaTeX support
638
+ - **Step-by-step sections**: Clearly organized solution steps
639
+ - **Print-friendly**: Optimized for printing and PDF conversion
640
+ - **Timestamps**: Each solution includes generation timestamp
641
+
642
+ **Note**: For best math rendering in PDF, open the HTML in your browser and use "Print to PDF" – this executes MathJax for perfect equations. The server-generated PDF may show raw math delimiters.
643
+
644
+ ### Tips for Best Results
645
+ - Be specific in your problem description
646
+ - Include all necessary information
647
+ - For complex equations, use clear notation
648
+ - For algebraic expressions, describe clearly (AI will format math properly)
649
+ - Use parentheses to group terms clearly
650
+ - For images, ensure the math problem is clearly visible and well-lit
651
+
652
+ ### Types of Problems You Can Solve
653
+ - Algebra (equations, inequalities, systems of equations)
654
+ - Calculus (derivatives, integrals, limits)
655
+ - Trigonometry
656
+ - Geometry
657
+ - Statistics and Probability
658
+ - Number Theory
659
+ - And many more!
660
+
661
+ ### Required Dependencies
662
+ To run this application, you'll need to install:
663
+ ```bash
664
+ pip install gradio openai together pillow markdown weasyprint
665
+
666
+ MathJax is loaded via CDN – no additional install needed."""
667
+ )
668
+ return demo
669
 
670
  # Launch the app
671
  if __name__ == "__main__":