|
|
import gradio as gr |
|
|
import os |
|
|
import re |
|
|
import time |
|
|
import base64 |
|
|
from openai import OpenAI |
|
|
from together import Together |
|
|
from PIL import Image |
|
|
import io |
|
|
import markdown |
|
|
from datetime import datetime |
|
|
import tempfile |
|
|
import weasyprint |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
def markdown_to_html(markdown_text, problem_text="", include_problem=True): |
|
|
"""Convert markdown to styled HTML with proper math rendering""" |
|
|
|
|
|
|
|
|
html_content = markdown.markdown(markdown_text, extensions=['tables', 'fenced_code']) |
|
|
|
|
|
|
|
|
|
|
|
html_content = re.sub(r'\\\[(.*?)\\\]', r'\\[\1\\]', html_content, flags=re.DOTALL) |
|
|
html_content = re.sub(r'\$\$(.*?)\$\$', r'\\[\1\\]', html_content, flags=re.DOTALL) |
|
|
|
|
|
|
|
|
html_content = re.sub(r'\\\((.*?)\\\)', r'\\(\1\\)', html_content) |
|
|
|
|
|
html_content = re.sub(r'(?<!\$)\$(?!\$)([^\$]+?)(?<!\$)\$(?!\$)', r'\\(\1\\)', html_content) |
|
|
|
|
|
|
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|
|
|
|
|
|
|
|
styled_html = f""" |
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Math Solution - Advanced Math Tutor</title> |
|
|
|
|
|
<!-- MathJax for rendering mathematical notation --> |
|
|
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> |
|
|
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> |
|
|
<script> |
|
|
window.MathJax = {{ |
|
|
tex: {{ |
|
|
inlineMath: [['\\\\(', '\\\\)']], |
|
|
displayMath: [['\\\\[', '\\\\]']], |
|
|
processEscapes: true, |
|
|
processEnvironments: true |
|
|
}}, |
|
|
options: {{ |
|
|
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'] |
|
|
}} |
|
|
}}; |
|
|
</script> |
|
|
|
|
|
<style> |
|
|
body {{ |
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
|
line-height: 1.8; |
|
|
color: #333; |
|
|
max-width: 900px; |
|
|
margin: 0 auto; |
|
|
padding: 20px; |
|
|
background-color: #f5f7fa; |
|
|
}} |
|
|
.container {{ |
|
|
background-color: white; |
|
|
padding: 50px; |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); |
|
|
}} |
|
|
.header {{ |
|
|
text-align: center; |
|
|
border-bottom: 4px solid #4CAF50; |
|
|
padding-bottom: 25px; |
|
|
margin-bottom: 40px; |
|
|
}} |
|
|
.header h1 {{ |
|
|
color: #2c3e50; |
|
|
margin: 0; |
|
|
font-size: 2.8em; |
|
|
font-weight: 700; |
|
|
}} |
|
|
.header .subtitle {{ |
|
|
color: #7f8c8d; |
|
|
font-style: italic; |
|
|
margin-top: 10px; |
|
|
font-size: 1.1em; |
|
|
}} |
|
|
.problem-section {{ |
|
|
background-color: #e8f5e9; |
|
|
padding: 25px; |
|
|
border-radius: 10px; |
|
|
margin-bottom: 35px; |
|
|
border-left: 6px solid #4CAF50; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05); |
|
|
}} |
|
|
.problem-section h2 {{ |
|
|
color: #2c3e50; |
|
|
margin-top: 0; |
|
|
font-size: 1.6em; |
|
|
}} |
|
|
.problem-section p {{ |
|
|
font-size: 1.1em; |
|
|
line-height: 1.6; |
|
|
}} |
|
|
.solution-content {{ |
|
|
background-color: #fafbfc; |
|
|
padding: 30px; |
|
|
border-radius: 10px; |
|
|
border-left: 6px solid #007bff; |
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05); |
|
|
}} |
|
|
h1, h2, h3, h4, h5, h6 {{ |
|
|
color: #2c3e50; |
|
|
margin-top: 30px; |
|
|
margin-bottom: 15px; |
|
|
font-weight: 600; |
|
|
}} |
|
|
h2 {{ |
|
|
border-bottom: 2px solid #e1e4e8; |
|
|
padding-bottom: 12px; |
|
|
font-size: 1.8em; |
|
|
}} |
|
|
h3 {{ |
|
|
font-size: 1.4em; |
|
|
color: #34495e; |
|
|
}} |
|
|
h4 {{ |
|
|
font-size: 1.2em; |
|
|
color: #34495e; |
|
|
}} |
|
|
code {{ |
|
|
background-color: #f1f3f5; |
|
|
padding: 3px 8px; |
|
|
border-radius: 4px; |
|
|
font-family: 'Courier New', Consolas, monospace; |
|
|
color: #e83e8c; |
|
|
font-size: 0.9em; |
|
|
}} |
|
|
pre {{ |
|
|
background-color: #f6f8fa; |
|
|
padding: 20px; |
|
|
border-radius: 6px; |
|
|
overflow-x: auto; |
|
|
border: 1px solid #d1d5da; |
|
|
line-height: 1.6; |
|
|
}} |
|
|
pre code {{ |
|
|
background-color: transparent; |
|
|
padding: 0; |
|
|
color: #24292e; |
|
|
}} |
|
|
.math-expression {{ |
|
|
background-color: #fff9e6; |
|
|
padding: 15px; |
|
|
border-radius: 6px; |
|
|
border: 1px solid #ffe066; |
|
|
margin: 15px 0; |
|
|
text-align: center; |
|
|
font-size: 1.15em; |
|
|
}} |
|
|
.step {{ |
|
|
margin: 25px 0; |
|
|
padding: 20px; |
|
|
background-color: #ffffff; |
|
|
border-radius: 8px; |
|
|
border: 1px solid #dee2e6; |
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05); |
|
|
}} |
|
|
.final-answer {{ |
|
|
background-color: #d4edda; |
|
|
border: 3px solid #28a745; |
|
|
padding: 25px; |
|
|
border-radius: 10px; |
|
|
margin-top: 35px; |
|
|
text-align: center; |
|
|
font-size: 1.3em; |
|
|
}} |
|
|
.final-answer h2, .final-answer h3 {{ |
|
|
color: #155724; |
|
|
margin-top: 0; |
|
|
}} |
|
|
.timestamp {{ |
|
|
text-align: right; |
|
|
color: #6c757d; |
|
|
font-size: 0.95em; |
|
|
margin-top: 40px; |
|
|
padding-top: 25px; |
|
|
border-top: 2px solid #e9ecef; |
|
|
}} |
|
|
ul, ol {{ |
|
|
padding-left: 30px; |
|
|
line-height: 1.8; |
|
|
}} |
|
|
li {{ |
|
|
margin: 10px 0; |
|
|
}} |
|
|
table {{ |
|
|
border-collapse: collapse; |
|
|
width: 100%; |
|
|
margin: 25px 0; |
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1); |
|
|
}} |
|
|
th, td {{ |
|
|
border: 1px solid #dee2e6; |
|
|
padding: 14px; |
|
|
text-align: left; |
|
|
}} |
|
|
th {{ |
|
|
background-color: #f8f9fa; |
|
|
font-weight: 600; |
|
|
color: #495057; |
|
|
}} |
|
|
tr:nth-child(even) {{ |
|
|
background-color: #f8f9fa; |
|
|
}} |
|
|
.print-button {{ |
|
|
background-color: #007bff; |
|
|
color: white; |
|
|
border: none; |
|
|
padding: 12px 24px; |
|
|
border-radius: 6px; |
|
|
cursor: pointer; |
|
|
font-size: 16px; |
|
|
margin: 10px 5px; |
|
|
display: inline-block; |
|
|
text-decoration: none; |
|
|
font-weight: 500; |
|
|
transition: background-color 0.2s; |
|
|
}} |
|
|
.print-button:hover {{ |
|
|
background-color: #0056b3; |
|
|
}} |
|
|
|
|
|
/* Math styling */ |
|
|
mjx-container {{ |
|
|
margin: 10px 0 !important; |
|
|
}} |
|
|
|
|
|
mjx-container[display="true"] {{ |
|
|
margin: 20px 0 !important; |
|
|
}} |
|
|
|
|
|
/* Boxed answers */ |
|
|
.boxed {{ |
|
|
border: 2px solid #4CAF50; |
|
|
padding: 10px 15px; |
|
|
border-radius: 5px; |
|
|
display: inline-block; |
|
|
background-color: #f0f9f0; |
|
|
}} |
|
|
|
|
|
@media print {{ |
|
|
body {{ |
|
|
background-color: white; |
|
|
}} |
|
|
.container {{ |
|
|
box-shadow: none; |
|
|
border: none; |
|
|
padding: 20px; |
|
|
}} |
|
|
.print-button {{ |
|
|
display: none; |
|
|
}} |
|
|
}} |
|
|
</style> |
|
|
<script> |
|
|
function printPage() {{ |
|
|
window.print(); |
|
|
}} |
|
|
|
|
|
// Wait for MathJax to finish rendering before showing content |
|
|
window.addEventListener('load', function() {{ |
|
|
if (window.MathJax) {{ |
|
|
MathJax.startup.promise.then(function () {{ |
|
|
console.log('MathJax rendering complete'); |
|
|
}}); |
|
|
}} |
|
|
}}); |
|
|
</script> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<div class="header"> |
|
|
<h1>π Advanced Math Tutor</h1> |
|
|
<div class="subtitle">Step-by-Step Mathematical Solution</div> |
|
|
</div> |
|
|
|
|
|
<button class="print-button" onclick="printPage()">π¨οΈ Print to PDF</button> |
|
|
|
|
|
{f''' |
|
|
<div class="problem-section"> |
|
|
<h2>π Problem Statement</h2> |
|
|
<p><strong>{problem_text}</strong></p> |
|
|
</div> |
|
|
''' if include_problem and problem_text.strip() else ''} |
|
|
|
|
|
<div class="solution-content"> |
|
|
<h2>π Solution</h2> |
|
|
{html_content} |
|
|
</div> |
|
|
|
|
|
<div class="timestamp"> |
|
|
Generated on: {timestamp} |
|
|
</div> |
|
|
</div> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
return styled_html |
|
|
|
|
|
|
|
|
def save_html_to_file(html_content, filename_prefix="math_solution"): |
|
|
"""Save HTML content to a temporary file and return the file path""" |
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
filename = f"{filename_prefix}_{timestamp}.html" |
|
|
|
|
|
|
|
|
temp_dir = tempfile.gettempdir() |
|
|
file_path = os.path.join(temp_dir, filename) |
|
|
|
|
|
with open(file_path, 'w', encoding='utf-8') as f: |
|
|
f.write(html_content) |
|
|
|
|
|
return file_path |
|
|
|
|
|
|
|
|
def html_to_pdf(html_content, filename_prefix="math_solution"): |
|
|
"""Convert HTML content to PDF and return the file path""" |
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
filename = f"{filename_prefix}_{timestamp}.pdf" |
|
|
|
|
|
|
|
|
temp_dir = tempfile.gettempdir() |
|
|
pdf_path = os.path.join(temp_dir, filename) |
|
|
|
|
|
try: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
weasyprint.HTML(string=html_content).write_pdf(pdf_path) |
|
|
return pdf_path |
|
|
except Exception as e: |
|
|
print(f"Error converting to PDF: {str(e)}") |
|
|
return None |
|
|
|
|
|
|
|
|
def generate_math_solution_openrouter(api_key, problem_text, history=None): |
|
|
if not api_key.strip(): |
|
|
return "Please enter your OpenRouter API key.", None, None, history |
|
|
|
|
|
if not problem_text.strip(): |
|
|
return "Please enter a math problem.", None, None, history |
|
|
|
|
|
try: |
|
|
client = OpenAI( |
|
|
base_url="https://openrouter.ai/api/v1", |
|
|
api_key=api_key, |
|
|
) |
|
|
|
|
|
messages = [ |
|
|
{"role": "system", "content": |
|
|
"""You are an expert math tutor who explains concepts clearly and thoroughly. |
|
|
Analyze the given math problem and provide a detailed step-by-step solution. |
|
|
For each step: |
|
|
1. Show the mathematical operation using proper LaTeX notation wrapped in \\[ \\] for display math or \\( \\) for inline math |
|
|
2. Explain why this step is necessary |
|
|
3. Connect it to relevant mathematical concepts |
|
|
|
|
|
IMPORTANT: Use proper LaTeX notation for all mathematical expressions: |
|
|
- Use \\[ ... \\] for display (centered) equations |
|
|
- Use \\( ... \\) for inline math |
|
|
- Use \\boxed{} for final answers |
|
|
- Use \\text{} for text within math mode |
|
|
- Example: \\[ A = \\pi r^{2} \\] |
|
|
- Example: The area \\( A \\) equals \\( \\pi r^{2} \\) |
|
|
|
|
|
Format your response using markdown with clear section headers. |
|
|
Begin with an "Initial Analysis" section, follow with numbered steps, |
|
|
and conclude with a "Final Answer" section. |
|
|
|
|
|
Use proper markdown formatting including: |
|
|
- Headers (##, ###) |
|
|
- **Bold text** for important points |
|
|
- Lists and numbered steps |
|
|
- Tables if needed for comparisons or data"""}, |
|
|
] |
|
|
|
|
|
|
|
|
if history: |
|
|
for exchange in history: |
|
|
messages.append({"role": "user", "content": exchange[0]}) |
|
|
if len(exchange) > 1 and exchange[1]: |
|
|
messages.append({"role": "assistant", "content": exchange[1]}) |
|
|
|
|
|
|
|
|
messages.append({"role": "user", "content": f"Solve this math problem step-by-step: {problem_text}"}) |
|
|
|
|
|
|
|
|
completion = client.chat.completions.create( |
|
|
model="meta-llama/llama-3.3-70b-instruct:free", |
|
|
messages=messages, |
|
|
extra_headers={ |
|
|
"HTTP-Referer": "https://advancedmathtutor.edu", |
|
|
"X-Title": "Advanced Math Tutor", |
|
|
} |
|
|
) |
|
|
|
|
|
markdown_solution = completion.choices[0].message.content |
|
|
|
|
|
|
|
|
html_solution = markdown_to_html(markdown_solution, problem_text) |
|
|
|
|
|
|
|
|
html_file_path = save_html_to_file(html_solution, "openrouter_solution") |
|
|
|
|
|
|
|
|
pdf_file_path = html_to_pdf(html_solution, "openrouter_solution") |
|
|
|
|
|
|
|
|
if history is None: |
|
|
history = [] |
|
|
history.append((problem_text, markdown_solution)) |
|
|
|
|
|
return html_solution, html_file_path, pdf_file_path, history |
|
|
|
|
|
except Exception as e: |
|
|
error_message = f"Error: {str(e)}" |
|
|
return error_message, None, None, history |
|
|
|
|
|
|
|
|
def generate_math_solution_together(api_key, problem_text, image_path=None, history=None): |
|
|
if not api_key.strip(): |
|
|
return "Please enter your Together AI API key.", None, None, history |
|
|
|
|
|
if not problem_text.strip() and image_path is None: |
|
|
return "Please enter a math problem or upload an image of a math problem.", None, None, history |
|
|
|
|
|
try: |
|
|
client = Together(api_key=api_key) |
|
|
|
|
|
|
|
|
messages = [ |
|
|
{ |
|
|
"role": "system", |
|
|
"content": """You are an expert math tutor who explains concepts clearly and thoroughly. |
|
|
Analyze the given math problem and provide a detailed step-by-step solution. |
|
|
For each step: |
|
|
1. Show the mathematical operation using proper LaTeX notation wrapped in \\[ \\] for display math or \\( \\) for inline math |
|
|
2. Explain why this step is necessary |
|
|
3. Connect it to relevant mathematical concepts |
|
|
|
|
|
IMPORTANT: Use proper LaTeX notation for all mathematical expressions: |
|
|
- Use \\[ ... \\] for display (centered) equations |
|
|
- Use \\( ... \\) for inline math |
|
|
- Use \\boxed{} for final answers |
|
|
- Use \\text{} for text within math mode |
|
|
- Example: \\[ A = \\pi r^{2} \\] |
|
|
- Example: The area \\( A \\) equals \\( \\pi r^{2} \\) |
|
|
|
|
|
Format your response using markdown with clear section headers. |
|
|
Begin with an "Initial Analysis" section, follow with numbered steps, |
|
|
and conclude with a "Final Answer" section. |
|
|
|
|
|
Use proper markdown formatting including: |
|
|
- Headers (##, ###) |
|
|
- **Bold text** for important points |
|
|
- Lists and numbered steps |
|
|
- Tables if needed for comparisons or data""" |
|
|
} |
|
|
] |
|
|
|
|
|
|
|
|
if history: |
|
|
for exchange in history: |
|
|
messages.append({"role": "user", "content": exchange[0]}) |
|
|
if len(exchange) > 1 and exchange[1]: |
|
|
messages.append({"role": "assistant", "content": exchange[1]}) |
|
|
|
|
|
|
|
|
user_message_content = [] |
|
|
|
|
|
|
|
|
if problem_text.strip(): |
|
|
user_message_content.append({ |
|
|
"type": "text", |
|
|
"text": f"Solve this math problem: {problem_text}" |
|
|
}) |
|
|
else: |
|
|
user_message_content.append({ |
|
|
"type": "text", |
|
|
"text": "Solve this math problem from the image:" |
|
|
}) |
|
|
|
|
|
|
|
|
if image_path: |
|
|
|
|
|
base64_image = image_to_base64(image_path) |
|
|
if base64_image: |
|
|
user_message_content.append({ |
|
|
"type": "image_url", |
|
|
"image_url": { |
|
|
"url": f"data:image/jpeg;base64,{base64_image}" |
|
|
} |
|
|
}) |
|
|
|
|
|
|
|
|
messages.append({ |
|
|
"role": "user", |
|
|
"content": user_message_content |
|
|
}) |
|
|
|
|
|
|
|
|
response = client.chat.completions.create( |
|
|
model="ServiceNow-AI/Apriel-1.5-15b-Thinker", |
|
|
messages=messages, |
|
|
stream=False |
|
|
) |
|
|
|
|
|
markdown_solution = response.choices[0].message.content |
|
|
|
|
|
|
|
|
problem_display = problem_text if problem_text.strip() else "Image-based problem" |
|
|
html_solution = markdown_to_html(markdown_solution, problem_display) |
|
|
|
|
|
|
|
|
html_file_path = save_html_to_file(html_solution, "together_solution") |
|
|
|
|
|
|
|
|
pdf_file_path = html_to_pdf(html_solution, "together_solution") |
|
|
|
|
|
|
|
|
if history is None: |
|
|
history = [] |
|
|
history.append((problem_display, markdown_solution)) |
|
|
|
|
|
return html_solution, html_file_path, pdf_file_path, history |
|
|
|
|
|
except Exception as e: |
|
|
error_message = f"Error: {str(e)}" |
|
|
return error_message, None, None, history |
|
|
|
|
|
|
|
|
def image_to_base64(image_path): |
|
|
if image_path is None: |
|
|
return None |
|
|
|
|
|
try: |
|
|
with open(image_path, "rb") as img_file: |
|
|
return base64.b64encode(img_file.read()).decode("utf-8") |
|
|
except Exception as e: |
|
|
print(f"Error converting image to base64: {str(e)}") |
|
|
return None |
|
|
|
|
|
|
|
|
def create_demo(): |
|
|
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo: |
|
|
gr.Markdown("# π Advanced Math Tutor") |
|
|
gr.Markdown(""" |
|
|
This application provides step-by-step solutions to math problems using advanced AI models. |
|
|
Solutions are generated in **HTML format** with proper mathematical notation rendering using MathJax. |
|
|
Choose between OpenRouter or Together AI for problem solving. |
|
|
""") |
|
|
|
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.TabItem("Text Problem Solver (OpenRouter)"): |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
openrouter_api_key = gr.Textbox( |
|
|
label="OpenRouter API Key", |
|
|
placeholder="Enter your OpenRouter API key (starts with sk-or-)", |
|
|
type="password" |
|
|
) |
|
|
text_problem_input = gr.Textbox( |
|
|
label="Math Problem", |
|
|
placeholder="Enter your math problem here...", |
|
|
lines=5 |
|
|
) |
|
|
example_problems = gr.Examples( |
|
|
examples=[ |
|
|
["Solve the quadratic equation: 3xΒ² + 5x - 2 = 0"], |
|
|
["Find the derivative of f(x) = xΒ³ln(x)"], |
|
|
["Calculate the area of a circle with radius 5 cm"], |
|
|
["Find all values of x that satisfy: logβ(x-1) + logβ(x+3) = 5"] |
|
|
], |
|
|
inputs=[text_problem_input], |
|
|
label="Example Problems" |
|
|
) |
|
|
with gr.Row(): |
|
|
openrouter_submit_btn = gr.Button("Solve Problem", variant="primary") |
|
|
openrouter_clear_btn = gr.Button("Clear") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
openrouter_solution_output = gr.HTML(label="Solution (HTML with LaTeX)") |
|
|
|
|
|
with gr.Row(): |
|
|
openrouter_html_download = gr.File( |
|
|
label="π Download HTML Solution", |
|
|
visible=False |
|
|
) |
|
|
openrouter_pdf_download = gr.File( |
|
|
label="π Download PDF Solution", |
|
|
visible=False |
|
|
) |
|
|
|
|
|
openrouter_conversation_history = gr.State(value=None) |
|
|
|
|
|
def handle_openrouter_submit(api_key, problem_text, history): |
|
|
html_solution, html_file, pdf_file, updated_history = generate_math_solution_openrouter( |
|
|
api_key, problem_text, history |
|
|
) |
|
|
|
|
|
return ( |
|
|
html_solution, |
|
|
updated_history, |
|
|
gr.update(value=html_file, visible=html_file is not None), |
|
|
gr.update(value=pdf_file, visible=pdf_file is not None) |
|
|
) |
|
|
|
|
|
openrouter_submit_btn.click( |
|
|
fn=handle_openrouter_submit, |
|
|
inputs=[openrouter_api_key, text_problem_input, openrouter_conversation_history], |
|
|
outputs=[ |
|
|
openrouter_solution_output, |
|
|
openrouter_conversation_history, |
|
|
openrouter_html_download, |
|
|
openrouter_pdf_download |
|
|
] |
|
|
) |
|
|
|
|
|
def clear_openrouter(): |
|
|
return ( |
|
|
"", |
|
|
None, |
|
|
gr.update(value=None, visible=False), |
|
|
gr.update(value=None, visible=False) |
|
|
) |
|
|
|
|
|
openrouter_clear_btn.click( |
|
|
fn=clear_openrouter, |
|
|
inputs=[], |
|
|
outputs=[ |
|
|
openrouter_solution_output, |
|
|
openrouter_conversation_history, |
|
|
openrouter_html_download, |
|
|
openrouter_pdf_download |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.TabItem("Image Problem Solver (Together AI)"): |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
together_api_key = gr.Textbox( |
|
|
label="Together AI API Key", |
|
|
placeholder="Enter your Together AI API key", |
|
|
type="password" |
|
|
) |
|
|
together_problem_input = gr.Textbox( |
|
|
label="Problem Description (Optional)", |
|
|
placeholder="Enter additional context...", |
|
|
lines=3 |
|
|
) |
|
|
together_image_input = gr.Image( |
|
|
label="Upload Math Problem Image", |
|
|
type="filepath" |
|
|
) |
|
|
with gr.Row(): |
|
|
together_submit_btn = gr.Button("Solve Problem", variant="primary") |
|
|
together_clear_btn = gr.Button("Clear") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
together_solution_output = gr.HTML(label="Solution (HTML with LaTeX)") |
|
|
|
|
|
with gr.Row(): |
|
|
together_html_download = gr.File( |
|
|
label="π Download HTML Solution", |
|
|
visible=False |
|
|
) |
|
|
together_pdf_download = gr.File( |
|
|
label="π Download PDF Solution", |
|
|
visible=False |
|
|
) |
|
|
|
|
|
together_conversation_history = gr.State(value=None) |
|
|
|
|
|
def handle_together_submit(api_key, problem_text, image_path, history): |
|
|
html_solution, html_file, pdf_file, updated_history = generate_math_solution_together( |
|
|
api_key, problem_text, image_path, history |
|
|
) |
|
|
|
|
|
return ( |
|
|
html_solution, |
|
|
updated_history, |
|
|
gr.update(value=html_file, visible=html_file is not None), |
|
|
gr.update(value=pdf_file, visible=pdf_file is not None) |
|
|
) |
|
|
|
|
|
together_submit_btn.click( |
|
|
fn=handle_together_submit, |
|
|
inputs=[together_api_key, together_problem_input, together_image_input, together_conversation_history], |
|
|
outputs=[ |
|
|
together_solution_output, |
|
|
together_conversation_history, |
|
|
together_html_download, |
|
|
together_pdf_download |
|
|
] |
|
|
) |
|
|
|
|
|
def clear_together(): |
|
|
return ( |
|
|
"", |
|
|
None, |
|
|
gr.update(value=None, visible=False), |
|
|
gr.update(value=None, visible=False) |
|
|
) |
|
|
|
|
|
together_clear_btn.click( |
|
|
fn=clear_together, |
|
|
inputs=[], |
|
|
outputs=[ |
|
|
together_solution_output, |
|
|
together_conversation_history, |
|
|
together_html_download, |
|
|
together_pdf_download |
|
|
] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.TabItem("Help"): |
|
|
gr.Markdown(""" |
|
|
## How to Use the Advanced Math Tutor |
|
|
|
|
|
### Key Features π |
|
|
- **Professional math rendering**: All equations rendered using MathJax/LaTeX |
|
|
- **HTML-formatted solutions**: Beautiful, readable format |
|
|
- **Download HTML**: Save solutions as HTML files with proper math notation |
|
|
- **Download PDF**: Convert solutions to PDF (Note: PDF math rendering may vary) |
|
|
- **Print functionality**: Print directly from your browser |
|
|
|
|
|
### Getting Started |
|
|
|
|
|
#### For Text Problems (OpenRouter) |
|
|
1. Get API key from [OpenRouter](https://openrouter.ai/) |
|
|
2. Enter your problem in proper notation |
|
|
3. Get beautifully formatted solutions with proper mathematical notation |
|
|
|
|
|
#### For Image Problems (Together AI) |
|
|
1. Get API key from [Together AI](https://www.together.ai/) |
|
|
2. Upload a clear image of the math problem |
|
|
3. Optionally add context |
|
|
|
|
|
### Math Notation Tips |
|
|
- Superscripts: xΒ² or x^2 |
|
|
- Subscripts: xβ or x_1 |
|
|
- Fractions: Use / or describe as "numerator over denominator" |
|
|
- Greek letters: pi, alpha, beta, theta, etc. |
|
|
- Square roots: β or sqrt() |
|
|
|
|
|
### Types of Problems |
|
|
- Algebra, Calculus, Trigonometry |
|
|
- Geometry, Statistics |
|
|
- Number Theory, Linear Algebra |
|
|
- And much more! |
|
|
|
|
|
### Note on PDF Export |
|
|
The HTML version will always have perfect math rendering. The PDF export uses |
|
|
WeasyPrint which may have limitations with complex math notation. For best results, |
|
|
use the "Print to PDF" button in the HTML view instead. |
|
|
""") |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### About |
|
|
Enhanced with **MathJax** for professional mathematical notation rendering. |
|
|
Your API keys are never stored permanently. |
|
|
""") |
|
|
|
|
|
return demo |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo = create_demo() |
|
|
demo.launch() |