Spaces:
Sleeping
Sleeping
add error logging
Browse files
app.py
CHANGED
@@ -4,6 +4,12 @@ from anthropic import Anthropic
|
|
4 |
from datetime import datetime, timedelta
|
5 |
from collections import deque
|
6 |
import random
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
# Initialize Anthropic client
|
9 |
anthropic = Anthropic(
|
@@ -14,94 +20,84 @@ anthropic = Anthropic(
|
|
14 |
MAX_REQUESTS_PER_DAY = 25
|
15 |
request_history = deque(maxlen=1000)
|
16 |
|
17 |
-
def check_api_key():
|
18 |
-
"""Verify API key is configured"""
|
19 |
-
if not os.environ.get('ANTHROPIC_API_KEY'):
|
20 |
-
raise ValueError("Anthropic API key not found. Please configure it in HuggingFace Spaces settings.")
|
21 |
-
|
22 |
-
def check_rate_limit():
|
23 |
-
"""Check if we're within rate limits"""
|
24 |
-
now = datetime.now()
|
25 |
-
while request_history and (now - request_history[0]) > timedelta(days=1):
|
26 |
-
request_history.popleft()
|
27 |
-
return len(request_history) < MAX_REQUESTS_PER_DAY
|
28 |
-
|
29 |
-
def clean_latex(text):
|
30 |
-
"""Simple LaTeX cleaning"""
|
31 |
-
text = text.replace('\n', '\n\n')
|
32 |
-
return text
|
33 |
-
|
34 |
def create_latex_document(content, questions_only=False):
|
35 |
"""Create a complete LaTeX document"""
|
36 |
-
|
|
|
37 |
\usepackage{amsmath,amssymb}
|
38 |
\usepackage[margin=1in]{geometry}
|
39 |
\begin{document}
|
40 |
\title{Mathematics Test}
|
41 |
\maketitle
|
42 |
"""
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
if 'Solution:' in line:
|
51 |
-
processed_content.append('\n'.join(current_question))
|
52 |
-
current_question = []
|
53 |
-
continue
|
54 |
-
if any(line.startswith(f"{i})") for i in range(1, 4)):
|
55 |
-
if current_question:
|
56 |
processed_content.append('\n'.join(current_question))
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
def generate_test(subject):
|
67 |
"""Generate a math test"""
|
68 |
try:
|
69 |
-
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
return "Daily request limit reached. Please try again tomorrow.", None, None
|
72 |
|
73 |
-
request_history.append(
|
74 |
|
75 |
topics = {
|
76 |
"Single Variable Calculus": ["limits", "derivatives", "integrals", "series", "applications"],
|
77 |
"Multivariable Calculus": ["partial derivatives", "multiple integrals", "vector fields", "optimization"],
|
78 |
"Linear Algebra": ["matrices", "vector spaces", "eigenvalues", "linear transformations"],
|
79 |
-
# ... add topics for other subjects
|
80 |
}
|
81 |
|
82 |
selected_topics = random.sample(topics.get(subject, ["general"]), min(3, len(topics.get(subject, ["general"]))))
|
83 |
-
|
84 |
-
"one easy, one medium, one challenging",
|
85 |
-
"two medium, one very challenging",
|
86 |
-
"one medium, two challenging"
|
87 |
-
])
|
88 |
|
89 |
system_prompt = f"""You will write math exam questions. Follow these requirements EXACTLY:
|
90 |
1. Write exactly 3 university-level questions focusing on these specific topics: {', '.join(selected_topics)}
|
91 |
-
2.
|
92 |
-
3.
|
93 |
-
4.
|
94 |
-
5. For LaTeX math formatting:
|
95 |
- Use $ for simple inline math
|
96 |
- For equations and solution steps, use $$ on separate lines
|
97 |
- For multi-step solutions, put each step on its own line in $$ $$
|
98 |
- DO NOT use \\begin{{aligned}} or any other environments
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
Important: Every question must require mathematical computation or proof. Do not ask for explanations, descriptions, or concepts."""
|
104 |
|
|
|
105 |
message = anthropic.messages.create(
|
106 |
model="claude-3-opus-20240229",
|
107 |
max_tokens=1500,
|
@@ -112,34 +108,36 @@ def generate_test(subject):
|
|
112 |
}]
|
113 |
)
|
114 |
|
115 |
-
if hasattr(message, 'content')
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
-
|
120 |
-
full_latex = create_latex_document(
|
121 |
-
|
|
|
122 |
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
126 |
|
127 |
except Exception as e:
|
|
|
128 |
return f"Error: {str(e)}", None, None
|
129 |
|
130 |
-
subjects = [
|
131 |
-
"Single Variable Calculus",
|
132 |
-
"Multivariable Calculus",
|
133 |
-
"Linear Algebra",
|
134 |
-
"Differential Equations",
|
135 |
-
"Real Analysis",
|
136 |
-
"Complex Analysis",
|
137 |
-
"Abstract Algebra",
|
138 |
-
"Probability Theory",
|
139 |
-
"Numerical Analysis",
|
140 |
-
"Topology"
|
141 |
-
]
|
142 |
-
|
143 |
# Create Gradio interface
|
144 |
with gr.Blocks() as interface:
|
145 |
gr.Markdown("# Advanced Mathematics Test Generator")
|
@@ -148,14 +146,25 @@ with gr.Blocks() as interface:
|
|
148 |
|
149 |
with gr.Row():
|
150 |
subject_dropdown = gr.Dropdown(
|
151 |
-
choices=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
label="Select Mathematics Subject",
|
153 |
info="Choose a subject for the exam questions"
|
154 |
)
|
155 |
generate_btn = gr.Button("Generate Test")
|
156 |
|
157 |
output_text = gr.Markdown(
|
158 |
-
label="Generated Test",
|
159 |
latex_delimiters=[
|
160 |
{"left": "$$", "right": "$$", "display": True},
|
161 |
{"left": "$", "right": "$", "display": False}
|
@@ -163,19 +172,19 @@ with gr.Blocks() as interface:
|
|
163 |
)
|
164 |
|
165 |
with gr.Row():
|
166 |
-
|
167 |
-
|
168 |
-
questions_file = gr.File(label="Questions Only (LaTeX)")
|
169 |
-
full_file = gr.File(label="Full Test with Solutions (LaTeX)")
|
170 |
|
171 |
def generate_and_prepare_files(subject):
|
172 |
-
|
|
|
173 |
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
|
|
179 |
|
180 |
generate_btn.click(
|
181 |
generate_and_prepare_files,
|
@@ -184,4 +193,5 @@ with gr.Blocks() as interface:
|
|
184 |
)
|
185 |
|
186 |
if __name__ == "__main__":
|
|
|
187 |
interface.launch()
|
|
|
4 |
from datetime import datetime, timedelta
|
5 |
from collections import deque
|
6 |
import random
|
7 |
+
import logging
|
8 |
+
import tempfile
|
9 |
+
|
10 |
+
# Set up logging
|
11 |
+
logging.basicConfig(level=logging.DEBUG)
|
12 |
+
logger = logging.getLogger(__name__)
|
13 |
|
14 |
# Initialize Anthropic client
|
15 |
anthropic = Anthropic(
|
|
|
20 |
MAX_REQUESTS_PER_DAY = 25
|
21 |
request_history = deque(maxlen=1000)
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
def create_latex_document(content, questions_only=False):
|
24 |
"""Create a complete LaTeX document"""
|
25 |
+
try:
|
26 |
+
latex_header = r"""\documentclass{article}
|
27 |
\usepackage{amsmath,amssymb}
|
28 |
\usepackage[margin=1in]{geometry}
|
29 |
\begin{document}
|
30 |
\title{Mathematics Test}
|
31 |
\maketitle
|
32 |
"""
|
33 |
+
latex_footer = r"\end{document}"
|
34 |
+
|
35 |
+
if questions_only:
|
36 |
+
processed_content = []
|
37 |
+
current_question = []
|
38 |
+
for line in content.split('\n'):
|
39 |
+
if 'Solution:' in line:
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
processed_content.append('\n'.join(current_question))
|
41 |
+
current_question = []
|
42 |
+
continue
|
43 |
+
if any(line.startswith(f"{i})") for i in range(1, 4)):
|
44 |
+
if current_question:
|
45 |
+
processed_content.append('\n'.join(current_question))
|
46 |
+
current_question = [line]
|
47 |
+
elif current_question:
|
48 |
+
current_question.append(line)
|
49 |
+
if current_question:
|
50 |
+
processed_content.append('\n'.join(current_question))
|
51 |
+
content = '\n\n'.join(processed_content)
|
52 |
+
|
53 |
+
full_document = f"{latex_header}\n{content}\n{latex_footer}"
|
54 |
+
logger.debug(f"Created {'questions-only' if questions_only else 'full'} LaTeX document")
|
55 |
+
return full_document
|
56 |
+
except Exception as e:
|
57 |
+
logger.error(f"Error creating LaTeX document: {str(e)}")
|
58 |
+
raise
|
59 |
|
60 |
def generate_test(subject):
|
61 |
"""Generate a math test"""
|
62 |
try:
|
63 |
+
if not os.environ.get('ANTHROPIC_API_KEY'):
|
64 |
+
logger.error("Anthropic API key not found")
|
65 |
+
return "Error: Anthropic API key not configured", None, None
|
66 |
+
|
67 |
+
logger.debug(f"Generating test for subject: {subject}")
|
68 |
+
|
69 |
+
# Check rate limit
|
70 |
+
now = datetime.now()
|
71 |
+
while request_history and (now - request_history[0]) > timedelta(days=1):
|
72 |
+
request_history.popleft()
|
73 |
+
if len(request_history) >= MAX_REQUESTS_PER_DAY:
|
74 |
return "Daily request limit reached. Please try again tomorrow.", None, None
|
75 |
|
76 |
+
request_history.append(now)
|
77 |
|
78 |
topics = {
|
79 |
"Single Variable Calculus": ["limits", "derivatives", "integrals", "series", "applications"],
|
80 |
"Multivariable Calculus": ["partial derivatives", "multiple integrals", "vector fields", "optimization"],
|
81 |
"Linear Algebra": ["matrices", "vector spaces", "eigenvalues", "linear transformations"],
|
|
|
82 |
}
|
83 |
|
84 |
selected_topics = random.sample(topics.get(subject, ["general"]), min(3, len(topics.get(subject, ["general"]))))
|
85 |
+
logger.debug(f"Selected topics: {selected_topics}")
|
|
|
|
|
|
|
|
|
86 |
|
87 |
system_prompt = f"""You will write math exam questions. Follow these requirements EXACTLY:
|
88 |
1. Write exactly 3 university-level questions focusing on these specific topics: {', '.join(selected_topics)}
|
89 |
+
2. ALL questions must be computational or proof-based problems. NO conceptual or explanation questions.
|
90 |
+
3. Each question must require mathematical work, calculations, or formal proofs
|
91 |
+
4. For LaTeX math formatting:
|
|
|
92 |
- Use $ for simple inline math
|
93 |
- For equations and solution steps, use $$ on separate lines
|
94 |
- For multi-step solutions, put each step on its own line in $$ $$
|
95 |
- DO NOT use \\begin{{aligned}} or any other environments
|
96 |
+
5. Number each question as 1), 2), 3)
|
97 |
+
6. Include detailed solutions after each question
|
98 |
+
7. Keep formatting simple and clear"""
|
|
|
|
|
99 |
|
100 |
+
logger.debug("Sending request to Anthropic API")
|
101 |
message = anthropic.messages.create(
|
102 |
model="claude-3-opus-20240229",
|
103 |
max_tokens=1500,
|
|
|
108 |
}]
|
109 |
)
|
110 |
|
111 |
+
if not hasattr(message, 'content') or not message.content:
|
112 |
+
logger.error("No content received from Anthropic API")
|
113 |
+
return "Error: No content received from API", None, None
|
114 |
+
|
115 |
+
response_text = message.content[0].text
|
116 |
+
logger.debug("Successfully received response from Anthropic API")
|
117 |
+
|
118 |
+
# Create temporary files
|
119 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.tex', delete=False) as f_questions:
|
120 |
+
questions_latex = create_latex_document(response_text, questions_only=True)
|
121 |
+
f_questions.write(questions_latex)
|
122 |
+
questions_path = f_questions.name
|
123 |
|
124 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.tex', delete=False) as f_full:
|
125 |
+
full_latex = create_latex_document(response_text, questions_only=False)
|
126 |
+
f_full.write(full_latex)
|
127 |
+
full_path = f_full.name
|
128 |
|
129 |
+
logger.debug("Successfully created temporary files")
|
130 |
+
|
131 |
+
return (
|
132 |
+
response_text,
|
133 |
+
(questions_path, "questions.tex"),
|
134 |
+
(full_path, "full_test.tex")
|
135 |
+
)
|
136 |
|
137 |
except Exception as e:
|
138 |
+
logger.error(f"Error generating test: {str(e)}")
|
139 |
return f"Error: {str(e)}", None, None
|
140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
# Create Gradio interface
|
142 |
with gr.Blocks() as interface:
|
143 |
gr.Markdown("# Advanced Mathematics Test Generator")
|
|
|
146 |
|
147 |
with gr.Row():
|
148 |
subject_dropdown = gr.Dropdown(
|
149 |
+
choices=[
|
150 |
+
"Single Variable Calculus",
|
151 |
+
"Multivariable Calculus",
|
152 |
+
"Linear Algebra",
|
153 |
+
"Differential Equations",
|
154 |
+
"Real Analysis",
|
155 |
+
"Complex Analysis",
|
156 |
+
"Abstract Algebra",
|
157 |
+
"Probability Theory",
|
158 |
+
"Numerical Analysis",
|
159 |
+
"Topology"
|
160 |
+
],
|
161 |
label="Select Mathematics Subject",
|
162 |
info="Choose a subject for the exam questions"
|
163 |
)
|
164 |
generate_btn = gr.Button("Generate Test")
|
165 |
|
166 |
output_text = gr.Markdown(
|
167 |
+
label="Generated Test Preview",
|
168 |
latex_delimiters=[
|
169 |
{"left": "$$", "right": "$$", "display": True},
|
170 |
{"left": "$", "right": "$", "display": False}
|
|
|
172 |
)
|
173 |
|
174 |
with gr.Row():
|
175 |
+
questions_file = gr.File(label="Questions Only (LaTeX)")
|
176 |
+
full_file = gr.File(label="Full Test with Solutions (LaTeX)")
|
|
|
|
|
177 |
|
178 |
def generate_and_prepare_files(subject):
|
179 |
+
logger.debug("Starting test generation")
|
180 |
+
preview, questions_path, full_path = generate_test(subject)
|
181 |
|
182 |
+
if questions_path and full_path:
|
183 |
+
logger.debug("Test generation successful, preparing files")
|
184 |
+
return preview, questions_path, full_path
|
185 |
+
else:
|
186 |
+
logger.error("Test generation failed")
|
187 |
+
return preview, None, None
|
188 |
|
189 |
generate_btn.click(
|
190 |
generate_and_prepare_files,
|
|
|
193 |
)
|
194 |
|
195 |
if __name__ == "__main__":
|
196 |
+
logger.info("Starting application")
|
197 |
interface.launch()
|