math agent
math agent
import logging
from asgiref.sync import sync_to_async
from openai import AsyncOpenAI
from contextlib import asynccontextmanager
import os
from main.models import ChatHistory
from .math_visualization_agent import ManimScriptGenerator
from .math_visualization_agent import ManimRenderer
from pathlib import Path
from django.conf import settings
import uuid
import base64
from io import BytesIO
from .math_image_agent import MathSolver
logger = logging.getLogger(__name__)
class ResponseTemplates:
"""Templates for system messages and response structures"""
@staticmethod
def get_interaction_prompt(interaction_type: str) -> str:
"""Get specific prompt based on interaction type"""
prompts = {
'explain': """Break down the concept step by step:
1. Start with basic principles
2. Explain each step clearly
3. Show all calculations
4. Connect concepts together
5. Provide clear explanations""",
@staticmethod
def get_selected_text_template(subject: str, topic: str, deep_think: bool,
interaction_type: str) -> str:
# Get interaction specific prompt
interaction_prompt =
ResponseTemplates.get_interaction_prompt(interaction_type)
return f"""You are an expert JEE tutor. A student has selected the
following text and has a question about it:
{interaction_prompt}
## Concept Analysis
• {'Advanced theoretical background' if deep_think else 'Key concepts'} from the
text
• {'Detailed mathematical foundations' if deep_think else 'Important formulas'}
• {'Complex relationships' if deep_think else 'Basic principles'}
## Detailed Explanation
1. {'Rigorous' if deep_think else 'Clear'} step-by-step breakdown
2. {'Advanced' if deep_think else 'Basic'} mathematical derivations
3. {'Deep insights and connections' if deep_think else 'Key relationships'}
## JEE Focus
• {'Advanced question patterns' if deep_think else 'Common question types'}
• {'Complex variations' if deep_think else 'Standard approaches'}
• {'Deep problem-solving strategies' if deep_think else 'Essential tips'}
Subject: {subject}
Topic: {topic}
Mode: {'Deep Analysis' if deep_think else 'Standard'}
Interaction Type: {interaction_type}"""
@staticmethod
def get_regular_template(subject: str, topic: str, deep_think: bool,
interaction_type: str) -> str:
# Get interaction specific prompt
interaction_prompt =
ResponseTemplates.get_interaction_prompt(interaction_type)
{interaction_prompt}
## Teaching Context
• Subject: {subject.capitalize()}
• Topic: {topic}
• Mode: {'Deep Analysis' if deep_think else 'Standard'}
• Interaction: {interaction_type}
## Concept Overview
• {'Advanced theoretical framework' if deep_think else 'Main concepts'}
• {'Complex mathematical background' if deep_think else 'Basic principles'}
• {'Detailed derivations' if deep_think else 'Key formulas'}
## Solution Approach
1. {'Rigorous' if deep_think else 'Clear'} step-by-step solution
2. {'Advanced' if deep_think else 'Standard'} mathematical working
3. {'Detailed proofs' if deep_think else 'Key steps'} explained
## JEE Strategy
• {'Advanced level questions' if deep_think else 'Common patterns'}
• {'Complex variations' if deep_think else 'Standard types'}
• {'Deep insights' if deep_think else 'Basic approaches'}
## Practice Guide
• {'Advanced' if deep_think else 'Standard'} practice problems
• {'Complex' if deep_think else 'Basic'} variations
• {'Detailed' if deep_think else 'Simple'} solutions"""
class MathAgent:
def __init__(self, api_key: str):
self.api_key = api_key
self.templates = ResponseTemplates()
self._setup_agent()
self.visualization_agent = ManimScriptGenerator()
self.renderer = ManimRenderer()
self.image_solver = MathSolver(api_key) # Initialize MathSolver
@classmethod
async def create(cls):
"""Factory method to create a MathAgent instance"""
try:
# Get API key from environment
api_key = os.getenv('DEEPSEEK_CHAT_API_KEY')
if not api_key:
raise ValueError("DEEPSEEK_CHAT_API_KEY environment variable is not
set")
# Create instance
return cls(api_key=api_key)
except Exception as e:
logger.error(f"Error creating MathAgent: {str(e)}")
raise
def _setup_agent(self):
"""Initialize agent settings"""
if not self.api_key:
raise ValueError("DEEPSEEK_CHAT_API_KEY is not set")
self.chat_history = []
self.max_history = 100
@asynccontextmanager
async def _get_client(self):
"""Context manager for API client"""
try:
# Try DeepSeek first
client = AsyncOpenAI(
api_key=self.api_key,
base_url="https://api.deepseek.com"
)
print("DeepSeek client created")
try:
yield client
finally:
await client.close()
except Exception as e:
logger.warning(f"DeepSeek API failed, falling back to OpenAI:
{str(e)}")
# Fallback to OpenAI
openai_key = os.getenv('OPENAI_API_KEY')
print("OpenAI fallback client created")
if not openai_key:
raise ValueError("OPENAI_API_KEY environment variable is not set")
client = AsyncOpenAI(api_key=openai_key)
print("OpenAI fallback client created")
try:
yield client
finally:
await client.close()
except Exception as e:
logger.error(f"Error saving image: {str(e)}")
raise
async def solve(self, question: str, context: Dict[Any, Any]) -> dict:
try:
logger.info(f"Processing question: {question}")
logger.info(f"question: {question}")
logger.info(f"Context received: {context}")
# Generate visualization
script_path = await self.visualization_agent.generate_script(
concept=concept,
details={
'subject': context_data['subject'],
'question': question,
'deep_think': context_data['deep_think']
}
)
if script_path:
# Render the visualization
visualization_data = await
self.renderer.render_animation(script_path)
logger.info(f"Generated visualization:
{visualization_data}")
except Exception as e:
logger.error(f"Error generating visualization: {str(e)}")
# Continue with text response even if visualization fails
# Prepare messages
messages = self._prepare_messages(question, system_message,
context_data)
print("messages", messages)
print("model_config", model_config)
print("context", context)
# Make API call
solution = await self._make_api_call(messages, model_config, context)
print("solution", solution)
# Prepare response
response = {
"solution": solution,
"context": {
"current_question": question,
"response": solution,
**context_data
}
}
return response
except Exception as e:
logger.error(f"Error in solve method: {str(e)}", exc_info=True)
return self._get_error_response(question, str(e), context)
return messages
except Exception as e:
logger.error(f"Error processing image request: {str(e)}")
raise ValueError("Failed to process image request.")
return response.choices[0].message.content
except Exception as api_error:
last_error = api_error
logger.error(f"DeepSeek API call failed: {str(api_error)}")
raise
except Exception as e:
logger.warning(f"DeepSeek API failed, falling back to OpenAI:
{str(e)}")
return response.choices[0].message.content
question_lower = question.lower()
return False
except Exception as e:
logger.error(f"Error in _needs_visualization: {str(e)}")
return False
async def _generate_visualization(self, concept: str, details: Dict[str, Any])
-> Optional[Dict[str, str]]:
"""Generate visualization for mathematical concepts"""
try:
# Generate animation script
script_path = await self.visualization_agent.generate_script(concept,
details)
if not script_path:
logger.warning(f"Failed to generate visualization script for
{concept}")
return None
# Render animation
visualization = await self.renderer.render_animation(script_path)
if not visualization:
logger.warning(f"Failed to render visualization for {concept}")
return None
return {
'script_path': script_path,
'video_path': visualization.get('local_path'),
'video_url': visualization.get('public_url')
}
except Exception as e:
logger.error(f"Error generating visualization: {str(e)}")
return None