AI-todo / app /api /services.py
umar-30's picture
Backend API for HF Spaces
2ac2730
Raw History Blame Contribute Delete
4.19 kB
"""
Conversation service for database operations.
Provides stateless database operations for conversations and messages.
All state is loaded fresh from the database per request.
"""
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID
from sqlmodel import Session, select
from ..database import get_engine
from ..models.conversation import Conversation
from ..models.message import Message
class ConversationNotFoundError(Exception):
"""Raised when a conversation is not found."""
pass
class ConversationAccessDeniedError(Exception):
"""Raised when user doesn't own the conversation."""
pass
def _get_session() -> Session:
"""Get a new database session."""
return Session(get_engine())
def get_conversation_with_messages(
conversation_id: UUID,
user_id: str
) -> tuple[Conversation, list[Message]]:
"""
Load a conversation with all its messages from the database.
Args:
conversation_id: The conversation's unique identifier
user_id: The user's unique identifier for ownership validation
Returns:
Tuple of (Conversation, list of Messages ordered by created_at)
Raises:
ConversationNotFoundError: If conversation doesn't exist
ConversationAccessDeniedError: If user doesn't own the conversation
"""
with _get_session() as session:
# Load conversation
statement = select(Conversation).where(Conversation.id == conversation_id)
conversation = session.exec(statement).first()
if not conversation:
raise ConversationNotFoundError(
f"Conversation {conversation_id} not found"
)
# Validate ownership
if conversation.user_id != user_id:
raise ConversationAccessDeniedError(
f"Access denied to conversation {conversation_id}"
)
# Load messages ordered by created_at
msg_statement = (
select(Message)
.where(Message.conversation_id == conversation_id)
.order_by(Message.created_at)
)
messages = list(session.exec(msg_statement).all())
return conversation, messages
def create_conversation(user_id: str) -> Conversation:
"""
Create a new conversation for a user.
Args:
user_id: The user's unique identifier
Returns:
Newly created Conversation with generated ID
"""
conversation = Conversation(user_id=user_id)
with _get_session() as session:
session.add(conversation)
session.commit()
session.refresh(conversation)
return conversation
def create_message(
user_id: str,
conversation_id: UUID,
role: str,
content: str
) -> Message:
"""
Create a new message in a conversation.
Also updates the conversation's updated_at timestamp.
Args:
user_id: The user's unique identifier
conversation_id: The parent conversation's ID
role: Message role ("user" or "assistant")
content: Message content
Returns:
Newly created Message
"""
message = Message(
user_id=user_id,
conversation_id=conversation_id,
role=role,
content=content,
)
with _get_session() as session:
# Add the message
session.add(message)
# Update conversation timestamp
statement = select(Conversation).where(Conversation.id == conversation_id)
conversation = session.exec(statement).first()
if conversation:
conversation.updated_at = datetime.now(timezone.utc)
session.add(conversation)
session.commit()
session.refresh(message)
return message
def get_conversation(conversation_id: UUID) -> Optional[Conversation]:
"""
Get a conversation by ID without messages.
Args:
conversation_id: The conversation's unique identifier
Returns:
Conversation if found, None otherwise
"""
with _get_session() as session:
statement = select(Conversation).where(Conversation.id == conversation_id)
return session.exec(statement).first()