AI_tutor / app /chatbot.py
vishalshelke's picture
Upload 10 files
a2438f7 verified
import openai
import os
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from app.models import ChatMessage, ChatSession
from app.rag_system import RAGSystem
logger = logging.getLogger(__name__)
class RAGChatbot:
"""RAG-powered chatbot with memory of PDF and lecture content"""
def __init__(self, openai_api_key: str):
self.client = openai.OpenAI(api_key=openai_api_key)
self.rag_system = RAGSystem(openai_api_key=openai_api_key)
self.sessions: Dict[str, ChatSession] = {}
self.max_context_length = 8000 # Token limit for context
def create_session(self, session_id: str, pdf_content: str = None, lecture_content: str = None) -> bool:
"""Create a new chat session with optional PDF and lecture content"""
try:
session = ChatSession(
session_id=session_id,
pdf_content=pdf_content,
lecture_content=lecture_content
)
self.sessions[session_id] = session
# Add content to RAG system if provided
if pdf_content:
self.rag_system.add_pdf_content(session_id, pdf_content)
if lecture_content:
self.rag_system.add_lecture_content(session_id, lecture_content)
logger.info(f"Created chat session {session_id}")
return True
except Exception as e:
logger.error(f"Failed to create session {session_id}: {str(e)}")
return False
def add_message(self, session_id: str, role: str, content: str) -> bool:
"""Add a message to the session history"""
try:
if session_id not in self.sessions:
return False
message = ChatMessage(role=role, content=content)
self.sessions[session_id].messages.append(message)
return True
except Exception as e:
logger.error(f"Failed to add message to session {session_id}: {str(e)}")
return False
def get_response(self, session_id: str, user_message: str) -> Dict[str, Any]:
"""Generate a response to user message using RAG"""
try:
if session_id not in self.sessions:
return {
'success': False,
'error': 'Session not found',
'response': ''
}
session = self.sessions[session_id]
# Add user message to history
self.add_message(session_id, "user", user_message)
# Retrieve relevant content
retrieval_result = self.rag_system.retrieve_relevant_content(
session_id, user_message, n_results=5
)
if not retrieval_result['success']:
logger.warning(f"Content retrieval failed for session {session_id}")
relevant_content = []
else:
relevant_content = retrieval_result['results']
# Generate response
response = self._generate_response(session, user_message, relevant_content)
# Add assistant response to history
self.add_message(session_id, "assistant", response)
return {
'success': True,
'response': response,
'sources_used': len(relevant_content),
'session_id': session_id
}
except Exception as e:
logger.error(f"Failed to generate response for session {session_id}: {str(e)}")
return {
'success': False,
'error': str(e),
'response': 'I apologize, but I encountered an error while processing your message. Please try again.'
}
def _generate_response(self, session: ChatSession, user_message: str, relevant_content: List[Dict]) -> str:
"""Generate response using OpenAI with RAG context"""
try:
# Build context from relevant content
context_parts = []
if relevant_content:
context_parts.append("Relevant information from your documents:")
for i, item in enumerate(relevant_content[:3], 1): # Limit to top 3 results
source = "PDF" if item['source'] == 'pdf' else "Lecture"
context_parts.append(f"{i}. [{source}] {item['content'][:500]}...")
context_parts.append("")
# Build conversation history (limited to recent messages)
conversation_history = []
recent_messages = session.messages[-6:] # Last 6 messages for context
for msg in recent_messages[:-1]: # Exclude the current user message
conversation_history.append(f"{msg.role.title()}: {msg.content}")
# Create system prompt
system_prompt = """You are a helpful AI assistant that can answer questions about uploaded PDF documents and generated lectures.
Key guidelines:
1. Use the provided relevant information to answer questions accurately
2. If you don't have enough information in the context, say so clearly
3. Maintain a conversational and educational tone
4. Reference the source (PDF or Lecture) when appropriate
5. Be concise but thorough in your explanations
6. If asked about something not in the documents, explain that your knowledge is limited to the uploaded content
Always strive to be helpful while being honest about the limitations of your knowledge."""
# Build the full prompt
messages = [{"role": "system", "content": system_prompt}]
# Add context if available
if context_parts:
context_message = "\n".join(context_parts)
messages.append({"role": "system", "content": context_message})
# Add conversation history
if conversation_history:
history_message = "Previous conversation:\n" + "\n".join(conversation_history)
messages.append({"role": "system", "content": history_message})
# Add current user message
messages.append({"role": "user", "content": user_message})
# Generate response
response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
temperature=0.7,
max_tokens=1000
)
return response.choices[0].message.content
except Exception as e:
logger.error(f"Response generation failed: {str(e)}")
return "I apologize, but I'm having trouble generating a response right now. Please try rephrasing your question."
def get_session_history(self, session_id: str) -> List[Dict[str, Any]]:
"""Get chat history for a session"""
try:
if session_id not in self.sessions:
return []
session = self.sessions[session_id]
return [
{
'role': msg.role,
'content': msg.content,
'timestamp': msg.timestamp.isoformat()
}
for msg in session.messages
]
except Exception as e:
logger.error(f"Failed to get session history {session_id}: {str(e)}")
return []
def clear_session(self, session_id: str) -> bool:
"""Clear a chat session and its data"""
try:
# Clear from RAG system
self.rag_system.clear_session_data(session_id)
# Remove from local sessions
if session_id in self.sessions:
del self.sessions[session_id]
logger.info(f"Cleared session {session_id}")
return True
except Exception as e:
logger.error(f"Failed to clear session {session_id}: {str(e)}")
return False
def get_session_stats(self, session_id: str) -> Dict[str, Any]:
"""Get statistics about a session"""
try:
if session_id not in self.sessions:
return {'exists': False}
session = self.sessions[session_id]
rag_stats = self.rag_system.get_session_stats(session_id)
return {
'exists': True,
'message_count': len(session.messages),
'created_at': session.created_at.isoformat(),
'has_pdf': session.pdf_content is not None,
'has_lecture': session.lecture_content is not None,
**rag_stats
}
except Exception as e:
logger.error(f"Failed to get session stats {session_id}: {str(e)}")
return {'exists': False, 'error': str(e)}
def update_session_content(self, session_id: str, pdf_content: str = None, lecture_content: str = None) -> bool:
"""Update session with new content"""
try:
if session_id not in self.sessions:
return False
session = self.sessions[session_id]
# Update PDF content
if pdf_content:
session.pdf_content = pdf_content
self.rag_system.add_pdf_content(session_id, pdf_content)
# Update lecture content
if lecture_content:
session.lecture_content = lecture_content
self.rag_system.add_lecture_content(session_id, lecture_content)
logger.info(f"Updated content for session {session_id}")
return True
except Exception as e:
logger.error(f"Failed to update session content {session_id}: {str(e)}")
return False
def set_api_key(self, api_key: str):
"""Set the OpenAI API key dynamically."""
self.client = openai.OpenAI(api_key=api_key)