shukdevdatta123 commited on
Commit
45b0793
·
verified ·
1 Parent(s): 5d7c590

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1596 -5
app.py CHANGED
@@ -9,15 +9,14 @@ from PIL import Image
9
  import io
10
  from datetime import datetime
11
  import tempfile
12
- import weasyprint
13
  from pathlib import Path
14
 
15
- # Function to convert markdown to HTML with styling
16
  def markdown_to_html(markdown_text, problem_text="", include_problem=True):
17
- """Convert markdown to styled HTML"""
18
 
19
- # Convert markdown to HTML
20
- html_content = markdown.markdown(markdown_text, extensions=['tables', 'fenced_code'])
21
 
22
  # Get current timestamp
23
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -223,9 +222,1601 @@ def markdown_to_html(markdown_text, problem_text="", include_problem=True):
223
  </body>
224
  </html>
225
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
  return styled_html
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  # Function to save HTML to file
230
  def save_html_to_file(html_content, filename_prefix="math_solution"):
231
  """Save HTML content to a temporary file and return the file path"""
 
9
  import io
10
  from datetime import datetime
11
  import tempfile
 
12
  from pathlib import Path
13
 
14
+ # Function to convert markdown-like text to HTML with styling
15
  def markdown_to_html(markdown_text, problem_text="", include_problem=True):
16
+ """Convert markdown-like text to styled HTML"""
17
 
18
+ # Simple markdown to HTML conversion
19
+ html_content = simple_markdown_to_html(markdown_text)
20
 
21
  # Get current timestamp
22
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
222
  </body>
223
  </html>
224
  """
225
+ # Simple markdown to HTML converter
226
+ def simple_markdown_to_html(text):
227
+ """Convert basic markdown to HTML without external libraries"""
228
+ if not text:
229
+ return ""
230
+
231
+ # Convert headers
232
+ text = re.sub(r'^### (.*?)
233
+
234
+ # Function to save HTML to file
235
+ def save_html_to_file(html_content, filename_prefix="math_solution"):
236
+ """Save HTML content to a temporary file and return the file path"""
237
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
238
+ filename = f"{filename_prefix}_{timestamp}.html"
239
+
240
+ # Create a temporary file
241
+ temp_dir = tempfile.gettempdir()
242
+ file_path = os.path.join(temp_dir, filename)
243
+
244
+ with open(file_path, 'w', encoding='utf-8') as f:
245
+ f.write(html_content)
246
+
247
+ return file_path
248
+
249
 
250
  return styled_html
251
 
252
+ # Enhanced function to generate math solution using OpenRouter with HTML output
253
+ def generate_math_solution_openrouter(api_key, problem_text, history=None):
254
+ if not api_key.strip():
255
+ return "Please enter your OpenRouter API key.", None, None, history
256
+
257
+ if not problem_text.strip():
258
+ return "Please enter a math problem.", None, None, history
259
+
260
+ try:
261
+ client = OpenAI(
262
+ base_url="https://openrouter.ai/api/v1",
263
+ api_key=api_key,
264
+ )
265
+
266
+ messages = [
267
+ {"role": "system", "content":
268
+ """You are an expert math tutor who explains concepts clearly and thoroughly.
269
+ Analyze the given math problem and provide a detailed step-by-step solution.
270
+ For each step:
271
+ 1. Show the mathematical operation
272
+ 2. Explain why this step is necessary
273
+ 3. Connect it to relevant mathematical concepts
274
+
275
+ Format your response using markdown with clear section headers.
276
+ Begin with an "Initial Analysis" section, follow with numbered steps,
277
+ and conclude with a "Final Answer" section.
278
+
279
+ Use proper markdown formatting including:
280
+ - Headers (##, ###)
281
+ - **Bold text** for important points
282
+ - `Code blocks` for mathematical expressions
283
+ - Lists and numbered steps
284
+ - Tables if needed for comparisons or data"""},
285
+ ]
286
+
287
+ # Add conversation history if it exists
288
+ if history:
289
+ for exchange in history:
290
+ messages.append({"role": "user", "content": exchange[0]})
291
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
292
+ messages.append({"role": "assistant", "content": exchange[1]})
293
+
294
+ # Add the current problem
295
+ messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"})
296
+
297
+ # Create the completion
298
+ completion = client.chat.completions.create(
299
+ model="microsoft/phi-4-reasoning-plus:free",
300
+ messages=messages,
301
+ extra_headers={
302
+ "HTTP-Referer": "https://advancedmathtutor.edu",
303
+ "X-Title": "Advanced Math Tutor",
304
+ }
305
+ )
306
+
307
+ markdown_solution = completion.choices[0].message.content
308
+
309
+ # Convert to HTML
310
+ html_solution = markdown_to_html(markdown_solution, problem_text)
311
+
312
+ # Save HTML file
313
+ html_file_path = save_html_to_file(html_solution, "openrouter_solution")
314
+
315
+ # Convert to PDF alternative (text file)
316
+ pdf_file_path = html_to_pdf_simple(html_solution, "openrouter_solution")
317
+
318
+ # Update history
319
+ if history is None:
320
+ history = []
321
+ history.append((problem_text, markdown_solution))
322
+
323
+ return html_solution, html_file_path, pdf_file_path, history
324
+
325
+ except Exception as e:
326
+ error_message = f"Error: {str(e)}"
327
+ return error_message, None, None, history
328
+
329
+ # Enhanced function to generate math solution using Together AI with HTML output
330
+ def generate_math_solution_together(api_key, problem_text, image_path=None, history=None):
331
+ if not api_key.strip():
332
+ return "Please enter your Together AI API key.", None, None, history
333
+
334
+ if not problem_text.strip() and image_path is None:
335
+ return "Please enter a math problem or upload an image of a math problem.", None, None, history
336
+
337
+ try:
338
+ client = Together(api_key=api_key)
339
+
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.
345
+ Analyze the given math problem and provide a detailed step-by-step solution.
346
+ For each step:
347
+ 1. Show the mathematical operation
348
+ 2. Explain why this step is necessary
349
+ 3. Connect it to relevant mathematical concepts
350
+
351
+ Format your response using markdown with clear section headers.
352
+ Begin with an "Initial Analysis" section, follow with numbered steps,
353
+ and conclude with a "Final Answer" section.
354
+
355
+ Use proper markdown formatting including:
356
+ - Headers (##, ###)
357
+ - **Bold text** for important points
358
+ - `Code blocks` for mathematical expressions
359
+ - Lists and numbered steps
360
+ - Tables if needed for comparisons or data"""
361
+ }
362
+ ]
363
+
364
+ # Add conversation history if it exists
365
+ if history:
366
+ for exchange in history:
367
+ messages.append({"role": "user", "content": exchange[0]})
368
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
369
+ messages.append({"role": "assistant", "content": exchange[1]})
370
+
371
+ # Prepare the user message content
372
+ user_message_content = []
373
+
374
+ # Add text content if provided
375
+ if problem_text.strip():
376
+ user_message_content.append({
377
+ "type": "text",
378
+ "text": f"Solve this math problem: {problem_text}"
379
+ })
380
+ else:
381
+ user_message_content.append({
382
+ "type": "text",
383
+ "text": "Solve this math problem from the image:"
384
+ })
385
+
386
+ # Add image if provided
387
+ if image_path:
388
+ # Convert image to base64
389
+ base64_image = image_to_base64(image_path)
390
+ if base64_image:
391
+ user_message_content.append({
392
+ "type": "image_url",
393
+ "image_url": {
394
+ "url": f"data:image/jpeg;base64,{base64_image}"
395
+ }
396
+ })
397
+
398
+ # Add the user message with content
399
+ messages.append({
400
+ "role": "user",
401
+ "content": user_message_content
402
+ })
403
+
404
+ # Create the completion
405
+ response = client.chat.completions.create(
406
+ model="meta-llama/Llama-Vision-Free",
407
+ messages=messages,
408
+ stream=False
409
+ )
410
+
411
+ markdown_solution = response.choices[0].message.content
412
+
413
+ # Convert to HTML
414
+ problem_display = problem_text if problem_text.strip() else "Image-based problem"
415
+ html_solution = markdown_to_html(markdown_solution, problem_display)
416
+
417
+ # Save HTML file
418
+ html_file_path = save_html_to_file(html_solution, "together_solution")
419
+
420
+ # Convert to PDF alternative (text file)
421
+ pdf_file_path = html_to_pdf_simple(html_solution, "together_solution")
422
+
423
+ # Update history - for simplicity, just store the text problem
424
+ if history is None:
425
+ history = []
426
+ history.append((problem_display, markdown_solution))
427
+
428
+ return html_solution, html_file_path, pdf_file_path, history
429
+
430
+ except Exception as e:
431
+ error_message = f"Error: {str(e)}"
432
+ return error_message, None, None, history
433
+
434
+ # Function to convert image to base64
435
+ def image_to_base64(image_path):
436
+ if image_path is None:
437
+ return None
438
+
439
+ try:
440
+ with open(image_path, "rb") as img_file:
441
+ return base64.b64encode(img_file.read()).decode("utf-8")
442
+ except Exception as e:
443
+ print(f"Error converting image to base64: {str(e)}")
444
+ return None
445
+
446
+ # Define the Gradio interface
447
+ def create_demo():
448
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
449
+ gr.Markdown("# 📚 Advanced Math Tutor")
450
+ gr.Markdown("""
451
+ This application provides step-by-step solutions to math problems using advanced AI models.
452
+ Solutions are generated in **HTML format** with download and print-to-PDF capabilities.
453
+ Choose between OpenRouter's Phi-4-reasoning-plus for text-based problems or Together AI's
454
+ Llama-Vision for problems with images.
455
+ """)
456
+
457
+ # Main tabs
458
+ with gr.Tabs():
459
+ # Text-based problem solver (OpenRouter)
460
+ with gr.TabItem("Text Problem Solver (OpenRouter)"):
461
+ with gr.Row():
462
+ with gr.Column(scale=1):
463
+ openrouter_api_key = gr.Textbox(
464
+ label="OpenRouter API Key",
465
+ placeholder="Enter your OpenRouter API key (starts with sk-or-)",
466
+ type="password"
467
+ )
468
+ text_problem_input = gr.Textbox(
469
+ label="Math Problem",
470
+ placeholder="Enter your math problem here...",
471
+ lines=5
472
+ )
473
+ example_problems = gr.Examples(
474
+ examples=[
475
+ ["Solve the quadratic equation: 3x² + 5x - 2 = 0"],
476
+ ["Find the derivative of f(x) = x³ln(x)"],
477
+ ["Calculate the area of a circle with radius 5 cm"],
478
+ ["Find all values of x that satisfy the equation: log₂(x-1) + log₂(x+3) = 5"]
479
+ ],
480
+ inputs=[text_problem_input],
481
+ label="Example Problems"
482
+ )
483
+ with gr.Row():
484
+ openrouter_submit_btn = gr.Button("Solve Problem", variant="primary")
485
+ openrouter_clear_btn = gr.Button("Clear")
486
+
487
+ with gr.Column(scale=2):
488
+ openrouter_solution_output = gr.HTML(label="Solution (HTML Format)")
489
+
490
+ with gr.Row():
491
+ openrouter_html_download = gr.File(
492
+ label="📄 Download HTML Solution",
493
+ visible=False
494
+ )
495
+ openrouter_pdf_download = gr.File(
496
+ label="📄 Download Text Version",
497
+ visible=False
498
+ )
499
+
500
+ # Store conversation history (invisible to user)
501
+ openrouter_conversation_history = gr.State(value=None)
502
+
503
+ # Button actions
504
+ def handle_openrouter_submit(api_key, problem_text, history):
505
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter(
506
+ api_key, problem_text, history
507
+ )
508
+
509
+ # Return outputs including file updates
510
+ return (
511
+ html_solution,
512
+ updated_history,
513
+ gr.update(value=html_file, visible=html_file is not None),
514
+ gr.update(value=pdf_file, visible=pdf_file is not None)
515
+ )
516
+
517
+ openrouter_submit_btn.click(
518
+ fn=handle_openrouter_submit,
519
+ inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history],
520
+ outputs=[
521
+ openrouter_solution_output,
522
+ openrouter_conversation_history,
523
+ openrouter_html_download,
524
+ openrouter_pdf_download
525
+ ]
526
+ )
527
+
528
+ def clear_openrouter():
529
+ return (
530
+ "",
531
+ None,
532
+ gr.update(value=None, visible=False),
533
+ gr.update(value=None, visible=False)
534
+ )
535
+
536
+ openrouter_clear_btn.click(
537
+ fn=clear_openrouter,
538
+ inputs=[],
539
+ outputs=[
540
+ openrouter_solution_output,
541
+ openrouter_conversation_history,
542
+ openrouter_html_download,
543
+ openrouter_pdf_download
544
+ ]
545
+ )
546
+
547
+ # Image-based problem solver (Together AI)
548
+ with gr.TabItem("Image Problem Solver (Together AI)"):
549
+ with gr.Row():
550
+ with gr.Column(scale=1):
551
+ together_api_key = gr.Textbox(
552
+ label="Together AI API Key",
553
+ placeholder="Enter your Together AI API key",
554
+ type="password"
555
+ )
556
+ together_problem_input = gr.Textbox(
557
+ label="Problem Description (Optional)",
558
+ placeholder="Enter additional context for the image problem...",
559
+ lines=3
560
+ )
561
+ together_image_input = gr.Image(
562
+ label="Upload Math Problem Image",
563
+ type="filepath"
564
+ )
565
+ with gr.Row():
566
+ together_submit_btn = gr.Button("Solve Problem", variant="primary")
567
+ together_clear_btn = gr.Button("Clear")
568
+
569
+ with gr.Column(scale=2):
570
+ together_solution_output = gr.HTML(label="Solution (HTML Format)")
571
+
572
+ with gr.Row():
573
+ together_html_download = gr.File(
574
+ label="📄 Download HTML Solution",
575
+ visible=False
576
+ )
577
+ together_pdf_download = gr.File(
578
+ label="📄 Download Text Version",
579
+ visible=False
580
+ )
581
+
582
+ # Store conversation history (invisible to user)
583
+ together_conversation_history = gr.State(value=None)
584
+
585
+ # Button actions
586
+ def handle_together_submit(api_key, problem_text, image_path, history):
587
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_together(
588
+ api_key, problem_text, image_path, history
589
+ )
590
+
591
+ # Return outputs including file updates
592
+ return (
593
+ html_solution,
594
+ updated_history,
595
+ gr.update(value=html_file, visible=html_file is not None),
596
+ gr.update(value=pdf_file, visible=pdf_file is not None)
597
+ )
598
+
599
+ together_submit_btn.click(
600
+ fn=handle_together_submit,
601
+ inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history],
602
+ outputs=[
603
+ together_solution_output,
604
+ together_conversation_history,
605
+ together_html_download,
606
+ together_pdf_download
607
+ ]
608
+ )
609
+
610
+ def clear_together():
611
+ return (
612
+ "",
613
+ None,
614
+ gr.update(value=None, visible=False),
615
+ gr.update(value=None, visible=False)
616
+ )
617
+
618
+ together_clear_btn.click(
619
+ fn=clear_together,
620
+ inputs=[],
621
+ outputs=[
622
+ together_solution_output,
623
+ together_conversation_history,
624
+ together_html_download,
625
+ together_pdf_download
626
+ ]
627
+ )
628
+
629
+ # Help tab
630
+ with gr.TabItem("Help"):
631
+ gr.Markdown("""
632
+ ## How to Use the Advanced Math Tutor
633
+
634
+ ### New Features 🎉
635
+ - **HTML-formatted solutions**: All responses are now generated in beautiful HTML format
636
+ - **Download HTML**: Download the complete solution as an HTML file
637
+ - **Download Text**: Download solutions as clean text files
638
+ - **Print functionality**: Use the "Print to PDF" button in the HTML output to print directly
639
+
640
+ ### Getting Started
641
+
642
+ #### For Text-Based Problems (OpenRouter)
643
+ 1. You'll need an API key from OpenRouter
644
+ 2. Sign up at [OpenRouter](https://openrouter.ai/) to get your API key
645
+ 3. Enter your API key in the designated field in the "Text Problem Solver" tab
646
+
647
+ #### For Image-Based Problems (Together AI)
648
+ 1. You'll need an API key from Together AI
649
+ 2. Sign up at [Together AI](https://www.together.ai/) to get your API key
650
+ 3. Enter your API key in the designated field in the "Image Problem Solver" tab
651
+ 4. Upload an image of your math problem
652
+ 5. Optionally add text to provide additional context
653
+
654
+ ### Solving Math Problems
655
+ - For text problems: Type or paste your math problem in the input field
656
+ - For image problems: Upload a clear image of the math problem
657
+ - Click "Solve Problem" to get a detailed step-by-step solution in HTML format
658
+ - Use the download buttons to save HTML or PDF versions
659
+ - Click "Print to PDF" within the solution to print directly from your browser
660
+
661
+ ### HTML Output Features
662
+ - **Professional styling**: Clean, readable format with proper typography
663
+ - **Mathematical expressions**: Highlighted math expressions and code blocks
664
+ - **Step-by-step sections**: Clearly organized solution steps
665
+ - **Print-friendly**: Optimized for printing and PDF conversion
666
+ - **Timestamps**: Each solution includes generation timestamp
667
+
668
+ ### Tips for Best Results
669
+ - Be specific in your problem description
670
+ - Include all necessary information
671
+ - For complex equations, use clear notation
672
+ - For algebraic expressions, use ^ for exponents (e.g., x^2 for x²)
673
+ - Use parentheses to group terms clearly
674
+ - For images, ensure the math problem is clearly visible and well-lit
675
+
676
+ ### Types of Problems You Can Solve
677
+ - Algebra (equations, inequalities, systems of equations)
678
+ - Calculus (derivatives, integrals, limits)
679
+ - Trigonometry
680
+ - Geometry
681
+ - Statistics and Probability
682
+ - Number Theory
683
+ - And many more!
684
+
685
+ ### Required Dependencies
686
+ To run this application, you'll need to install:
687
+ ```bash
688
+ pip install gradio openai together pillow
689
+ ```
690
+ """)
691
+
692
+ # Footer
693
+ gr.Markdown("""
694
+ ---
695
+ ### About
696
+ This enhanced application uses Microsoft's Phi-4-reasoning-plus model via OpenRouter for text-based problems
697
+ and Llama-Vision-Free via Together AI for image-based problems.
698
+
699
+ **New Features:**
700
+ - HTML-formatted responses with professional styling
701
+ - Download solutions as HTML files
702
+ - Download solutions as clean text files
703
+ - Print-to-PDF functionality using browser's built-in print feature
704
+ - Enhanced formatting with mathematical expressions highlighting
705
+
706
+ Your API keys are required but not stored permanently.
707
+ """)
708
+
709
+ return demo
710
+
711
+ # Launch the app
712
+ if __name__ == "__main__":
713
+ demo = create_demo()
714
+ demo.launch()
715
+ , r'<h3>\1</h3>', text, flags=re.MULTILINE)
716
+ text = re.sub(r'^## (.*?)
717
+
718
+ # Function to save HTML to file
719
+ def save_html_to_file(html_content, filename_prefix="math_solution"):
720
+ """Save HTML content to a temporary file and return the file path"""
721
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
722
+ filename = f"{filename_prefix}_{timestamp}.html"
723
+
724
+ # Create a temporary file
725
+ temp_dir = tempfile.gettempdir()
726
+ file_path = os.path.join(temp_dir, filename)
727
+
728
+ with open(file_path, 'w', encoding='utf-8') as f:
729
+ f.write(html_content)
730
+
731
+ return file_path
732
+
733
+ # Function to convert HTML to PDF
734
+ def html_to_pdf(html_content, filename_prefix="math_solution"):
735
+ """Convert HTML content to PDF and return the file path"""
736
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
737
+ filename = f"{filename_prefix}_{timestamp}.pdf"
738
+
739
+ # Create a temporary file for PDF
740
+ temp_dir = tempfile.gettempdir()
741
+ pdf_path = os.path.join(temp_dir, filename)
742
+
743
+ try:
744
+ # Convert HTML to PDF using WeasyPrint
745
+ weasyprint.HTML(string=html_content).write_pdf(pdf_path)
746
+ return pdf_path
747
+ except Exception as e:
748
+ print(f"Error converting to PDF: {str(e)}")
749
+ return None
750
+
751
+ # Enhanced function to generate math solution using OpenRouter with HTML output
752
+ def generate_math_solution_openrouter(api_key, problem_text, history=None):
753
+ if not api_key.strip():
754
+ return "Please enter your OpenRouter API key.", None, None, history
755
+
756
+ if not problem_text.strip():
757
+ return "Please enter a math problem.", None, None, history
758
+
759
+ try:
760
+ client = OpenAI(
761
+ base_url="https://openrouter.ai/api/v1",
762
+ api_key=api_key,
763
+ )
764
+
765
+ messages = [
766
+ {"role": "system", "content":
767
+ """You are an expert math tutor who explains concepts clearly and thoroughly.
768
+ Analyze the given math problem and provide a detailed step-by-step solution.
769
+ For each step:
770
+ 1. Show the mathematical operation
771
+ 2. Explain why this step is necessary
772
+ 3. Connect it to relevant mathematical concepts
773
+
774
+ Format your response using markdown with clear section headers.
775
+ Begin with an "Initial Analysis" section, follow with numbered steps,
776
+ and conclude with a "Final Answer" section.
777
+
778
+ Use proper markdown formatting including:
779
+ - Headers (##, ###)
780
+ - **Bold text** for important points
781
+ - `Code blocks` for mathematical expressions
782
+ - Lists and numbered steps
783
+ - Tables if needed for comparisons or data"""},
784
+ ]
785
+
786
+ # Add conversation history if it exists
787
+ if history:
788
+ for exchange in history:
789
+ messages.append({"role": "user", "content": exchange[0]})
790
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
791
+ messages.append({"role": "assistant", "content": exchange[1]})
792
+
793
+ # Add the current problem
794
+ messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"})
795
+
796
+ # Create the completion
797
+ completion = client.chat.completions.create(
798
+ model="microsoft/phi-4-reasoning-plus:free",
799
+ messages=messages,
800
+ extra_headers={
801
+ "HTTP-Referer": "https://advancedmathtutor.edu",
802
+ "X-Title": "Advanced Math Tutor",
803
+ }
804
+ )
805
+
806
+ markdown_solution = completion.choices[0].message.content
807
+
808
+ # Convert to HTML
809
+ html_solution = markdown_to_html(markdown_solution, problem_text)
810
+
811
+ # Save HTML file
812
+ html_file_path = save_html_to_file(html_solution, "openrouter_solution")
813
+
814
+ # Convert to PDF
815
+ pdf_file_path = html_to_pdf(html_solution, "openrouter_solution")
816
+
817
+ # Update history
818
+ if history is None:
819
+ history = []
820
+ history.append((problem_text, markdown_solution))
821
+
822
+ return html_solution, html_file_path, pdf_file_path, history
823
+
824
+ except Exception as e:
825
+ error_message = f"Error: {str(e)}"
826
+ return error_message, None, None, history
827
+
828
+ # Enhanced function to generate math solution using Together AI with HTML output
829
+ def generate_math_solution_together(api_key, problem_text, image_path=None, history=None):
830
+ if not api_key.strip():
831
+ return "Please enter your Together AI API key.", None, None, history
832
+
833
+ if not problem_text.strip() and image_path is None:
834
+ return "Please enter a math problem or upload an image of a math problem.", None, None, history
835
+
836
+ try:
837
+ client = Together(api_key=api_key)
838
+
839
+ # Create the base message structure
840
+ messages = [
841
+ {
842
+ "role": "system",
843
+ "content": """You are an expert math tutor who explains concepts clearly and thoroughly.
844
+ Analyze the given math problem and provide a detailed step-by-step solution.
845
+ For each step:
846
+ 1. Show the mathematical operation
847
+ 2. Explain why this step is necessary
848
+ 3. Connect it to relevant mathematical concepts
849
+
850
+ Format your response using markdown with clear section headers.
851
+ Begin with an "Initial Analysis" section, follow with numbered steps,
852
+ and conclude with a "Final Answer" section.
853
+
854
+ Use proper markdown formatting including:
855
+ - Headers (##, ###)
856
+ - **Bold text** for important points
857
+ - `Code blocks` for mathematical expressions
858
+ - Lists and numbered steps
859
+ - Tables if needed for comparisons or data"""
860
+ }
861
+ ]
862
+
863
+ # Add conversation history if it exists
864
+ if history:
865
+ for exchange in history:
866
+ messages.append({"role": "user", "content": exchange[0]})
867
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
868
+ messages.append({"role": "assistant", "content": exchange[1]})
869
+
870
+ # Prepare the user message content
871
+ user_message_content = []
872
+
873
+ # Add text content if provided
874
+ if problem_text.strip():
875
+ user_message_content.append({
876
+ "type": "text",
877
+ "text": f"Solve this math problem: {problem_text}"
878
+ })
879
+ else:
880
+ user_message_content.append({
881
+ "type": "text",
882
+ "text": "Solve this math problem from the image:"
883
+ })
884
+
885
+ # Add image if provided
886
+ if image_path:
887
+ # Convert image to base64
888
+ base64_image = image_to_base64(image_path)
889
+ if base64_image:
890
+ user_message_content.append({
891
+ "type": "image_url",
892
+ "image_url": {
893
+ "url": f"data:image/jpeg;base64,{base64_image}"
894
+ }
895
+ })
896
+
897
+ # Add the user message with content
898
+ messages.append({
899
+ "role": "user",
900
+ "content": user_message_content
901
+ })
902
+
903
+ # Create the completion
904
+ response = client.chat.completions.create(
905
+ model="meta-llama/Llama-Vision-Free",
906
+ messages=messages,
907
+ stream=False
908
+ )
909
+
910
+ markdown_solution = response.choices[0].message.content
911
+
912
+ # Convert to HTML
913
+ problem_display = problem_text if problem_text.strip() else "Image-based problem"
914
+ html_solution = markdown_to_html(markdown_solution, problem_display)
915
+
916
+ # Save HTML file
917
+ html_file_path = save_html_to_file(html_solution, "together_solution")
918
+
919
+ # Convert to PDF
920
+ pdf_file_path = html_to_pdf(html_solution, "together_solution")
921
+
922
+ # Update history - for simplicity, just store the text problem
923
+ if history is None:
924
+ history = []
925
+ history.append((problem_display, markdown_solution))
926
+
927
+ return html_solution, html_file_path, pdf_file_path, history
928
+
929
+ except Exception as e:
930
+ error_message = f"Error: {str(e)}"
931
+ return error_message, None, None, history
932
+
933
+ # Function to convert image to base64
934
+ def image_to_base64(image_path):
935
+ if image_path is None:
936
+ return None
937
+
938
+ try:
939
+ with open(image_path, "rb") as img_file:
940
+ return base64.b64encode(img_file.read()).decode("utf-8")
941
+ except Exception as e:
942
+ print(f"Error converting image to base64: {str(e)}")
943
+ return None
944
+
945
+ # Define the Gradio interface
946
+ def create_demo():
947
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
948
+ gr.Markdown("# 📚 Advanced Math Tutor")
949
+ gr.Markdown("""
950
+ This application provides step-by-step solutions to math problems using advanced AI models.
951
+ Solutions are generated in **HTML format** with download and print-to-PDF capabilities.
952
+ Choose between OpenRouter's Phi-4-reasoning-plus for text-based problems or Together AI's
953
+ Llama-Vision for problems with images.
954
+ """)
955
+
956
+ # Main tabs
957
+ with gr.Tabs():
958
+ # Text-based problem solver (OpenRouter)
959
+ with gr.TabItem("Text Problem Solver (OpenRouter)"):
960
+ with gr.Row():
961
+ with gr.Column(scale=1):
962
+ openrouter_api_key = gr.Textbox(
963
+ label="OpenRouter API Key",
964
+ placeholder="Enter your OpenRouter API key (starts with sk-or-)",
965
+ type="password"
966
+ )
967
+ text_problem_input = gr.Textbox(
968
+ label="Math Problem",
969
+ placeholder="Enter your math problem here...",
970
+ lines=5
971
+ )
972
+ example_problems = gr.Examples(
973
+ examples=[
974
+ ["Solve the quadratic equation: 3x² + 5x - 2 = 0"],
975
+ ["Find the derivative of f(x) = x³ln(x)"],
976
+ ["Calculate the area of a circle with radius 5 cm"],
977
+ ["Find all values of x that satisfy the equation: log₂(x-1) + log₂(x+3) = 5"]
978
+ ],
979
+ inputs=[text_problem_input],
980
+ label="Example Problems"
981
+ )
982
+ with gr.Row():
983
+ openrouter_submit_btn = gr.Button("Solve Problem", variant="primary")
984
+ openrouter_clear_btn = gr.Button("Clear")
985
+
986
+ with gr.Column(scale=2):
987
+ openrouter_solution_output = gr.HTML(label="Solution (HTML Format)")
988
+
989
+ with gr.Row():
990
+ openrouter_html_download = gr.File(
991
+ label="📄 Download HTML Solution",
992
+ visible=False
993
+ )
994
+ openrouter_pdf_download = gr.File(
995
+ label="📄 Download PDF Solution",
996
+ visible=False
997
+ )
998
+
999
+ # Store conversation history (invisible to user)
1000
+ openrouter_conversation_history = gr.State(value=None)
1001
+
1002
+ # Button actions
1003
+ def handle_openrouter_submit(api_key, problem_text, history):
1004
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter(
1005
+ api_key, problem_text, history
1006
+ )
1007
+
1008
+ # Return outputs including file updates
1009
+ return (
1010
+ html_solution,
1011
+ updated_history,
1012
+ gr.update(value=html_file, visible=html_file is not None),
1013
+ gr.update(value=pdf_file, visible=pdf_file is not None)
1014
+ )
1015
+
1016
+ openrouter_submit_btn.click(
1017
+ fn=handle_openrouter_submit,
1018
+ inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history],
1019
+ outputs=[
1020
+ openrouter_solution_output,
1021
+ openrouter_conversation_history,
1022
+ openrouter_html_download,
1023
+ openrouter_pdf_download
1024
+ ]
1025
+ )
1026
+
1027
+ def clear_openrouter():
1028
+ return (
1029
+ "",
1030
+ None,
1031
+ gr.update(value=None, visible=False),
1032
+ gr.update(value=None, visible=False)
1033
+ )
1034
+
1035
+ openrouter_clear_btn.click(
1036
+ fn=clear_openrouter,
1037
+ inputs=[],
1038
+ outputs=[
1039
+ openrouter_solution_output,
1040
+ openrouter_conversation_history,
1041
+ openrouter_html_download,
1042
+ openrouter_pdf_download
1043
+ ]
1044
+ )
1045
+
1046
+ # Image-based problem solver (Together AI)
1047
+ with gr.TabItem("Image Problem Solver (Together AI)"):
1048
+ with gr.Row():
1049
+ with gr.Column(scale=1):
1050
+ together_api_key = gr.Textbox(
1051
+ label="Together AI API Key",
1052
+ placeholder="Enter your Together AI API key",
1053
+ type="password"
1054
+ )
1055
+ together_problem_input = gr.Textbox(
1056
+ label="Problem Description (Optional)",
1057
+ placeholder="Enter additional context for the image problem...",
1058
+ lines=3
1059
+ )
1060
+ together_image_input = gr.Image(
1061
+ label="Upload Math Problem Image",
1062
+ type="filepath"
1063
+ )
1064
+ with gr.Row():
1065
+ together_submit_btn = gr.Button("Solve Problem", variant="primary")
1066
+ together_clear_btn = gr.Button("Clear")
1067
+
1068
+ with gr.Column(scale=2):
1069
+ together_solution_output = gr.HTML(label="Solution (HTML Format)")
1070
+
1071
+ with gr.Row():
1072
+ together_html_download = gr.File(
1073
+ label="📄 Download HTML Solution",
1074
+ visible=False
1075
+ )
1076
+ together_pdf_download = gr.File(
1077
+ label="📄 Download PDF Solution",
1078
+ visible=False
1079
+ )
1080
+
1081
+ # Store conversation history (invisible to user)
1082
+ together_conversation_history = gr.State(value=None)
1083
+
1084
+ # Button actions
1085
+ def handle_together_submit(api_key, problem_text, image_path, history):
1086
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_together(
1087
+ api_key, problem_text, image_path, history
1088
+ )
1089
+
1090
+ # Return outputs including file updates
1091
+ return (
1092
+ html_solution,
1093
+ updated_history,
1094
+ gr.update(value=html_file, visible=html_file is not None),
1095
+ gr.update(value=pdf_file, visible=pdf_file is not None)
1096
+ )
1097
+
1098
+ together_submit_btn.click(
1099
+ fn=handle_together_submit,
1100
+ inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history],
1101
+ outputs=[
1102
+ together_solution_output,
1103
+ together_conversation_history,
1104
+ together_html_download,
1105
+ together_pdf_download
1106
+ ]
1107
+ )
1108
+
1109
+ def clear_together():
1110
+ return (
1111
+ "",
1112
+ None,
1113
+ gr.update(value=None, visible=False),
1114
+ gr.update(value=None, visible=False)
1115
+ )
1116
+
1117
+ together_clear_btn.click(
1118
+ fn=clear_together,
1119
+ inputs=[],
1120
+ outputs=[
1121
+ together_solution_output,
1122
+ together_conversation_history,
1123
+ together_html_download,
1124
+ together_pdf_download
1125
+ ]
1126
+ )
1127
+
1128
+ # Help tab
1129
+ with gr.TabItem("Help"):
1130
+ gr.Markdown("""
1131
+ ## How to Use the Advanced Math Tutor
1132
+
1133
+ ### New Features 🎉
1134
+ - **HTML-formatted solutions**: All responses are now generated in beautiful HTML format
1135
+ - **Download HTML**: Download the complete solution as an HTML file
1136
+ - **Download PDF**: Convert and download solutions as PDF files
1137
+ - **Print functionality**: Use the "Print to PDF" button in the HTML output to print directly
1138
+
1139
+ ### Getting Started
1140
+
1141
+ #### For Text-Based Problems (OpenRouter)
1142
+ 1. You'll need an API key from OpenRouter
1143
+ 2. Sign up at [OpenRouter](https://openrouter.ai/) to get your API key
1144
+ 3. Enter your API key in the designated field in the "Text Problem Solver" tab
1145
+
1146
+ #### For Image-Based Problems (Together AI)
1147
+ 1. You'll need an API key from Together AI
1148
+ 2. Sign up at [Together AI](https://www.together.ai/) to get your API key
1149
+ 3. Enter your API key in the designated field in the "Image Problem Solver" tab
1150
+ 4. Upload an image of your math problem
1151
+ 5. Optionally add text to provide additional context
1152
+
1153
+ ### Solving Math Problems
1154
+ - For text problems: Type or paste your math problem in the input field
1155
+ - For image problems: Upload a clear image of the math problem
1156
+ - Click "Solve Problem" to get a detailed step-by-step solution in HTML format
1157
+ - Use the download buttons to save HTML or PDF versions
1158
+ - Click "Print to PDF" within the solution to print directly from your browser
1159
+
1160
+ ### HTML Output Features
1161
+ - **Professional styling**: Clean, readable format with proper typography
1162
+ - **Mathematical expressions**: Highlighted math expressions and code blocks
1163
+ - **Step-by-step sections**: Clearly organized solution steps
1164
+ - **Print-friendly**: Optimized for printing and PDF conversion
1165
+ - **Timestamps**: Each solution includes generation timestamp
1166
+
1167
+ ### Tips for Best Results
1168
+ - Be specific in your problem description
1169
+ - Include all necessary information
1170
+ - For complex equations, use clear notation
1171
+ - For algebraic expressions, use ^ for exponents (e.g., x^2 for x²)
1172
+ - Use parentheses to group terms clearly
1173
+ - For images, ensure the math problem is clearly visible and well-lit
1174
+
1175
+ ### Types of Problems You Can Solve
1176
+ - Algebra (equations, inequalities, systems of equations)
1177
+ - Calculus (derivatives, integrals, limits)
1178
+ - Trigonometry
1179
+ - Geometry
1180
+ - Statistics and Probability
1181
+ - Number Theory
1182
+ - And many more!
1183
+
1184
+ ### Required Dependencies
1185
+ To run this application, you'll need to install:
1186
+ ```bash
1187
+ pip install gradio openai together pillow markdown weasyprint
1188
+ ```
1189
+ """)
1190
+
1191
+ # Footer
1192
+ gr.Markdown("""
1193
+ ---
1194
+ ### About
1195
+ This enhanced application uses Microsoft's Phi-4-reasoning-plus model via OpenRouter for text-based problems
1196
+ and Llama-Vision-Free via Together AI for image-based problems.
1197
+
1198
+ **New Features:**
1199
+ - HTML-formatted responses with professional styling
1200
+ - Download solutions as HTML files
1201
+ - Convert and download solutions as PDF files
1202
+ - Print-to-PDF functionality
1203
+ - Enhanced formatting with mathematical expressions highlighting
1204
+
1205
+ Your API keys are required but not stored permanently.
1206
+ """)
1207
+
1208
+ return demo
1209
+
1210
+ # Launch the app
1211
+ if __name__ == "__main__":
1212
+ demo = create_demo()
1213
+ demo.launch()
1214
+ , r'<h2>\1</h2>', text, flags=re.MULTILINE)
1215
+ text = re.sub(r'^# (.*?)
1216
+
1217
+ # Function to save HTML to file
1218
+ def save_html_to_file(html_content, filename_prefix="math_solution"):
1219
+ """Save HTML content to a temporary file and return the file path"""
1220
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1221
+ filename = f"{filename_prefix}_{timestamp}.html"
1222
+
1223
+ # Create a temporary file
1224
+ temp_dir = tempfile.gettempdir()
1225
+ file_path = os.path.join(temp_dir, filename)
1226
+
1227
+ with open(file_path, 'w', encoding='utf-8') as f:
1228
+ f.write(html_content)
1229
+
1230
+ return file_path
1231
+
1232
+ # Function to convert HTML to PDF
1233
+ def html_to_pdf(html_content, filename_prefix="math_solution"):
1234
+ """Convert HTML content to PDF and return the file path"""
1235
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1236
+ filename = f"{filename_prefix}_{timestamp}.pdf"
1237
+
1238
+ # Create a temporary file for PDF
1239
+ temp_dir = tempfile.gettempdir()
1240
+ pdf_path = os.path.join(temp_dir, filename)
1241
+
1242
+ try:
1243
+ # Convert HTML to PDF using WeasyPrint
1244
+ weasyprint.HTML(string=html_content).write_pdf(pdf_path)
1245
+ return pdf_path
1246
+ except Exception as e:
1247
+ print(f"Error converting to PDF: {str(e)}")
1248
+ return None
1249
+
1250
+ # Enhanced function to generate math solution using OpenRouter with HTML output
1251
+ def generate_math_solution_openrouter(api_key, problem_text, history=None):
1252
+ if not api_key.strip():
1253
+ return "Please enter your OpenRouter API key.", None, None, history
1254
+
1255
+ if not problem_text.strip():
1256
+ return "Please enter a math problem.", None, None, history
1257
+
1258
+ try:
1259
+ client = OpenAI(
1260
+ base_url="https://openrouter.ai/api/v1",
1261
+ api_key=api_key,
1262
+ )
1263
+
1264
+ messages = [
1265
+ {"role": "system", "content":
1266
+ """You are an expert math tutor who explains concepts clearly and thoroughly.
1267
+ Analyze the given math problem and provide a detailed step-by-step solution.
1268
+ For each step:
1269
+ 1. Show the mathematical operation
1270
+ 2. Explain why this step is necessary
1271
+ 3. Connect it to relevant mathematical concepts
1272
+
1273
+ Format your response using markdown with clear section headers.
1274
+ Begin with an "Initial Analysis" section, follow with numbered steps,
1275
+ and conclude with a "Final Answer" section.
1276
+
1277
+ Use proper markdown formatting including:
1278
+ - Headers (##, ###)
1279
+ - **Bold text** for important points
1280
+ - `Code blocks` for mathematical expressions
1281
+ - Lists and numbered steps
1282
+ - Tables if needed for comparisons or data"""},
1283
+ ]
1284
+
1285
+ # Add conversation history if it exists
1286
+ if history:
1287
+ for exchange in history:
1288
+ messages.append({"role": "user", "content": exchange[0]})
1289
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
1290
+ messages.append({"role": "assistant", "content": exchange[1]})
1291
+
1292
+ # Add the current problem
1293
+ messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"})
1294
+
1295
+ # Create the completion
1296
+ completion = client.chat.completions.create(
1297
+ model="microsoft/phi-4-reasoning-plus:free",
1298
+ messages=messages,
1299
+ extra_headers={
1300
+ "HTTP-Referer": "https://advancedmathtutor.edu",
1301
+ "X-Title": "Advanced Math Tutor",
1302
+ }
1303
+ )
1304
+
1305
+ markdown_solution = completion.choices[0].message.content
1306
+
1307
+ # Convert to HTML
1308
+ html_solution = markdown_to_html(markdown_solution, problem_text)
1309
+
1310
+ # Save HTML file
1311
+ html_file_path = save_html_to_file(html_solution, "openrouter_solution")
1312
+
1313
+ # Convert to PDF
1314
+ pdf_file_path = html_to_pdf(html_solution, "openrouter_solution")
1315
+
1316
+ # Update history
1317
+ if history is None:
1318
+ history = []
1319
+ history.append((problem_text, markdown_solution))
1320
+
1321
+ return html_solution, html_file_path, pdf_file_path, history
1322
+
1323
+ except Exception as e:
1324
+ error_message = f"Error: {str(e)}"
1325
+ return error_message, None, None, history
1326
+
1327
+ # Enhanced function to generate math solution using Together AI with HTML output
1328
+ def generate_math_solution_together(api_key, problem_text, image_path=None, history=None):
1329
+ if not api_key.strip():
1330
+ return "Please enter your Together AI API key.", None, None, history
1331
+
1332
+ if not problem_text.strip() and image_path is None:
1333
+ return "Please enter a math problem or upload an image of a math problem.", None, None, history
1334
+
1335
+ try:
1336
+ client = Together(api_key=api_key)
1337
+
1338
+ # Create the base message structure
1339
+ messages = [
1340
+ {
1341
+ "role": "system",
1342
+ "content": """You are an expert math tutor who explains concepts clearly and thoroughly.
1343
+ Analyze the given math problem and provide a detailed step-by-step solution.
1344
+ For each step:
1345
+ 1. Show the mathematical operation
1346
+ 2. Explain why this step is necessary
1347
+ 3. Connect it to relevant mathematical concepts
1348
+
1349
+ Format your response using markdown with clear section headers.
1350
+ Begin with an "Initial Analysis" section, follow with numbered steps,
1351
+ and conclude with a "Final Answer" section.
1352
+
1353
+ Use proper markdown formatting including:
1354
+ - Headers (##, ###)
1355
+ - **Bold text** for important points
1356
+ - `Code blocks` for mathematical expressions
1357
+ - Lists and numbered steps
1358
+ - Tables if needed for comparisons or data"""
1359
+ }
1360
+ ]
1361
+
1362
+ # Add conversation history if it exists
1363
+ if history:
1364
+ for exchange in history:
1365
+ messages.append({"role": "user", "content": exchange[0]})
1366
+ if len(exchange) > 1 and exchange[1]: # Check if there's a response
1367
+ messages.append({"role": "assistant", "content": exchange[1]})
1368
+
1369
+ # Prepare the user message content
1370
+ user_message_content = []
1371
+
1372
+ # Add text content if provided
1373
+ if problem_text.strip():
1374
+ user_message_content.append({
1375
+ "type": "text",
1376
+ "text": f"Solve this math problem: {problem_text}"
1377
+ })
1378
+ else:
1379
+ user_message_content.append({
1380
+ "type": "text",
1381
+ "text": "Solve this math problem from the image:"
1382
+ })
1383
+
1384
+ # Add image if provided
1385
+ if image_path:
1386
+ # Convert image to base64
1387
+ base64_image = image_to_base64(image_path)
1388
+ if base64_image:
1389
+ user_message_content.append({
1390
+ "type": "image_url",
1391
+ "image_url": {
1392
+ "url": f"data:image/jpeg;base64,{base64_image}"
1393
+ }
1394
+ })
1395
+
1396
+ # Add the user message with content
1397
+ messages.append({
1398
+ "role": "user",
1399
+ "content": user_message_content
1400
+ })
1401
+
1402
+ # Create the completion
1403
+ response = client.chat.completions.create(
1404
+ model="meta-llama/Llama-Vision-Free",
1405
+ messages=messages,
1406
+ stream=False
1407
+ )
1408
+
1409
+ markdown_solution = response.choices[0].message.content
1410
+
1411
+ # Convert to HTML
1412
+ problem_display = problem_text if problem_text.strip() else "Image-based problem"
1413
+ html_solution = markdown_to_html(markdown_solution, problem_display)
1414
+
1415
+ # Save HTML file
1416
+ html_file_path = save_html_to_file(html_solution, "together_solution")
1417
+
1418
+ # Convert to PDF
1419
+ pdf_file_path = html_to_pdf(html_solution, "together_solution")
1420
+
1421
+ # Update history - for simplicity, just store the text problem
1422
+ if history is None:
1423
+ history = []
1424
+ history.append((problem_display, markdown_solution))
1425
+
1426
+ return html_solution, html_file_path, pdf_file_path, history
1427
+
1428
+ except Exception as e:
1429
+ error_message = f"Error: {str(e)}"
1430
+ return error_message, None, None, history
1431
+
1432
+ # Function to convert image to base64
1433
+ def image_to_base64(image_path):
1434
+ if image_path is None:
1435
+ return None
1436
+
1437
+ try:
1438
+ with open(image_path, "rb") as img_file:
1439
+ return base64.b64encode(img_file.read()).decode("utf-8")
1440
+ except Exception as e:
1441
+ print(f"Error converting image to base64: {str(e)}")
1442
+ return None
1443
+
1444
+ # Define the Gradio interface
1445
+ def create_demo():
1446
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo:
1447
+ gr.Markdown("# 📚 Advanced Math Tutor")
1448
+ gr.Markdown("""
1449
+ This application provides step-by-step solutions to math problems using advanced AI models.
1450
+ Solutions are generated in **HTML format** with download and print-to-PDF capabilities.
1451
+ Choose between OpenRouter's Phi-4-reasoning-plus for text-based problems or Together AI's
1452
+ Llama-Vision for problems with images.
1453
+ """)
1454
+
1455
+ # Main tabs
1456
+ with gr.Tabs():
1457
+ # Text-based problem solver (OpenRouter)
1458
+ with gr.TabItem("Text Problem Solver (OpenRouter)"):
1459
+ with gr.Row():
1460
+ with gr.Column(scale=1):
1461
+ openrouter_api_key = gr.Textbox(
1462
+ label="OpenRouter API Key",
1463
+ placeholder="Enter your OpenRouter API key (starts with sk-or-)",
1464
+ type="password"
1465
+ )
1466
+ text_problem_input = gr.Textbox(
1467
+ label="Math Problem",
1468
+ placeholder="Enter your math problem here...",
1469
+ lines=5
1470
+ )
1471
+ example_problems = gr.Examples(
1472
+ examples=[
1473
+ ["Solve the quadratic equation: 3x² + 5x - 2 = 0"],
1474
+ ["Find the derivative of f(x) = x³ln(x)"],
1475
+ ["Calculate the area of a circle with radius 5 cm"],
1476
+ ["Find all values of x that satisfy the equation: log₂(x-1) + log₂(x+3) = 5"]
1477
+ ],
1478
+ inputs=[text_problem_input],
1479
+ label="Example Problems"
1480
+ )
1481
+ with gr.Row():
1482
+ openrouter_submit_btn = gr.Button("Solve Problem", variant="primary")
1483
+ openrouter_clear_btn = gr.Button("Clear")
1484
+
1485
+ with gr.Column(scale=2):
1486
+ openrouter_solution_output = gr.HTML(label="Solution (HTML Format)")
1487
+
1488
+ with gr.Row():
1489
+ openrouter_html_download = gr.File(
1490
+ label="📄 Download HTML Solution",
1491
+ visible=False
1492
+ )
1493
+ openrouter_pdf_download = gr.File(
1494
+ label="📄 Download PDF Solution",
1495
+ visible=False
1496
+ )
1497
+
1498
+ # Store conversation history (invisible to user)
1499
+ openrouter_conversation_history = gr.State(value=None)
1500
+
1501
+ # Button actions
1502
+ def handle_openrouter_submit(api_key, problem_text, history):
1503
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter(
1504
+ api_key, problem_text, history
1505
+ )
1506
+
1507
+ # Return outputs including file updates
1508
+ return (
1509
+ html_solution,
1510
+ updated_history,
1511
+ gr.update(value=html_file, visible=html_file is not None),
1512
+ gr.update(value=pdf_file, visible=pdf_file is not None)
1513
+ )
1514
+
1515
+ openrouter_submit_btn.click(
1516
+ fn=handle_openrouter_submit,
1517
+ inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history],
1518
+ outputs=[
1519
+ openrouter_solution_output,
1520
+ openrouter_conversation_history,
1521
+ openrouter_html_download,
1522
+ openrouter_pdf_download
1523
+ ]
1524
+ )
1525
+
1526
+ def clear_openrouter():
1527
+ return (
1528
+ "",
1529
+ None,
1530
+ gr.update(value=None, visible=False),
1531
+ gr.update(value=None, visible=False)
1532
+ )
1533
+
1534
+ openrouter_clear_btn.click(
1535
+ fn=clear_openrouter,
1536
+ inputs=[],
1537
+ outputs=[
1538
+ openrouter_solution_output,
1539
+ openrouter_conversation_history,
1540
+ openrouter_html_download,
1541
+ openrouter_pdf_download
1542
+ ]
1543
+ )
1544
+
1545
+ # Image-based problem solver (Together AI)
1546
+ with gr.TabItem("Image Problem Solver (Together AI)"):
1547
+ with gr.Row():
1548
+ with gr.Column(scale=1):
1549
+ together_api_key = gr.Textbox(
1550
+ label="Together AI API Key",
1551
+ placeholder="Enter your Together AI API key",
1552
+ type="password"
1553
+ )
1554
+ together_problem_input = gr.Textbox(
1555
+ label="Problem Description (Optional)",
1556
+ placeholder="Enter additional context for the image problem...",
1557
+ lines=3
1558
+ )
1559
+ together_image_input = gr.Image(
1560
+ label="Upload Math Problem Image",
1561
+ type="filepath"
1562
+ )
1563
+ with gr.Row():
1564
+ together_submit_btn = gr.Button("Solve Problem", variant="primary")
1565
+ together_clear_btn = gr.Button("Clear")
1566
+
1567
+ with gr.Column(scale=2):
1568
+ together_solution_output = gr.HTML(label="Solution (HTML Format)")
1569
+
1570
+ with gr.Row():
1571
+ together_html_download = gr.File(
1572
+ label="📄 Download HTML Solution",
1573
+ visible=False
1574
+ )
1575
+ together_pdf_download = gr.File(
1576
+ label="📄 Download PDF Solution",
1577
+ visible=False
1578
+ )
1579
+
1580
+ # Store conversation history (invisible to user)
1581
+ together_conversation_history = gr.State(value=None)
1582
+
1583
+ # Button actions
1584
+ def handle_together_submit(api_key, problem_text, image_path, history):
1585
+ html_solution, html_file, pdf_file, updated_history = generate_math_solution_together(
1586
+ api_key, problem_text, image_path, history
1587
+ )
1588
+
1589
+ # Return outputs including file updates
1590
+ return (
1591
+ html_solution,
1592
+ updated_history,
1593
+ gr.update(value=html_file, visible=html_file is not None),
1594
+ gr.update(value=pdf_file, visible=pdf_file is not None)
1595
+ )
1596
+
1597
+ together_submit_btn.click(
1598
+ fn=handle_together_submit,
1599
+ inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history],
1600
+ outputs=[
1601
+ together_solution_output,
1602
+ together_conversation_history,
1603
+ together_html_download,
1604
+ together_pdf_download
1605
+ ]
1606
+ )
1607
+
1608
+ def clear_together():
1609
+ return (
1610
+ "",
1611
+ None,
1612
+ gr.update(value=None, visible=False),
1613
+ gr.update(value=None, visible=False)
1614
+ )
1615
+
1616
+ together_clear_btn.click(
1617
+ fn=clear_together,
1618
+ inputs=[],
1619
+ outputs=[
1620
+ together_solution_output,
1621
+ together_conversation_history,
1622
+ together_html_download,
1623
+ together_pdf_download
1624
+ ]
1625
+ )
1626
+
1627
+ # Help tab
1628
+ with gr.TabItem("Help"):
1629
+ gr.Markdown("""
1630
+ ## How to Use the Advanced Math Tutor
1631
+
1632
+ ### New Features 🎉
1633
+ - **HTML-formatted solutions**: All responses are now generated in beautiful HTML format
1634
+ - **Download HTML**: Download the complete solution as an HTML file
1635
+ - **Download PDF**: Convert and download solutions as PDF files
1636
+ - **Print functionality**: Use the "Print to PDF" button in the HTML output to print directly
1637
+
1638
+ ### Getting Started
1639
+
1640
+ #### For Text-Based Problems (OpenRouter)
1641
+ 1. You'll need an API key from OpenRouter
1642
+ 2. Sign up at [OpenRouter](https://openrouter.ai/) to get your API key
1643
+ 3. Enter your API key in the designated field in the "Text Problem Solver" tab
1644
+
1645
+ #### For Image-Based Problems (Together AI)
1646
+ 1. You'll need an API key from Together AI
1647
+ 2. Sign up at [Together AI](https://www.together.ai/) to get your API key
1648
+ 3. Enter your API key in the designated field in the "Image Problem Solver" tab
1649
+ 4. Upload an image of your math problem
1650
+ 5. Optionally add text to provide additional context
1651
+
1652
+ ### Solving Math Problems
1653
+ - For text problems: Type or paste your math problem in the input field
1654
+ - For image problems: Upload a clear image of the math problem
1655
+ - Click "Solve Problem" to get a detailed step-by-step solution in HTML format
1656
+ - Use the download buttons to save HTML or PDF versions
1657
+ - Click "Print to PDF" within the solution to print directly from your browser
1658
+
1659
+ ### HTML Output Features
1660
+ - **Professional styling**: Clean, readable format with proper typography
1661
+ - **Mathematical expressions**: Highlighted math expressions and code blocks
1662
+ - **Step-by-step sections**: Clearly organized solution steps
1663
+ - **Print-friendly**: Optimized for printing and PDF conversion
1664
+ - **Timestamps**: Each solution includes generation timestamp
1665
+
1666
+ ### Tips for Best Results
1667
+ - Be specific in your problem description
1668
+ - Include all necessary information
1669
+ - For complex equations, use clear notation
1670
+ - For algebraic expressions, use ^ for exponents (e.g., x^2 for x²)
1671
+ - Use parentheses to group terms clearly
1672
+ - For images, ensure the math problem is clearly visible and well-lit
1673
+
1674
+ ### Types of Problems You Can Solve
1675
+ - Algebra (equations, inequalities, systems of equations)
1676
+ - Calculus (derivatives, integrals, limits)
1677
+ - Trigonometry
1678
+ - Geometry
1679
+ - Statistics and Probability
1680
+ - Number Theory
1681
+ - And many more!
1682
+
1683
+ ### Required Dependencies
1684
+ To run this application, you'll need to install:
1685
+ ```bash
1686
+ pip install gradio openai together pillow markdown weasyprint
1687
+ ```
1688
+ """)
1689
+
1690
+ # Footer
1691
+ gr.Markdown("""
1692
+ ---
1693
+ ### About
1694
+ This enhanced application uses Microsoft's Phi-4-reasoning-plus model via OpenRouter for text-based problems
1695
+ and Llama-Vision-Free via Together AI for image-based problems.
1696
+
1697
+ **New Features:**
1698
+ - HTML-formatted responses with professional styling
1699
+ - Download solutions as HTML files
1700
+ - Convert and download solutions as PDF files
1701
+ - Print-to-PDF functionality
1702
+ - Enhanced formatting with mathematical expressions highlighting
1703
+
1704
+ Your API keys are required but not stored permanently.
1705
+ """)
1706
+
1707
+ return demo
1708
+
1709
+ # Launch the app
1710
+ if __name__ == "__main__":
1711
+ demo = create_demo()
1712
+ demo.launch()
1713
+ , r'<h1>\1</h1>', text, flags=re.MULTILINE)
1714
+
1715
+ # Convert bold text
1716
+ text = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', text)
1717
+ text = re.sub(r'__(.*?)__', r'<strong>\1</strong>', text)
1718
+
1719
+ # Convert italic text
1720
+ text = re.sub(r'\*(.*?)\*', r'<em>\1</em>', text)
1721
+ text = re.sub(r'_(.*?)_', r'<em>\1</em>', text)
1722
+
1723
+ # Convert inline code
1724
+ text = re.sub(r'`([^`]+?)`', r'<code>\1</code>', text)
1725
+
1726
+ # Convert code blocks
1727
+ text = re.sub(r'```(.*?)```', r'<pre><code>\1</code></pre>', text, flags=re.DOTALL)
1728
+
1729
+ # Convert unordered lists
1730
+ lines = text.split('\n')
1731
+ in_list = False
1732
+ result_lines = []
1733
+
1734
+ for line in lines:
1735
+ if re.match(r'^\s*[-*+]\s+', line):
1736
+ if not in_list:
1737
+ result_lines.append('<ul>')
1738
+ in_list = True
1739
+ item_text = re.sub(r'^\s*[-*+]\s+', '', line)
1740
+ result_lines.append(f'<li>{item_text}</li>')
1741
+ else:
1742
+ if in_list:
1743
+ result_lines.append('</ul>')
1744
+ in_list = False
1745
+ result_lines.append(line)
1746
+
1747
+ if in_list:
1748
+ result_lines.append('</ul>')
1749
+
1750
+ text = '\n'.join(result_lines)
1751
+
1752
+ # Convert ordered lists
1753
+ lines = text.split('\n')
1754
+ in_ordered_list = False
1755
+ result_lines = []
1756
+
1757
+ for line in lines:
1758
+ if re.match(r'^\s*\d+\.\s+', line):
1759
+ if not in_ordered_list:
1760
+ result_lines.append('<ol>')
1761
+ in_ordered_list = True
1762
+ item_text = re.sub(r'^\s*\d+\.\s+', '', line)
1763
+ result_lines.append(f'<li>{item_text}</li>')
1764
+ else:
1765
+ if in_ordered_list:
1766
+ result_lines.append('</ol>')
1767
+ in_ordered_list = False
1768
+ result_lines.append(line)
1769
+
1770
+ if in_ordered_list:
1771
+ result_lines.append('</ol>')
1772
+
1773
+ text = '\n'.join(result_lines)
1774
+
1775
+ # Convert line breaks to paragraphs
1776
+ paragraphs = text.split('\n\n')
1777
+ html_paragraphs = []
1778
+
1779
+ for para in paragraphs:
1780
+ para = para.strip()
1781
+ if para and not para.startswith('<'):
1782
+ # Don't wrap already formatted HTML elements
1783
+ if any(tag in para for tag in ['<h1>', '<h2>', '<h3>', '<ul>', '<ol>', '<pre>', '<code>']):
1784
+ html_paragraphs.append(para)
1785
+ else:
1786
+ html_paragraphs.append(f'<p>{para}</p>')
1787
+ elif para:
1788
+ html_paragraphs.append(para)
1789
+
1790
+ return '\n'.join(html_paragraphs)
1791
+
1792
+ # Function to convert HTML to PDF (simplified without WeasyPrint)
1793
+ def html_to_pdf_simple(html_content, filename_prefix="math_solution"):
1794
+ """Create a simple text-based PDF alternative"""
1795
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1796
+ filename = f"{filename_prefix}_{timestamp}.txt"
1797
+
1798
+ # Create a temporary file for text output
1799
+ temp_dir = tempfile.gettempdir()
1800
+ txt_path = os.path.join(temp_dir, filename)
1801
+
1802
+ try:
1803
+ # Strip HTML tags for text version
1804
+ import html
1805
+ text_content = re.sub(r'<[^>]+>', '', html_content)
1806
+ text_content = html.unescape(text_content)
1807
+
1808
+ with open(txt_path, 'w', encoding='utf-8') as f:
1809
+ f.write("ADVANCED MATH TUTOR - SOLUTION\n")
1810
+ f.write("=" * 50 + "\n\n")
1811
+ f.write(text_content)
1812
+ f.write(f"\n\n{'=' * 50}\n")
1813
+ f.write(f"Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
1814
+
1815
+ return txt_path
1816
+ except Exception as e:
1817
+ print(f"Error creating text file: {str(e)}")
1818
+ return None
1819
+
1820
  # Function to save HTML to file
1821
  def save_html_to_file(html_content, filename_prefix="math_solution"):
1822
  """Save HTML content to a temporary file and return the file path"""