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 | |
from sympy import * | |
import json | |
from pathlib import Path | |
import openai | |
# 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') | |
) | |
# Initialize OpenAI client | |
openai.api_key = os.environ.get('My_MathTest_Key') | |
if openai.api_key is None: | |
logger.error("OpenAI API key not found in environment variables") | |
raise ValueError("OpenAI API key not found. Please set the My_MathTest_Key environment variable.") | |
# Request tracking | |
MAX_REQUESTS_PER_DAY = 500 | |
request_history = deque(maxlen=1000) | |
SYMPY_GUIDELINES = """ | |
When writing SymPy code to verify solutions: | |
NOTE: For eigenvalue problems, use 'lam = Symbol('lam')' instead of importing from sympy.abc | |
1. Variable Declaration and Expressions: | |
- ALWAYS create symbolic expressions instead of literal numbers when working with mathematical operations: | |
```python | |
# CORRECT: | |
x = Symbol('x') | |
expr = x + 1 # Creates a symbolic expression | |
# INCORRECT: | |
expr = 1 # This is just a number, can't be differentiated | |
``` | |
- For polynomials and functions: | |
```python | |
# CORRECT: | |
x = Symbol('x') | |
p = x**2 + 2*x + 1 # Creates a polynomial expression | |
# INCORRECT: | |
p = 1 # This won't work for operations like diff() | |
``` | |
- When verifying operator actions: | |
```python | |
# CORRECT: | |
x = Symbol('x') | |
def verify_operator(p): | |
x = Symbol('x') # Always use Symbol inside functions too | |
return p.subs(x, 1) # Substitute values after creating expression | |
# INCORRECT: | |
def verify_operator(p): | |
return p # Passing raw numbers won't work | |
``` | |
- For integration bounds: | |
```python | |
# CORRECT: | |
t = Symbol('t') | |
expr = t**2 | |
result = integrate(expr, (t, 0, 1)) | |
# INCORRECT: | |
result = integrate(2, (t, 0, 1)) # Can't integrate a number | |
``` | |
2. Solving and Computing: | |
- Never use strings in solve() or other SymPy functions: | |
CORRECT: solve(eq, x) | |
INCORRECT: solve(eq, 'x') | |
- Define equations symbolically: | |
CORRECT: eq = 2*sqrt(h) - sqrt(12) + 5*k | |
INCORRECT: eq = 2*sqrt('h') - sqrt(12) + 5*k | |
3. Printing and Output: | |
- Include print statements for ALL calculations and results | |
- Print intermediate steps and final answers | |
- Print variable values after they are computed | |
- Use simple print statements instead of f-strings for SymPy expressions | |
- Print expressions with labels on separate lines: | |
```python | |
print("Expression label:") | |
print(expression) | |
``` | |
4. Numeric Calculations: | |
- Use Float() for decimal numbers in calculations | |
- Use float() for final printing of results | |
- Avoid evalf() as it may cause errors | |
- For numeric results: | |
```python | |
result = expression.evalf() | |
print("Result:") | |
print(float(result)) | |
``` | |
5. Working with Series and Sequences: | |
- Use Float() for sequence terms | |
- Convert sums to float() before printing | |
- For series calculations, print intermediate terms | |
6. Matrix Operations and Systems of Equations: | |
- Never use symbolic variables as matrix indices: | |
```python | |
# CORRECT: | |
i, j = 0, 1 # Use integers for indexing | |
M = Matrix([[1, 2], [3, 4]]) | |
element = M[i, j] | |
# INCORRECT: | |
x = Symbol('x') | |
element = M[x, 0] # This will raise an error | |
``` | |
- For matrix analysis, always convert equations to Matrix form: | |
```python | |
# CORRECT: | |
A = Matrix([[1, 2], [3, 4]]) | |
eigenvals = A.eigenvals() | |
# For system of equations: | |
x, y = symbols('x y') | |
system = Matrix([[2, 1], [1, -1]]) | |
b = Matrix([5, 1]) | |
solution = system.solve(b) | |
``` | |
- For matrix operations with variables: | |
```python | |
# CORRECT: | |
x = Symbol('x') | |
M = Matrix([[x, 1], [2, 3]]) | |
result = M * M # Matrix multiplication | |
# INCORRECT: | |
M[Symbol('i'), Symbol('j')] = x # Don't use symbolic indices | |
``` | |
- For systems of equations that might be linearly dependent, use row reduction instead of matrix inversion. Here's the template for handling such systems: | |
7. Limit Calculations: | |
- ALWAYS compute one-sided limits (from the left and the right) when evaluating any limit: | |
```python | |
# Example: | |
x = Symbol('x') | |
expr = (sin(x)*cos(x) - x + x**3) / (x**3 * sqrt(1 + x) - x**3) | |
# Calculate the limit from the left (x -> 0-): | |
left_limit = limit(expr, x, 0, dir='-') | |
print("Limit from the left (x -> 0-):") | |
print(left_limit) | |
# Calculate the limit from the right (x -> 0+): | |
right_limit = limit(expr, x, 0, dir='+') | |
print("Limit from the right (x -> 0+):") | |
print(right_limit) | |
``` | |
- After computing both one-sided limits, verify if they match: | |
```python | |
if left_limit == right_limit: | |
print("The two-sided limit exists and is:", left_limit) | |
else: | |
print("The two-sided limit does not exist.") | |
``` | |
- If the limit diverges (\(\infty\) or \(-\infty\)), explicitly state that in the print output. | |
- For piecewise or discontinuous functions, compute limits at all points of interest, including boundaries. | |
- **Important Note**: Always test limits symbolically first. If SymPy produces unexpected results, simplify the expression or expand it (e.g., using `series`) before re-evaluating the limit. | |
8. If calculating integrals, write the SymPy code to compute the original integral directly with respect to the original variable of integration, | |
not any integral that might have been rewritten with a variable substitution. In addition to seeking an analytic solution, also obtain a numerical solution using two methods | |
(i) SymPy's integrate function; and | |
(ii) using mpmath, example: | |
```python | |
import mpmath | |
f = lambda x: x**2 / ((x**4+1)*mpmath.sqrt(x**2+1)) | |
mpmath.quad(f, [0, mpmath.inf]) | |
``` | |
9. If using SymPy to evalute an infinite sum, attempt using the infinite sum, and in addition report what the sum of the first 100 terms is as a sanity check. | |
10. Do not use SciPy. | |
```python | |
from sympy import Matrix, symbols, solve | |
def analyze_system(A, b): | |
'Analyze a system Ax = b using row reduction. Returns whether solution exists and if it's unique.' | |
# Augmented matrix [A|b] | |
aug = Matrix(A.row_join(b)) | |
# Get row echelon form | |
rref, pivots = aug.rref() | |
print("Row reduced augmented matrix:") | |
print(rref) | |
print("\\nPivot columns:", pivots) | |
# Get rank of coefficient matrix and augmented matrix | |
rank_A = Matrix(A).rank() | |
rank_aug = aug.rank() | |
print(f"\\nRank of coefficient matrix: {rank_A}") | |
print(f"Rank of augmented matrix: {rank_aug}") | |
if rank_aug > rank_A: | |
print("\\nNo solution exists") | |
return None | |
elif rank_A < A.cols: | |
print("\\nInfinitely many solutions exist") | |
return "infinite" | |
else: | |
print("\\nUnique solution exists") | |
return "unique" | |
# When solving a system Ax = b: | |
A = Matrix([[...], [...], [...]]) # coefficient matrix | |
b = Matrix([[...], [...], [...]]) # right-hand side | |
# Analyze system | |
result = analyze_system(A, b) | |
if result == "infinite": | |
# Get parametric form of solution | |
aug = Matrix(A.row_join(b)) | |
rref, pivots = aug.rref() | |
# Get free variables | |
vars = symbols('x y z') # adjust variable names as needed | |
free_vars = [var for i, var in enumerate(vars) if i not in pivots] | |
print("\\nParametric solution (t is free parameter):") | |
for i, var in enumerate(vars): | |
if i in pivots: | |
row = pivots.index(i) | |
expr = rref[row, -1] | |
for j, free_var in enumerate(free_vars): | |
expr -= rref[row, pivots[-1] + 1 + j] * free_var | |
print(f"{var} = {expr}") | |
else: | |
print(f"{var} = t") # use different parameter names for multiple free variables | |
``` | |
Always use this template when working with systems of equations to handle potential linear dependence correctly. """ | |
def load_proof_repository(): | |
"""Load the proof repository from the repository file""" | |
repo_path = Path("Lebl-theorems-all.json") | |
try: | |
with open(repo_path, "r") as f: | |
return json.load(f) | |
except Exception as e: | |
logger.error(f"Error loading proof repository: {str(e)}") | |
return None | |
TOPIC_MAPPINGS = { | |
"integration": ["integral", "integrable", "riemann", "integrate", "antiderivative"], | |
"continuity": ["continuous", "discontinuous", "discontinuity", "uniformly continuous"], | |
"sequences": ["sequence", "convergent", "divergent", "monotone", "subsequence"], | |
"series": ["series", "sum", "convergent series", "power series"], | |
"differentiation": ["derivative", "differentiable", "differential"], | |
"limits": ["limit", "cluster point", "accumulation"], | |
"functions": ["function", "mapping", "surjective", "injective", "bijective"], | |
"bounded": ["bound", "bounded above", "bounded below", "supremum", "infimum"] | |
} | |
def get_related_terms(topic): | |
"""Get all related terms for a given topic""" | |
# Get direct mappings | |
related = TOPIC_MAPPINGS.get(topic.lower(), []) | |
# Add the original topic | |
related.append(topic.lower()) | |
# Remove duplicates while preserving order | |
return list(dict.fromkeys(related)) | |
def matches_topic(text, topic_terms): | |
"""Check if any topic terms appear in the text""" | |
text_lower = text.lower() | |
return any(term in text_lower for term in topic_terms) | |
def get_relevant_proofs(topic): | |
"""Get relevant proofs from repository based on topic, randomly selecting examples""" | |
repository = load_proof_repository() | |
if not repository: | |
logger.error("Failed to load proof repository") | |
return [] | |
logger.debug(f"Searching for proofs related to topic: {topic}") | |
topic_terms = get_related_terms(topic) | |
logger.debug(f"Related terms: {topic_terms}") | |
relevant_proofs = [] | |
for theorem in repository.get("dataset", {}).get("theorems", []): | |
# Check categories | |
categories = theorem.get("categories", []) | |
category_match = any(matches_topic(cat, topic_terms) for cat in categories) | |
# Check contents | |
contents = theorem.get("contents", []) | |
content_match = any(matches_topic(content, topic_terms) for content in contents) | |
# Check title | |
title = theorem.get("title", "") | |
title_match = matches_topic(title, topic_terms) | |
if (category_match or content_match or title_match): | |
if theorem.get("contents") and theorem.get("proofs"): | |
proof_content = { | |
"title": theorem.get("title", ""), | |
"contents": theorem.get("contents", []), | |
"proofs": [p.get("contents", []) for p in theorem.get("proofs", [])] | |
} | |
relevant_proofs.append(proof_content) | |
logger.debug(f"Found matching proof: {proof_content['title']}") | |
logger.debug(f"Matched via: {'categories' if category_match else 'contents' if content_match else 'title'}") | |
logger.debug(f"Found {len(relevant_proofs)} relevant proofs before sampling") | |
# Randomly select 3 proofs if we have more than 3 | |
if len(relevant_proofs) > 3: | |
selected = random.sample(relevant_proofs, 3) | |
logger.debug("Selected proofs for enhancement:") | |
for proof in selected: | |
logger.debug(f"- {proof['title']}") | |
return selected | |
return relevant_proofs | |
def enhance_prompt_with_proofs(system_prompt, subject, topic): | |
"""Enhance the system prompt with relevant proofs if subject is Real Analysis""" | |
if subject != "Real Analysis": | |
logger.debug("Skipping proof enhancement - not Real Analysis") | |
return system_prompt | |
relevant_proofs = get_relevant_proofs(topic) | |
if not relevant_proofs: | |
logger.debug(f"No relevant proofs found for topic: {topic}") | |
return system_prompt | |
logger.debug(f"Enhancing prompt with {len(relevant_proofs)} proofs") | |
# Add proof examples to the prompt | |
proof_examples = "\n\nReference these proof examples for style and approach:\n" | |
for proof in relevant_proofs: | |
logger.debug(f"Adding proof: {proof['title']}") | |
proof_examples += f"\nTheorem: {proof['title']}\n" | |
proof_examples += "Statement: " + " ".join(proof['contents']) + "\n" | |
if proof['proofs']: | |
first_proof = " ".join(proof['proofs'][0]) | |
logger.debug(f"Proof length: {len(first_proof)} characters") | |
proof_examples += "Proof: " + first_proof + "\n" | |
# Add specific instructions for using the examples | |
enhanced_prompt = f"""{system_prompt} | |
ADDITIONAL PROOF GUIDELINES: | |
1. Consider the following proof examples from an established textbook | |
2. Maintain similar level of rigor and detail | |
3. Use similar proof techniques where applicable | |
4. Follow similar notation and presentation style | |
{proof_examples}""" | |
return enhanced_prompt | |
def get_difficulty_parameters(difficulty_level): | |
"""Return specific parameters and constraints based on difficulty level""" | |
parameters = { | |
1: { # Very Easy | |
"description": "suitable for beginners", | |
"constraints": [ | |
"Use only basic concepts and straightforward calculations", | |
"Break complex problems into smaller, guided steps", | |
"Use simple numbers and avoid complex algebraic expressions" | |
], | |
"example_style": "Similar to standard homework problems", | |
"model": "claude-3-5-sonnet-20241022" | |
}, | |
2: { # Easy | |
"description": "easy, but requiring some thought", | |
"constraints": [ | |
"Use basic concepts with minor complications", | |
"Minimal guidance provided", | |
"Use moderately complex numbers or expressions" | |
], | |
"example_style": "Similar to quiz questions", | |
"model": "claude-3-5-sonnet-20241022" | |
}, | |
3: { # Intermediate | |
"description": "intermediate difficulty, testing deeper understanding", | |
"constraints": [ | |
"Combine 2-3 related concepts", | |
"Include some non-obvious solution paths", | |
"Require multi-step reasoning", | |
"Use moderate algebraic complexity" | |
], | |
"example_style": "Similar to intermediate-difficulty exam questions", | |
"model": "claude-3-5-sonnet-20241022" | |
}, | |
4: { # Difficult | |
"description": "challenging, requiring strong mathematical maturity", | |
"constraints": [ | |
"Combine multiple concepts creatively", | |
"Require insight and deep understanding", | |
"Use sophisticated mathematical reasoning" | |
], | |
"example_style": "Similar to challenging exam questions", | |
"model": "claude-3-5-sonnet-20241022" | |
}, | |
5: { # Very Difficult | |
"description": "very challenging, testing mastery and creativity at a graduate level", | |
"constraints": [ | |
"Create novel applications of theoretical concepts", | |
"Require graduate-level mathematical reasoning", | |
"Combine multiple advanced topics in unexpected ways", | |
"Demand creative problem-solving approaches", | |
"Include rigorous proof construction", | |
"Require synthesis across mathematical domains", | |
"Test deep theoretical understanding" | |
], | |
"example_style": "Similar to graduate qualifying exams or advanced competition problems", | |
"model": "claude-3-5-sonnet-20241022" | |
} | |
} | |
return parameters.get(difficulty_level) | |
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 Question} | |
\maketitle | |
""" | |
latex_footer = r"\end{document}" | |
if questions_only: | |
# Modified to handle single question | |
processed_content = content.split('Solution:')[0] | |
content = 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: | |
temp_dir = Path(tempfile.gettempdir()) / "math_test_files" | |
temp_dir.mkdir(exist_ok=True) | |
file_path = temp_dir / filename | |
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 get_problem_type_addition(question_type): | |
"""Return specific requirements based on problem type""" | |
problem_type_additions = { | |
"application": """ | |
The application question MUST: | |
- Present a real-world scenario or practical problem | |
- Require modeling the situation mathematically | |
- Connect abstract mathematical concepts to concrete situations | |
- Include realistic context and data | |
- Require students to: | |
1. Identify relevant mathematical concepts | |
2. Translate the practical problem into mathematical terms | |
3. Solve using appropriate mathematical techniques | |
4. Interpret the results in the context of the original problem | |
- Randomly select one of these topic areas with equal probability | |
* Physics applications (motion, forces, work) | |
* Engineering scenarios | |
* Economics problems | |
* Biological systems | |
* Business applications | |
* Social science applications | |
* Data science applications | |
""", | |
"proof": """ | |
The proof question MUST: | |
- Require a formal mathematical proof | |
- Focus on demonstrating logical reasoning | |
- Require justification for each step | |
- Emphasize theoretical understanding | |
The proof question MAY NOT: | |
- Include Real-world applications or scenarios | |
- Include Pure computation problems | |
- Ask only for numerical answers | |
""", | |
"computation": """ | |
The computation question MUST: | |
- Require specific algebraic calculations | |
- Focus on mathematical techniques | |
- Have concrete answers in the form of algebraic expressions (about half of questions) or numbers (about half of questions) | |
- Test procedural knowledge | |
The computation question MAY NOT: | |
- Include extended real-world applications or scenarios | |
- Ask for a proof | |
""" | |
} | |
return problem_type_additions.get(question_type, "") | |
def get_solution_for_verification(response_text, SYMPY_CONFIRMED, final_verification=None): | |
""" | |
Extract the relevant parts of the solution for verification based on whether | |
the original solution was correct or not. Always preserves the original question. | |
Returns tuple of (question, solution). | |
""" | |
# Extract the question using the specific markers | |
question_start = "Here is a test question" | |
solution_start = "Here is a detailed solution to the test question" | |
# Find the question section | |
q_start = response_text.find(question_start) | |
q_end = response_text.find(solution_start) | |
if q_start == -1 or q_end == -1: | |
logger.error("Could not find question markers") | |
# Return best effort split - assume first paragraph is question | |
paragraphs = response_text.split('\n\n') | |
if len(paragraphs) > 1: | |
return paragraphs[0].strip(), '\n\n'.join(paragraphs[1:]).strip() | |
else: | |
# If we can't even split paragraphs, just return the whole text as both question and solution | |
return response_text.strip(), response_text.strip() | |
# Extract question | |
question = response_text[q_start:q_end].strip() | |
# If we have a final verification solution, use that | |
if final_verification: | |
marker = "Here is the complete verified solution:" | |
if marker in final_verification: | |
solution = final_verification.split(marker)[1].strip() | |
logger.debug("Using final verified solution for verification") | |
return question, solution | |
else: | |
# If marker not found in final verification, extract solution part | |
logger.debug("Marker not found in final verification, using full verification text") | |
return question, final_verification.strip() | |
# Otherwise, extract original solution (before SymPy code if present) | |
original_solution = response_text[q_end:] | |
sympy_start = original_solution.find('```python') | |
if sympy_start != -1: | |
solution = original_solution[:sympy_start].strip() | |
else: | |
solution = original_solution.strip() | |
logger.debug("Using original solution for verification") | |
return question, solution | |
def verify_with_chatgpt(question, solution): | |
""" | |
Send the solution to ChatGPT for verification and grading. | |
Returns the verification response. | |
""" | |
try: | |
# Extract SymPy verification if it exists | |
sympy_start = solution.find("Here's the SymPy code") | |
sympy_end = solution.find("Verification Analysis:") | |
sympy_section = "" | |
main_solution = solution | |
if sympy_start != -1 and sympy_end != -1: | |
sympy_section = solution[sympy_start:sympy_end].strip() | |
main_solution = solution[:sympy_start].strip() | |
# Construct the prompt for ChatGPT | |
verification_prompt = f"""As an expert mathematician, please verify and grade this mathematics solution. | |
Analyze the following aspects: | |
1. Mathematical Correctness (50 points): | |
- Are all calculations correct? | |
- Are proofs logically sound? | |
- Are all steps properly justified? | |
2. Completeness (20 points): | |
- Are all necessary cases considered? | |
- Are edge cases addressed? | |
- Are all required steps shown? | |
3. Clarity and Presentation (20 points): | |
- Is the solution well-organized? | |
- Are steps clearly explained? | |
- Is mathematical notation used correctly? | |
4. Mathematical Sophistication (10 points): | |
- Is the approach elegant? | |
- Are efficient methods used? | |
- Is mathematical insight demonstrated? | |
Question: | |
{question} | |
Student's Solution: | |
{main_solution} | |
{f"The student also provided the following SymPy verification of their solution: {sympy_section}" if sympy_section else ""} | |
Please consider this in your assessment of the work. | |
Please provide: | |
1. A brief point-by-point analysis of the solution | |
2. Specific comments on any errors or oversights | |
3. Suggestions for improvement (if any) | |
4. A numerical score out of 100 based on the criteria above | |
5. Finally, if and only if the numerical score is less than 90 out of 100, present a complete revised solution to which you would award a score of 100. | |
The complete revised solution if you provide one must show ALL steps and be fully self-contained. | |
Important Formatting Instructions: | |
- Put each solution step on its own line in $$ $$ | |
- For inline math expressions, always use $ $, do not use the LaTeX bracket notation, do not use backslash ( ), do not use backslash [ ] | |
- DO NOT use begin{{aligned}} or similar environments | |
- For currency amounts in dollars, write out the word dollars instead of using $ | |
* Example: 1000 dollars | |
- make sure there are never unmatched dollar signs as that will ruin the LaTeX rendering | |
Before marking a mathematical expression as incorrect: | |
- Check if the expression could be equivalent to what you think it should be through algebraic manipulation | |
- Write out the full steps to verify non-equivalence | |
- Consider multiple valid ways to write the same mathematical relationship | |
- Only mark expressions as wrong if you can prove they are not equivalent to the correct form | |
- When grading mathematical solutions: | |
Format your response with clear headers and bullet points.""" | |
# Call OpenAI API | |
response = openai.chat.completions.create( | |
model="o1-preview", | |
messages=[ | |
{ | |
"role": "assistant", | |
"content": "I am an expert mathematics professor who grades student solutions using LaTeX formatting. When writing inline math, I always write math using LaTeX $ $ delimiters - NEVER the backslash parentheses or backslash brackets approach. I am FORBIDDEN from ever using any notation that uses backslash followed by open parenthesis, backslash followed by open bracket, or backslash followed by square bracket. For displayed equations, I use $$ $$." | |
}, | |
{ | |
"role": "user", | |
"content": verification_prompt | |
} | |
], | |
temperature=1 | |
) | |
# Extract the verification text from the response | |
verification_text = response.choices[0].message.content | |
return verification_text | |
except Exception as e: | |
logger.error(f"Error in ChatGPT verification: {str(e)}") | |
return f"Error in ChatGPT verification: {str(e)}" | |
def append_chatgpt_verification(initial_response, SYMPY_CONFIRMED, final_verification=None): | |
""" | |
Main function to handle the ChatGPT verification process. | |
Returns the original response with the ChatGPT verification appended. | |
""" | |
try: | |
# Get the appropriate solution text for verification | |
question, solution_text = get_solution_for_verification(initial_response, SYMPY_CONFIRMED, final_verification) | |
# Get ChatGPT's verification | |
chatgpt_verification = verify_with_chatgpt(question, solution_text) | |
if chatgpt_verification: # Add this check | |
full_response = f"{initial_response}\n\nChatGPT Verification and Grading:\n{chatgpt_verification}" | |
return full_response | |
return initial_response | |
except Exception as e: | |
logger.error(f"Error in verification process: {str(e)}") | |
return initial_response + f"\n\nError in ChatGPT verification: {str(e)}" | |
def generate_question(subject, difficulty, question_type, use_enhancement=False, include_chatgpt="no"): | |
"""Generate a single math question with additional verification""" | |
try: | |
logger.debug(f"ChatGPT verification: {'enabled' if include_chatgpt == 'yes' else 'disabled'}") | |
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 {question_type} question for subject: {subject} at difficulty level: {difficulty}") | |
logger.debug(f"Textbook enhancement: {'enabled' if use_enhancement else 'disabled'}") | |
# 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", "related rates", | |
"linear_approximation", "lhopitals rule", "integration techniques","improper integrals","area between curves", | |
"volumes of revolution","arc length","parametric equations","polar coordinates"], | |
"Multivariable Calculus": ["partial derivatives", "multiple integrals", "vector fields", "optimization"], | |
"Linear Algebra": ["matrices", "vector spaces", "eigenvalues", "linear transformations"], | |
"Differential Equations": ["first order equations", "second order equations", "systems", "stability analysis"], | |
"Real Analysis": ["sequences", "series", "continuity", "differentiation", "integration"], | |
"Complex Analysis": ["complex functions", "analyticity", "contour integration", "residues"], | |
"Abstract Algebra": ["groups", "rings", "fields", "homomorphisms"], | |
"Probability Theory": ["probability spaces", "random variables", "distributions", "limit theorems"], | |
"Numerical Analysis": ["approximation", "interpolation", "numerical integration", "error analysis"], | |
"Topology": ["metric spaces", "continuity", "compactness", "connectedness"] | |
} | |
selected_topic = random.choice(topics.get(subject, ["general"])) | |
logger.debug(f"Selected topic: {selected_topic}") | |
difficulty_params = get_difficulty_parameters(difficulty) | |
problem_type_addition = get_problem_type_addition(question_type) | |
system_prompt = f"""You are an expert mathematics professor creating a {difficulty_params['description']} exam question. | |
STRICT REQUIREMENTS: | |
1. Write exactly 1 {question_type} question on {subject} covering {selected_topic} that can be solved analytically without numerical methods. | |
- A question where any part of the solution must resort to numerical methods is invalid. | |
- Begin the output with the text "Here is a test question that is a {question_type} question on {subject} covering {selected_topic}." | |
2. Difficulty Level Guidelines: | |
{difficulty_params['description'].upper()} | |
Follow these specific constraints: | |
{chr(10).join(f' - {c}' for c in difficulty_params['constraints'])} | |
{problem_type_addition} | |
3. Style Reference: | |
Question should be {difficulty_params['example_style']} | |
4. For LaTeX formatting: | |
- Make sure that the question statement uses proper LaTeX math mode | |
- Use $ for inline math | |
- Use $$ on separate lines for equations and solutions | |
- Put each solution step on its own line in $$ $$ | |
- DO NOT use \\begin{{aligned}} or similar environments | |
- When writing questions involving currency expressed in dollars NEVER use the `$` symbol as it will be interepreted as math mode. ALWAYS write out the word dollars. | |
* Example: 1000 dollars | |
5. Include a detailed solution | |
- Begin the solution with "Here is a detailed solution to the test question." | |
- If the question involves geometry make sure to identify any general geometric formulas that apply, For example: | |
* Areas/volumes of common shapes and solids | |
* Cross-sectional areas of geometric figures | |
* Arc lengths and sector areas | |
- When setting up differential equations either in calculus or differential equation applications | |
* carefully consider the direction of change in variables | |
* ensure integration bounds align with the physical direction of the process being modeled | |
- The solution must be analytical. It must not rely on numerical methods. | |
* NO part of the solution may resort to or be based on numerical analysis. | |
* The only numerical calculations that should be done are those that could be done on a simple scientific calculator. | |
* Make sure to simplify completely as far as analytical methods will allow | |
6. Maintain clear formatting | |
7. At the end of the solution output, print SymPy code that you would use to solve or verify the main equations in the question | |
8. Observe the folloiwng SymPy Guidelines | |
{SYMPY_GUIDELINES} | |
9. For problems where the subject is Real Analysis, observe the following guidelines: | |
a. **Justify Every Step** | |
- Provide detailed reasoning for each step and explicitly justify every bounding argument, inequality, or limit claim. | |
- If concluding that terms vanish in a limit, clearly explain why. | |
- When using supremum/infimum, justify its behavior under limits, differentiation, or integration, ensuring it does not introduce discontinuities. | |
b. **Handling Limits and Differentiability** | |
- In epsilon-delta proofs, clearly explain why the chosen delta works. | |
- When using limit substitutions, justify why the transformation preserves limit behavior. | |
- If verifying differentiability, explicitly state why continuity at that point is necessary and confirm that continuity has been established before proceeding. | |
- If proving continuity or differentiability, check symmetry in approach from both sides (left-hand and right-hand limits or derivatives). | |
c. **Function Definitions and Explicit Statements** | |
- When proving continuity, explicitly confirm that f(x) is **defined** at the point of interest and state what its value is. | |
- If a function is given piecewise, clearly state the function values at transition points before evaluating limits. | |
d. **Limit Justifications and Transitions** | |
- When using standard limits briefly justify why it applies | |
- If a limit is computed informally before a formal epsilon-delta proof, explicitly state that the formal proof serves to confirm the computed limit rigorously. | |
- Ensure smooth logical transitions between different parts of the proof by briefly explaining why one step leads naturally to the next. | |
e. **Function Properties and Integrability** | |
- If stating that a function is Riemann integrable, compact, or uniformly continuous, explain why. | |
- If claiming a function is continuous for all x not equal to zero, explicitly justify why using function composition, bounded functions, or known theorems. | |
- When assuming an integral is finite, provide justification based on function class properties (e.g., Riemann integrability implies boundedness). | |
f. **Inequalities and Asymptotics** | |
- When using inequalities (e.g., Hölder’s, Jensen’s), explain why they apply and what function properties make them relevant. | |
- If using factorial ratios or infinite series sums, explicitly state their rate of convergence and reference known bounds (e.g., Stirling’s approximation). | |
g. **Uniform Convergence and Sequence Behavior** | |
- When proving uniform convergence, ensure that the bound obtained is independent of x to establish uniform control. | |
- If using asymptotic behavior (e.g., factorial ratios tending to zero), provide explicit justification rather than just stating the result. | |
h. **Clarify the Use of Key Theorems (e.g., Squeeze Theorem)** | |
- When using the squeeze theorem, clearly state why both bounding functions tend to the same limit and explicitly apply the theorem in the conclusion. | |
i. **Logical Flow and Transitions** | |
- After major steps (e.g., computing a limit, verifying continuity), summarize why the step was necessary and how it connects to the next part of the proof. | |
- If transitioning from an informal calculation to a formal proof, explicitly state the purpose of the formal proof in confirming the earlier result. | |
j. **Concluding and Intuitive Explanations** | |
- Conclude with an intuitive explanation of why the result makes sense, possibly connecting it to known theorems or simple examples. | |
- In notes after the proof, highlight potential sources of confusion for students and clarify tricky aspects of the problem. | |
10. If you specify a quadrant restriction (e.g. "in the first quadrant") in a problem with calculating area between lines/curves or volume between surfaces, | |
make sure the question is CLEAR about what regions you intend to be included in the solution, by breaking up the question. Examples: | |
- NOT CLEAR question: Find the area of the region bounded by the curves y = sin(x), y = cos(x), and x = 7*pi/4 in the first quadrant. | |
- CLEAR question: Find the area of the region bounded by the curves y = sin(x), y = cos(x), and x = 7*pi/4. Then find the area of that region that intersects with the first quadrant. | |
11. When finding critical points in multivariable calculus: | |
- Always check what happens when any variable equals zero (except where undefined) | |
- Just because a point is ruled out of the domain doesn't mean that entire line/curve is ruled out | |
- When the Hessian is inconclusive, evaluate the function along the critical curves to determine behavior | |
- Don't rely solely on the Hessian - consider direct function evaluation and nearby points | |
12. When using symmetry arguments: | |
- Explicitly state what is symmetric | |
- Identify the axis/plane of symmetry | |
13. In calculus do not forget opportunities to apply power-reduction formulas for trig functions | |
- e.g.: Integral from 0 to pi of [cos(theta)]^(2n) d(theta) = (pi / 2^(2n)) * (2n choose n), where choose is the combinatorial choose function | |
14. In expanding or factoring polynomial expressions, be careful not to make errors | |
- (1+u^2)^2 is equal to (1+2u^2+u^4), NOT equal to (1+3u^2+u^4) | |
15. When finding points where dy/dx = 0 in parametric equations: | |
- (a) First find the ratio dy/dx = (dy/dt)/(dx/dt) | |
- (b) Find t-values where this ratio equals 0 | |
- (c) CRITICAL: For any t-values where both dy/dt = 0 AND dx/dt = 0: | |
- (d) These are potential "corner points" or cusps | |
- (e) Must apply parametric L'Hôpital's rule: | |
* Define f(t) = dy/dt and g(t) = dx/dt | |
* If f(t₀) = g(t₀) = 0, evaluate lim[t→t₀] f'(t)/g'(t) | |
* This limit, if it exists, gives the actual slope at t₀ | |
Only conclude the tangent is horizontal if this limit equals 0 | |
- (f) For each solution t: | |
- Calculate the corresponding point (x(t), y(t)) | |
- Verify the point lies on the curve | |
- State whether it's a regular point or special point (cusp, corner, etc.) | |
16. Be careful with trigonometric expressions involving powers | |
- Example: solving sin^2(x)=cos(x) can be solved as 1-cos^2(x)=cos(x) | |
""" | |
#Consider | |
#When writing SymPy code: | |
#- Use FiniteSet(1, 2, 3) instead of Set([1, 2, 3]) for finite sets | |
#- Import specific functions instead of using 'from sympy import *' | |
#- Print results of each calculation step | |
# Only enhance the prompt if explicitly requested AND it's a Real Analysis proof | |
if use_enhancement and subject == "Real Analysis" and question_type == "proof": | |
logger.debug("Applying textbook enhancement to prompt") | |
system_prompt = enhance_prompt_with_proofs(system_prompt, subject, selected_topic) | |
else: | |
logger.debug("Skipping textbook enhancement") | |
logger.debug("Sending request to Anthropic API") | |
message = anthropic.messages.create( | |
model=difficulty_params['model'], | |
max_tokens=4096, | |
temperature=0.7, | |
messages=[{ | |
"role": "user", | |
"content": f"{system_prompt}\n\nWrite a question 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") | |
# Execute SymPy code and append results | |
sympy_output = extract_and_run_sympy_code_simple(response_text) | |
has_discrepancy = False # Initialize outside the if block | |
SYMPY_CONFIRMED = None | |
revised_solution = None | |
final_verification = None | |
# Store original response text before modifications | |
original_response = response_text | |
# Always do verification analysis | |
if "Error" in sympy_output: | |
verification_text = "SymPy verification failed with error. Manual solution must be verified independently." | |
SYMPY_CONFIRMED = "Inconclusive" | |
response_text = f"{response_text}\n\nSymPy Verification Results:\n```\n{sympy_output}\n```\n\nVerification Analysis:\n{verification_text}" | |
else: | |
resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED = check_and_resolve_discrepancy(original_response, sympy_output) | |
response_text = f"{response_text}\n\nSymPy Verification Results:\n```\n{sympy_output}\n```\n\nVerification Analysis:\n{resolution_text}" | |
if has_discrepancy and revised_solution: | |
logger.debug("Performing final verification for problem with discrepancy") | |
final_verification = perform_final_verification(revised_solution, SYMPY_CONFIRMED) | |
response_text += "\n\nFinal Expert Verification:\n" + final_verification | |
# add the ChatGPT verification | |
if include_chatgpt == "yes": | |
response_text = append_chatgpt_verification( | |
response_text, | |
SYMPY_CONFIRMED, | |
final_verification if has_discrepancy else None | |
) | |
# 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, "question.tex") | |
full_path = save_to_temp_file(full_latex, "full_question.tex") | |
logger.debug("Successfully created temporary files") | |
return response_text, questions_path, full_path | |
except Exception as e: | |
logger.error(f"Error generating question: {str(e)}") | |
return f"Error: {str(e)}", None, None | |
def extract_and_run_sympy_code_simple(response_text): | |
""" | |
Extract SymPy code from the response and execute it. | |
""" | |
try: | |
# Extract code | |
sympy_start = response_text.find('```python') | |
if sympy_start == -1: | |
return "No SymPy code found in the response." | |
code_start = response_text.find('\n', sympy_start) + 1 | |
code_end = response_text.find('```', code_start) | |
if code_end == -1: | |
return "Malformed SymPy code block." | |
sympy_code = response_text[code_start:code_end].strip() | |
# Import SymPy at the module level | |
import sympy | |
# Create globals dict with all SymPy functions | |
globals_dict = {} | |
globals_dict.update(vars(sympy)) | |
globals_dict.update({ | |
'print': print, | |
'float': float, | |
'Symbol': sympy.Symbol, | |
'symbols': sympy.symbols, | |
'solve': sympy.solve, | |
'sqrt': sympy.sqrt, | |
'pi': sympy.pi, | |
'diff': sympy.diff, | |
'integrate': sympy.integrate, | |
'simplify': sympy.simplify, | |
'Matrix': sympy.Matrix | |
}) | |
# Remove the sympy import line from the code if present | |
lines = sympy_code.split('\n') | |
filtered_lines = [line for line in lines if not line.strip().startswith('from sympy import') and not line.strip().startswith('import sympy')] | |
modified_code = '\n'.join(filtered_lines) | |
# Capture output | |
import io | |
from contextlib import redirect_stdout | |
output_buffer = io.StringIO() | |
with redirect_stdout(output_buffer): | |
exec(modified_code, globals_dict) | |
return output_buffer.getvalue().strip() or "No output produced" | |
except Exception as e: | |
return f"Error executing SymPy code: {str(e)}" | |
def check_and_resolve_discrepancy(initial_response, sympy_output): | |
""" | |
Compare the SymPy output with the initial response and resolve any discrepancies. | |
Returns tuple of (resolution_text, has_discrepancy, revised_solution) | |
""" | |
has_discrepancy = False #Initialize | |
resolution_text = "" | |
revised_solution = None | |
SYMPY_CONFIRMED = None # Initialize at the start | |
try: | |
resolution_prompt = f"""Here is a mathematics question with two answers. | |
1. First, write out both answers: | |
- Original solution: [write the final answer] | |
- SymPy solution: [write the SymPy answer] | |
2. To prove equivalence, you MUST do at least ONE of the following: | |
a) Algebraically transform one expression into the other through valid steps | |
b) Show that they evaluate to the sane number. | |
NOTE: Before you assert that the SymPy and Original solution evaluate to the same number, CAREFULLY calculate the value of each. If the expressions are not obviously identical, double check: CALCULATE each solution CORRECTLY out to 10 decimal places and compare the results. | |
3. For special functions (like hypergeometric functions): | |
- Do not assume equivalence without verification | |
- Use series expansions or numerical evaluation at test points if needed | |
- Explicitly state if you cannot verify equivalence | |
4. After your analysis, conclude ONE of the following: | |
If equivalence is PROVEN: | |
- Write "SYMPY_CONFIRMED: True" on its own line (this means SymPy's output CONFIRMS the original solution) | |
- Explain exactly how you proved equivalence | |
- Show all steps of the verification | |
If equivalence CANNOT be proven: | |
- Write "SYMPY_CONFIRMED: False" on its own line | |
- Explain why equivalence cannot be established | |
- Write "Here is the revised complete solution:" and then write out an ENTIRE corrected solution from beginning | |
to end, including all parts that were correct and the corrections for any incorrect parts. | |
Do not try to shorten by referring to the previous solution or say things like "the rest remains the same" - write | |
out everything in full. | |
If the two answers are inconsistent with each other then please: | |
1. Identify which solution is correct | |
2. Explain the error in the incorrect solution | |
3. Write "Here is the revised complete solution:" | |
4. Start with "SYMPY_CONFIRMED: False" on its own line. | |
If verification is INCONCLUSIVE: | |
- Write "SYMPY_CONFIRMED: Inconclusive" on its own line | |
- Explain why equivalence cannot be determined | |
- Request a new SymPy verification with additional checks | |
Never claim solutions match without showing explicit mathematical proof of equivalence. | |
Please maintain the same LaTeX formatting as the original solution. | |
Original solution: | |
{initial_response} | |
SymPy Verification Results: | |
{sympy_output} | |
Please maintain the same LaTeX formatting as the original solution.""" | |
# Make API call for resolution | |
message = anthropic.messages.create( | |
model="claude-3-5-sonnet-20241022", | |
max_tokens=4096, | |
temperature=0.2, | |
messages=[{ | |
"role": "user", | |
"content": resolution_prompt | |
}] | |
) | |
resolution_text = message.content[0].text | |
# Check if resolution contains new SymPy code | |
if "```python" in resolution_text: | |
new_sympy_output = extract_and_run_sympy_code_simple(resolution_text) | |
resolution_text += "\n\nNew SymPy Verification Results:\n```\n" + new_sympy_output + "\n```" | |
# Determine if there was a discrepancy that required a revised solution | |
# Check for any indication of inconsistency or error | |
inconsistency_phrases = [ | |
"inconsistent", "inconsistency", "incorrect", "error", "wrong", | |
"discrepancy", "mistaken", "mistake" | |
] | |
has_discrepancy = any(phrase in resolution_text.lower() for phrase in inconsistency_phrases) | |
# Look for the required marker phrase and extract the solution after it | |
marker = "Here is the revised complete solution:" | |
revised_solution = None | |
if has_discrepancy: | |
# Split at the marker | |
if marker in resolution_text: | |
parts = resolution_text.split(marker, maxsplit=1) | |
if len(parts) > 1: | |
revised_solution = parts[1].strip() | |
# If the solution seems too short (might be partial), don't accept it | |
if len(revised_solution) < 100: # Rough minimum length for a complete solution | |
revised_solution = None | |
# If we didn't find a complete solution, force a recheck | |
if not revised_solution: | |
logger.debug("Initial solution extraction failed, requesting a complete solution") | |
# Make a new API call specifically requesting a complete solution | |
complete_solution_prompt = f"""The previous solution had inconsistencies. Please provide a complete solution | |
from beginning to end. Start your response with exactly this phrase: | |
"Here is the revised complete solution:" | |
Then write out the entire solution, including all parts both correct and corrected. | |
Do not refer to the original solution or say any parts remain the same. | |
Original problem and verification results: | |
{initial_response} | |
SymPy Results: | |
{sympy_output}""" | |
try: | |
message = anthropic.messages.create( | |
model="claude-3-5-sonnet-20241022", | |
max_tokens=4096, | |
temperature=0.2, | |
messages=[{"role": "user", "content": complete_solution_prompt}] | |
) | |
new_response = message.content[0].text | |
if marker in new_response: | |
parts = new_response.split(marker, maxsplit=1) | |
if len(parts) > 1: | |
revised_solution = parts[1].strip() | |
except Exception as e: | |
logger.error(f"Error in solution recheck: {str(e)}") | |
# Parse whether SymPy was correct | |
SYMPY_CONFIRMED = None | |
if "SYMPY_CONFIRMED: True" in resolution_text: | |
SYMPY_CONFIRMED = True | |
elif "SYMPY_CONFIRMED: False" in resolution_text: | |
SYMPY_CONFIRMED = False | |
return resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED | |
except Exception as e: | |
logger.error(f"Error in discrepancy resolution: {str(e)}") | |
resolution_text = f"Error in resolution: {str(e)}" | |
has_discrepancy = False # Explicitly set in error case | |
revised_solution = None | |
return resolution_text, has_discrepancy, revised_solution, SYMPY_CONFIRMED | |
def perform_final_verification(revised_solution, SYMPY_CONFIRMED): | |
""" | |
Perform a final verification of the revised solution. | |
""" | |
verification_prompt = f"""As an expert mathematician, please carefully verify this revised solution to an advanced mathematics problem. | |
Revised Solution to Verify: | |
{revised_solution} | |
Please follow these steps exactly: | |
1. First, analyze the solution for: | |
- Mathematical correctness | |
- Missing cases or assumptions | |
- Completeness and rigor | |
- Necessary conditions and edge cases | |
- Any subtle errors or oversights | |
2. Write exactly this phrase to begin your analysis: | |
"Here is the complete verified solution:" | |
3. Then write out the ENTIRE solution from beginning to end, including: | |
- All correct parts from the original solution | |
- All needed corrections | |
- All additional cases and verifications | |
- Any missing steps or assumptions | |
- Any necessary additional proofs or derivations | |
4. The answer aligns with the {'SymPy' if SYMPY_CONFIRMED else 'original'} answer proven correct | |
Your complete solution must: | |
- Be completely self-contained, do not assume the reader has read the previous solution | |
- Not refer to the original solution, again do not assume the reader has read the previous solution | |
- Show every step of the calculation | |
- Include all necessary verifications | |
- Maintain proper LaTeX formatting with $ for inline math and $ on separate lines | |
- When referring to the dollar as a currency, never use the `$` symbol but rather write out the word dollar | |
Remember to write out the complete solution even if you are repeating things from the first solution - the goal is to have a single, complete, verified solution, INCLUDING all details and full mathematical rigor, and WITHOUT assuming the reader has read any of the previous solution material. | |
""" | |
try: | |
# Make API call for final verification | |
message = anthropic.messages.create( | |
model="claude-3-5-sonnet-20241022", | |
max_tokens=4096, | |
temperature=0.2, | |
messages=[{ | |
"role": "user", | |
"content": verification_prompt | |
}] | |
) | |
verification_result = message.content[0].text | |
# If verification includes new SymPy code, run it | |
if "```python" in verification_result: | |
new_sympy_output = extract_and_run_sympy_code_simple(verification_result) | |
verification_result += "\n\nFinal SymPy Verification:\n```\n" + new_sympy_output + "\n```" | |
return verification_result | |
except Exception as e: | |
logger.error(f"Error in final verification: {str(e)}") | |
return f"Error in final verification: {str(e)}" | |
# Create Gradio interface | |
with gr.Blocks() as interface: | |
gr.Markdown("# Advanced Mathematics Question Generator") | |
gr.Markdown("""Generates a unique university-level mathematics question with solution using Claude 3. | |
Each question features different topics and difficulty levels. Limited to 500 requests per day.""") | |
with gr.Row(): | |
with gr.Column(): | |
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 question" | |
) | |
difficulty_slider = gr.Slider( | |
minimum=1, | |
maximum=5, | |
step=1, | |
value=3, | |
label="Difficulty Level", | |
info="1: Very Easy, 2: Easy, 3: Moderate, 4: Difficult, 5: Very Difficult" | |
) | |
question_type = gr.Radio( | |
choices=["computation", "proof", "application"], | |
label="Question Type", | |
info="Select the type of question you want", | |
value="computation" | |
) | |
# Add the new enhancement checkbox | |
use_enhancement = gr.Radio( | |
choices=["yes", "no"], | |
label="Enhance with Textbook Material", | |
info="Include relevant textbook examples to guide question generation", | |
value="no" | |
) | |
# Add ChatGPT verification toggle | |
chatgpt_verify = gr.Radio( | |
choices=["yes", "no"], | |
label="Include ChatGPT Verification", | |
info="Enable/disable ChatGPT grading (saves credits)", | |
value="no" # Default to off to save credits | |
) | |
generate_btn = gr.Button("Generate Question") | |
output_text = gr.Markdown( | |
label="Generated Question Preview", | |
latex_delimiters=[ | |
{"left": "$$", "right": "$$", "display": True}, | |
{"left": "$", "right": "$", "display": False} | |
] | |
) | |
with gr.Row(): | |
questions_file = gr.File(label="Question Only (LaTeX)") | |
full_file = gr.File(label="Question with Solution (LaTeX)") | |
# Update the click event to include the new parameter | |
generate_btn.click( | |
generate_question, | |
inputs=[ | |
subject_dropdown, | |
difficulty_slider, | |
question_type, | |
use_enhancement, | |
chatgpt_verify | |
], | |
outputs=[output_text, questions_file, full_file] | |
) | |
if __name__ == "__main__": | |
logger.info("Starting application") | |
interface.launch() |