todoappapi / services /conversation.py
GrowWithTalha's picture
feat: sync backend changes from main repo
dc3879e
"""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()