Spaces:
Running
Running
| """Conversation service for managing chat sessions. | |
| [Task]: T016 | |
| [From]: specs/004-ai-chatbot/tasks.md | |
| This service handles conversation persistence and history loading. | |
| """ | |
| import uuid | |
| from datetime import datetime | |
| from typing import Optional, List | |
| from sqlmodel import Session, select | |
| from models.conversation import Conversation | |
| from models.message import Message, MessageRole | |
| def get_or_create_conversation( | |
| db: Session, | |
| user_id: uuid.UUID, | |
| conversation_id: Optional[uuid.UUID] = None | |
| ) -> Conversation: | |
| """Get existing conversation or create new one. | |
| [From]: specs/004-ai-chatbot/plan.md - Conversation Management | |
| Args: | |
| db: Database session | |
| user_id: User ID who owns the conversation | |
| conversation_id: Optional conversation ID to load | |
| Returns: | |
| Conversation object (existing or new) | |
| Raises: | |
| ValueError: If conversation_id provided but not found or doesn't belong to user | |
| """ | |
| if conversation_id: | |
| # Load existing conversation | |
| conversation = db.get(Conversation, conversation_id) | |
| if not conversation: | |
| raise ValueError(f"Conversation {conversation_id} not found") | |
| if conversation.user_id != user_id: | |
| raise ValueError("Conversation does not belong to this user") | |
| return conversation | |
| else: | |
| # Create new conversation | |
| conversation = Conversation( | |
| id=uuid.uuid4(), | |
| user_id=user_id, | |
| created_at=datetime.utcnow(), | |
| updated_at=datetime.utcnow() | |
| ) | |
| db.add(conversation) | |
| db.commit() | |
| db.refresh(conversation) | |
| return conversation | |
| def load_conversation_history( | |
| db: Session, | |
| conversation_id: uuid.UUID | |
| ) -> List[dict[str, str]]: | |
| """Load conversation history in OpenAI format. | |
| [From]: specs/004-ai-chatbot/plan.md - Conversation History Loading | |
| Args: | |
| db: Database session | |
| conversation_id: Conversation ID to load | |
| Returns: | |
| List of messages in OpenAI format: | |
| [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}] | |
| """ | |
| statement = select(Message).where( | |
| Message.conversation_id == conversation_id | |
| ).order_by(Message.created_at.asc()) | |
| messages = db.exec(statement).all() | |
| # Convert to OpenAI format (role is already a string from database) | |
| conversation_history = [ | |
| {"role": msg.role, "content": msg.content} | |
| for msg in messages | |
| ] | |
| return conversation_history | |
| def list_user_conversations( | |
| db: Session, | |
| user_id: uuid.UUID, | |
| limit: int = 50, | |
| offset: int = 0 | |
| ) -> List[Conversation]: | |
| """List all conversations for a user. | |
| [From]: specs/004-ai-chatbot/spec.md - US2 (Future) | |
| Args: | |
| db: Database session | |
| user_id: User ID | |
| limit: Maximum number of conversations to return | |
| offset: Number of conversations to skip | |
| Returns: | |
| List of conversations ordered by updated_at (most recent first) | |
| """ | |
| statement = select(Conversation).where( | |
| Conversation.user_id == user_id | |
| ).order_by( | |
| Conversation.updated_at.desc() | |
| ).offset(offset).limit(limit) | |
| conversations = db.exec(statement).all() | |
| return list(conversations) | |
| def update_conversation_timestamp( | |
| db: Session, | |
| conversation_id: uuid.UUID | |
| ) -> None: | |
| """Update conversation's updated_at timestamp. | |
| [From]: specs/004-ai-chatbot/plan.md - Conversation Management | |
| This is called when a new message is added to update the conversation's | |
| position in the user's conversation list. | |
| Args: | |
| db: Database session | |
| conversation_id: Conversation ID to update | |
| """ | |
| conversation = db.get(Conversation, conversation_id) | |
| if conversation: | |
| conversation.updated_at = datetime.utcnow() | |
| db.add(conversation) | |
| db.commit() | |