Spaces:
Running
Running
import os | |
import gradio as gr | |
from anthropic import Anthropic | |
from datetime import datetime, timedelta | |
from collections import deque | |
import random | |
# 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} | |
\title{Mathematics Test} | |
\maketitle | |
""" | |
latex_footer = r"\end{document}" | |
if questions_only: | |
# Extract only the questions (everything before "Solution:") | |
processed_content = [] | |
current_question = [] | |
for line in content.split('\n'): | |
if 'Solution:' in line: | |
processed_content.append('\n'.join(current_question)) | |
current_question = [] | |
continue | |
if any(line.startswith(f"{i})") for i in range(1, 4)): | |
if current_question: | |
processed_content.append('\n'.join(current_question)) | |
current_question = [line] | |
elif current_question: | |
current_question.append(line) | |
if current_question: | |
processed_content.append('\n'.join(current_question)) | |
content = '\n\n'.join(processed_content) | |
return f"{latex_header}\n{content}\n{latex_footer}" | |
def generate_test(subject): | |
"""Generate a math test""" | |
try: | |
check_api_key() | |
if not check_rate_limit(): | |
return "Daily request limit reached. Please try again tomorrow.", None, None | |
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.""" | |
message = anthropic.messages.create( | |
model="claude-3-opus-20240229", | |
max_tokens=1500, | |
temperature=0.7, | |
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 | |
cleaned_text = clean_latex(response_text) | |
# Create both versions of the LaTeX document | |
full_latex = create_latex_document(cleaned_text, questions_only=False) | |
questions_latex = create_latex_document(cleaned_text, questions_only=True) | |
return cleaned_text, questions_latex, full_latex | |
else: | |
return "Error: No content in response", None, None | |
except Exception as e: | |
return f"Error: {str(e)}", None, None | |
subjects = [ | |
"Single Variable Calculus", | |
"Multivariable Calculus", | |
"Linear Algebra", | |
"Differential Equations", | |
"Real Analysis", | |
"Complex Analysis", | |
"Abstract Algebra", | |
"Probability Theory", | |
"Numerical Analysis", | |
"Topology" | |
] | |
# 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 LaTeX Files") | |
questions_file = gr.File(label="Questions Only (LaTeX)") | |
full_file = gr.File(label="Full Test with Solutions (LaTeX)") | |
def generate_and_prepare_files(subject): | |
preview, questions_latex, full_latex = generate_test(subject) | |
# Create temporary files for download | |
questions_file = {"content": questions_latex.encode(), "name": "questions.tex"} | |
full_file = {"content": full_latex.encode(), "name": "full_test.tex"} | |
return preview, questions_file, full_file | |
generate_btn.click( | |
generate_and_prepare_files, | |
inputs=[subject_dropdown], | |
outputs=[output_text, questions_file, full_file] | |
) | |
if __name__ == "__main__": | |
interface.launch() |