use posrgress for chat history

parent 425d7b68
......@@ -7,5 +7,8 @@ COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
#keep the container running always
CMD ["python", "main.py"]
#just keep the container running without doing anything
CMD ["sh", "-c", "while :; do sleep 10; done"]
#run the app automatically when the container starts
#CMD ["python", "main.py"]
......@@ -3,7 +3,7 @@ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from typing import Optional
import uvicorn
from core import AppConfig
from core import AppConfig, StudentNationality
from repositories import StorageRepository, MinIOStorageRepository
from handlers import AudioMessageHandler, TextMessageHandler
from services import (
......@@ -64,7 +64,7 @@ def create_app() -> FastAPI:
Handles incoming chat messages (either text or audio).
Generates responses locally using the agent service.
"""
return container.chat_service.process_message(file, text)
return container.chat_service.process_message(student_id="student_001", file=file, text=text, nationality=StudentNationality.EGYPTIAN)
@app.get("/get-audio-response")
async def get_audio_response():
......
import os
import psycopg2
from psycopg2.extras import RealDictCursor
from typing import List, Dict
import logging
logger = logging.getLogger(__name__)
class ChatDatabaseService:
"""Simple service for managing chat history in PostgreSQL"""
def __init__(self):
self.conn = psycopg2.connect(
host=os.getenv("POSTGRES_HOST", "postgres"),
user=os.getenv("POSTGRES_USER"),
password=os.getenv("POSTGRES_PASSWORD"),
dbname=os.getenv("POSTGRES_DB"),
)
def get_chat_history(self, student_id: str, limit: int = 20) -> List[Dict[str, str]]:
"""Get chat history for a student, returns in chronological order"""
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute(
"""
SELECT role, content
FROM chat_history
WHERE student_id = %s
ORDER BY created_at DESC
LIMIT %s;
""",
(student_id, limit)
)
results = cur.fetchall()
# Return in chronological order (oldest first)
return [{"role": row["role"], "content": row["content"]} for row in reversed(results)]
def add_message(self, student_id: str, role: str, content: str):
"""Add a message to chat history"""
with self.conn.cursor() as cur:
cur.execute(
"""
INSERT INTO chat_history (student_id, role, content)
VALUES (%s, %s, %s);
""",
(student_id, role, content)
)
self.conn.commit()
def clear_history(self, student_id: str):
"""Clear chat history for a student"""
with self.conn.cursor() as cur:
cur.execute(
"DELETE FROM chat_history WHERE student_id = %s",
(student_id,)
)
self.conn.commit()
def limit_history(self, student_id: str, max_messages: int = 40):
"""Keep only recent messages for a student"""
with self.conn.cursor() as cur:
cur.execute(
"""
DELETE FROM chat_history
WHERE student_id = %s
AND role != 'system'
AND id NOT IN (
SELECT id FROM chat_history
WHERE student_id = %s AND role != 'system'
ORDER BY created_at DESC
LIMIT %s
);
""",
(student_id, student_id, max_messages)
)
self.conn.commit()
def close(self):
if self.conn:
self.conn.close()
\ No newline at end of file
......@@ -21,7 +21,7 @@ class ChatService:
self.openai_service = openai_service
self.agent_service = agent_service
# Message handlers (no webhook dependencies)
# Message handlers
self.handlers = {
MessageType.AUDIO: AudioMessageHandler(
storage_repo,
......@@ -31,99 +31,85 @@ class ChatService:
MessageType.TEXT: TextMessageHandler()
}
def process_message(self, file: Optional[UploadFile] = None, text: Optional[str] = None) -> dict:
"""Process incoming message and generate agent response directly"""
def process_message(self,
student_id: str,
file: Optional[UploadFile] = None,
text: Optional[str] = None,
nationality: StudentNationality = StudentNationality.EGYPTIAN) -> dict:
"""Process message for student using database memory"""
self.response_manager.clear_response()
try:
# Process the input message first
# Process the input message
if file and file.filename:
# Handle audio message - transcribe first
result = self.handlers[MessageType.AUDIO].handle(file=file)
if result.get("status") == "success":
# Get transcribed text from the result
user_message = result.get("transcription", "")
if not user_message:
# Fallback message if transcription failed
user_message = "تم إرسال رسالة صوتية - فشل في التفريغ المحلي"
else:
raise HTTPException(status_code=400, detail="Failed to process audio message")
elif text:
# Handle text message
result = self.handlers[MessageType.TEXT].handle(text=text)
user_message = text
else:
raise HTTPException(status_code=400, detail="No text or audio file provided.")
# Generate agent response using the local agent service
# Generate agent response using database
try:
agent_response = self.agent_service.generate_response(user_message, nationality=StudentNationality.EGYPTIAN)
agent_response = self.agent_service.generate_response(
user_message=user_message,
student_id=student_id,
nationality=nationality
)
# Generate TTS audio from the response
# Generate TTS audio
audio_filename = self._generate_and_upload_audio(agent_response)
# Store response for retrieval
self.response_manager.store_response(agent_response, audio_filename)
print(f"Generated agent response: {agent_response[:100]}...")
print(f"Generated response for student {student_id}: {agent_response[:100]}...")
return {
"status": "success",
"message": "Message processed and agent response ready",
"student_id": student_id,
"agent_response": agent_response,
"audio_filename": audio_filename
}
except Exception as agent_error:
print(f"Agent service error: {agent_error}")
raise HTTPException(status_code=500, detail=f"Agent response generation failed: {str(agent_error)}")
print(f"Agent error for student {student_id}: {agent_error}")
raise HTTPException(status_code=500, detail=f"Agent response failed: {str(agent_error)}")
except Exception as e:
print(f"Error processing message: {e}")
print(f"Error processing message for student {student_id}: {e}")
raise HTTPException(status_code=500, detail=f"Failed to process message: {str(e)}")
def _generate_and_upload_audio(self, text: str) -> str:
"""Generate audio from text and upload to MinIO, return filename"""
"""Generate and upload TTS audio"""
try:
import time
# Generate audio using OpenAI service
temp_file_path = self.openai_service.generate_speech(text)
# Generate unique filename for MinIO
timestamp = int(time.time())
filename = f"agent_response_{timestamp}.mp3"
minio_file_path = f"audio/{filename}"
print(f"Uploading generated audio to MinIO: {minio_file_path}")
print(f"Uploading audio to MinIO: {minio_file_path}")
# Upload to MinIO
with open(temp_file_path, 'rb') as audio_file:
self.storage_repo.upload_file(audio_file, self.config.minio_bucket, minio_file_path)
# Clean up temporary file
self.openai_service.cleanup_temp_file(temp_file_path)
print(f"Successfully generated and uploaded TTS audio: {filename}")
print(f"Successfully generated TTS audio: {filename}")
return filename
except Exception as e:
print(f"Error generating and uploading audio: {e}")
# Don't fail the entire request if TTS fails
print(f"Error generating audio: {e}")
return None
\ No newline at end of file
def get_agent_stats(self, conversation_id: str = "default") -> dict:
"""Get conversation statistics from agent service"""
return self.agent_service.get_conversation_stats(conversation_id)
def clear_conversation(self, conversation_id: str = "default"):
"""Clear conversation history"""
self.agent_service.clear_conversation(conversation_id)
return {"status": "success", "message": f"Conversation {conversation_id} cleared"}
def set_system_prompt(self, prompt: str):
"""Update the agent's system prompt"""
self.agent_service.set_system_prompt(prompt)
return {"status": "success", "message": "System prompt updated"}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment