Spaces:
Running
Running
import os | |
import gradio as gr | |
from anthropic import Anthropic | |
from datetime import datetime, timedelta | |
from collections import deque | |
import random | |
import tempfile | |
import subprocess | |
from pathlib import Path | |
# Initialize Anthropic client | |
anthropic = Anthropic( | |
api_key=os.environ.get('ANTHROPIC_API_KEY') | |
) | |
# Request tracking | |
MAX_REQUESTS_PER_DAY = 25 | |
request_history = deque(maxlen=1000) | |
def check_api_key(): | |
"""Verify API key is configured""" | |
if not os.environ.get('ANTHROPIC_API_KEY'): | |
raise ValueError("Anthropic API key not found. Please configure it in HuggingFace Spaces settings.") | |
def check_rate_limit(): | |
"""Check if we're within rate limits""" | |
now = datetime.now() | |
while request_history and (now - request_history[0]) > timedelta(days=1): | |
request_history.popleft() | |
return len(request_history) < MAX_REQUESTS_PER_DAY | |
def clean_latex(text): | |
"""Simple LaTeX cleaning""" | |
text = text.replace('\n', '\n\n') | |
return text | |
def create_latex_document(content, questions_only=False): | |
"""Create a complete LaTeX document""" | |
latex_header = r"""\documentclass{article} | |
\usepackage{amsmath,amssymb} | |
\usepackage[margin=1in]{geometry} | |
\begin{document} | |
""" | |
latex_footer = r"\end{document}" | |
if questions_only: | |
# Extract only the questions (everything before "Solution:") | |
questions = [] | |
for question in content.split('\n'): | |
if 'Solution:' in question: | |
continue | |
questions.append(question) | |
content = '\n'.join(questions) | |
return f"{latex_header}\n{content}\n{latex_footer}" | |
def latex_to_pdf(latex_content): | |
"""Convert LaTeX content to PDF""" | |
with tempfile.TemporaryDirectory() as tmpdir: | |
tex_path = Path(tmpdir) / "output.tex" | |
with open(tex_path, "w", encoding="utf-8") as f: | |
f.write(latex_content) | |
# Run pdflatex twice to resolve references | |
subprocess.run(["pdflatex", "-interaction=nonstopmode", str(tex_path)], | |
cwd=tmpdir, capture_output=True) | |
subprocess.run(["pdflatex", "-interaction=nonstopmode", str(tex_path)], | |
cwd=tmpdir, capture_output=True) | |
pdf_path = Path(tmpdir) / "output.pdf" | |
if pdf_path.exists(): | |
return pdf_path.read_bytes() | |
return None | |
def generate_test_with_format(subject, output_format): | |
"""Generate a math test with specified output format""" | |
try: | |
check_api_key() | |
if not check_rate_limit(): | |
return "Daily request limit reached. Please try again tomorrow." | |
request_history.append(datetime.now()) | |
topics = { | |
"Single Variable Calculus": ["limits", "derivatives", "integrals", "series", "applications"], | |
"Multivariable Calculus": ["partial derivatives", "multiple integrals", "vector fields", "optimization"], | |
"Linear Algebra": ["matrices", "vector spaces", "eigenvalues", "linear transformations"], | |
# ... add topics for other subjects | |
} | |
selected_topics = random.sample(topics.get(subject, ["general"]), min(3, len(topics.get(subject, ["general"])))) | |
difficulty_distribution = random.choice([ | |
"one easy, one medium, one challenging", | |
"two medium, one very challenging", | |
"one medium, two challenging" | |
]) | |
system_prompt = f"""You will write math exam questions. Follow these requirements EXACTLY: | |
1. Write exactly 3 university-level questions focusing on these specific topics: {', '.join(selected_topics)} | |
2. Make the questions {difficulty_distribution} | |
3. ALL questions must be computational or proof-based problems. NO conceptual or explanation questions. | |
4. Each question must require mathematical work, calculations, or formal proofs | |
5. For LaTeX math formatting: | |
- Use $ for simple inline math | |
- For equations and solution steps, use $$ on separate lines | |
- For multi-step solutions, put each step on its own line in $$ $$ | |
- DO NOT use \\begin{{aligned}} or any other environments | |
6. Number each question as 1), 2), 3) | |
7. Include detailed solutions after each question | |
8. Keep formatting simple and clear | |
Important: Every question must require mathematical computation or proof. Do not ask for explanations, descriptions, or concepts.""" | |
temperature = 0.7 | |
message = anthropic.messages.create( | |
model="claude-3-opus-20240229", | |
max_tokens=1500, | |
temperature=temperature, | |
messages=[{ | |
"role": "user", | |
"content": f"{system_prompt}\n\nWrite an exam for {subject}." | |
}] | |
) | |
if hasattr(message, 'content') and len(message.content) > 0: | |
response_text = message.content[0].text | |
return clean_latex(response_text) | |
else: | |
return "Error: No content in response" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def create_download_files(test_content): | |
"""Create downloadable files in different formats""" | |
# Full test (questions and solutions) | |
full_latex = create_latex_document(test_content, questions_only=False) | |
full_pdf = latex_to_pdf(full_latex) | |
# Questions only | |
questions_latex = create_latex_document(test_content, questions_only=True) | |
questions_pdf = latex_to_pdf(questions_latex) | |
return { | |
"full_latex": full_latex, | |
"full_pdf": full_pdf, | |
"questions_latex": questions_latex, | |
"questions_pdf": questions_pdf | |
} | |
subjects = [ | |
"Single Variable Calculus", | |
"Multivariable Calculus", | |
"Linear Algebra", | |
"Differential Equations", | |
"Real Analysis", | |
"Complex Analysis", | |
"Abstract Algebra", | |
"Probability Theory", | |
"Numerical Analysis", | |
"Topology" | |
] | |
def download_handler(file_content, filename): | |
"""Handle file downloads""" | |
return file_content | |
# Create Gradio interface | |
with gr.Blocks() as interface: | |
gr.Markdown("# Advanced Mathematics Test Generator") | |
gr.Markdown("""Generates unique university-level mathematics exam questions with solutions using Claude 3 Opus. | |
Each test features different topics and difficulty levels. Limited to 25 requests per day.""") | |
with gr.Row(): | |
subject_dropdown = gr.Dropdown( | |
choices=subjects, | |
label="Select Mathematics Subject", | |
info="Choose a subject for the exam questions" | |
) | |
generate_btn = gr.Button("Generate Test") | |
output_text = gr.Markdown( | |
label="Generated Test", | |
latex_delimiters=[ | |
{"left": "$$", "right": "$$", "display": True}, | |
{"left": "$", "right": "$", "display": False} | |
] | |
) | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### Download Questions Only") | |
questions_latex_btn = gr.Button("Download Questions (LaTeX)") | |
questions_pdf_btn = gr.Button("Download Questions (PDF)") | |
with gr.Column(): | |
gr.Markdown("### Download Full Test with Solutions") | |
full_latex_btn = gr.Button("Download Full Test (LaTeX)") | |
full_pdf_btn = gr.Button("Download Full Test (PDF)") | |
# Hidden components for downloads | |
questions_latex_output = gr.File(label="Questions LaTeX", visible=False) | |
questions_pdf_output = gr.File(label="Questions PDF", visible=False) | |
full_latex_output = gr.File(label="Full LaTeX", visible=False) | |
full_pdf_output = gr.File(label="Full PDF", visible=False) | |
# Event handlers | |
def generate_and_prepare_downloads(subject): | |
test_content = generate_test_with_format(subject, "latex") | |
files = create_download_files(test_content) | |
return ( | |
test_content, | |
(files["questions_latex"], "questions.tex"), | |
(files["questions_pdf"], "questions.pdf"), | |
(files["full_latex"], "full_test.tex"), | |
(files["full_pdf"], "full_test.pdf") | |
) | |
generate_btn.click( | |
generate_and_prepare_downloads, | |
inputs=[subject_dropdown], | |
outputs=[output_text, questions_latex_output, questions_pdf_output, | |
full_latex_output, full_pdf_output] | |
) | |
# Download button clicks | |
questions_latex_btn.click(lambda: questions_latex_output.value) | |
questions_pdf_btn.click(lambda: questions_pdf_output.value) | |
full_latex_btn.click(lambda: full_latex_output.value) | |
full_pdf_btn.click(lambda: full_pdf_output.value) | |
if __name__ == "__main__": | |
interface.launch() |