shukdevdatta123 commited on
Commit
0754a59
·
verified ·
1 Parent(s): 45b0793

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +6 -1596
app.py CHANGED
@@ -7,16 +7,18 @@ from openai import OpenAI
7
  from together import Together
8
  from PIL import Image
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,1601 +224,9 @@ def markdown_to_html(markdown_text, problem_text="", include_problem=True):
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"""
 
7
  from together import Together
8
  from PIL import Image
9
  import io
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
 
20
+ # Convert markdown to HTML
21
+ html_content = markdown.markdown(markdown_text, extensions=['tables', 'fenced_code'])
22
 
23
  # Get current timestamp
24
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
224
  </body>
225
  </html>
226
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  return styled_html
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  # Function to save HTML to file
231
  def save_html_to_file(html_content, filename_prefix="math_solution"):
232
  """Save HTML content to a temporary file and return the file path"""