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 logging | |
import tempfile | |
from pathlib import Path | |
# Set up logging | |
logging.basicConfig( | |
level=logging.DEBUG, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
) | |
logger = logging.getLogger(__name__) | |
# 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 create_latex_document(content, questions_only=False): | |
"""Create a complete LaTeX document""" | |
try: | |
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: | |
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) | |
full_document = f"{latex_header}\n{content}\n{latex_footer}" | |
logger.debug(f"Created {'questions-only' if questions_only else 'full'} LaTeX document") | |
return full_document | |
except Exception as e: | |
logger.error(f"Error creating LaTeX document: {str(e)}") | |
raise | |
def save_to_temp_file(content, filename): | |
"""Save content to a temporary file and return the path""" | |
try: | |
# Create a temporary directory that persists | |
temp_dir = Path(tempfile.gettempdir()) / "math_test_files" | |
temp_dir.mkdir(exist_ok=True) | |
# Create the file path | |
file_path = temp_dir / filename | |
# Write the content | |
file_path.write_text(content, encoding='utf-8') | |
logger.debug(f"Saved content to temporary file: {file_path}") | |
return str(file_path) | |
except Exception as e: | |
logger.error(f"Error saving temporary file: {str(e)}") | |
raise | |
def generate_test(subject): | |
"""Generate a math test""" | |
try: | |
if not os.environ.get('ANTHROPIC_API_KEY'): | |
logger.error("Anthropic API key not found") | |
return "Error: Anthropic API key not configured", None, None | |
logger.debug(f"Generating test for subject: {subject}") | |
# Check rate limit | |
now = datetime.now() | |
while request_history and (now - request_history[0]) > timedelta(days=1): | |
request_history.popleft() | |
if len(request_history) >= MAX_REQUESTS_PER_DAY: | |
return "Daily request limit reached. Please try again tomorrow.", None, None | |
request_history.append(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"], | |
} | |
selected_topics = random.sample(topics.get(subject, ["general"]), min(3, len(topics.get(subject, ["general"])))) | |
logger.debug(f"Selected topics: {selected_topics}") | |
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. ALL questions must be computational or proof-based problems. NO conceptual or explanation questions. | |
3. Each question must require mathematical work, calculations, or formal proofs | |
4. 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 | |
5. Number each question as 1), 2), 3) | |
6. Include detailed solutions after each question | |
7. Keep formatting simple and clear""" | |
logger.debug("Sending request to Anthropic API") | |
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 not hasattr(message, 'content') or not message.content: | |
logger.error("No content received from Anthropic API") | |
return "Error: No content received from API", None, None | |
response_text = message.content[0].text | |
logger.debug("Successfully received response from Anthropic API") | |
# Create LaTeX content | |
questions_latex = create_latex_document(response_text, questions_only=True) | |
full_latex = create_latex_document(response_text, questions_only=False) | |
# Save to temporary files | |
questions_path = save_to_temp_file(questions_latex, "questions.tex") | |
full_path = save_to_temp_file(full_latex, "full_test.tex") | |
logger.debug("Successfully created temporary files") | |
return response_text, questions_path, full_path | |
except Exception as e: | |
logger.error(f"Error generating test: {str(e)}") | |
return f"Error: {str(e)}", None, None | |
# 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=[ | |
"Single Variable Calculus", | |
"Multivariable Calculus", | |
"Linear Algebra", | |
"Differential Equations", | |
"Real Analysis", | |
"Complex Analysis", | |
"Abstract Algebra", | |
"Probability Theory", | |
"Numerical Analysis", | |
"Topology" | |
], | |
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 Preview", | |
latex_delimiters=[ | |
{"left": "$$", "right": "$$", "display": True}, | |
{"left": "$", "right": "$", "display": False} | |
] | |
) | |
with gr.Row(): | |
questions_file = gr.File(label="Questions Only (LaTeX)") | |
full_file = gr.File(label="Full Test with Solutions (LaTeX)") | |
generate_btn.click( | |
generate_test, | |
inputs=[subject_dropdown], | |
outputs=[output_text, questions_file, full_file] | |
) | |
if __name__ == "__main__": | |
logger.info("Starting application") | |
interface.launch() |