Source code for rag_service.routers.conversations

"""Conversation history endpoints (list / messages / delete)."""

from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from rag_service.bootstrap import RagComponents
from rag_service.conversation import ConversationManager
from rag_service.routers._deps import get_components

router = APIRouter(prefix="/conversations", tags=["conversations"])


def _ensure_conversation_access(owner_user_id: Optional[str], requester_user_id: Optional[str]) -> None:
    ConversationManager._ensure_conversation_access(
        owner_user_id=owner_user_id,
        requester_user_id=requester_user_id,
    )


[docs] @router.get("") async def list_conversations( components: RagComponents = Depends(get_components), user_id: Optional[str] = None, limit: int = 50, ): """List conversations for a user, ordered by most recent.""" pg_repo = components.pg_repo if pg_repo is None: raise HTTPException(status_code=503, detail="Database not available") session = pg_repo.get_session() try: rows = pg_repo.list_conversations(session, user_id, limit=limit) return [ { "id": str(row.id), "title": row.title, "created_at": row.created_at.isoformat() if row.created_at is not None else None, "updated_at": row.updated_at.isoformat() if row.updated_at else None, } for row in rows ] finally: session.close()
[docs] @router.get("/{conversation_id}/messages") async def get_conversation_messages( conversation_id: str, components: RagComponents = Depends(get_components), user_id: Optional[str] = None, ): """Get messages for a conversation (with ownership check).""" pg_repo = components.pg_repo if pg_repo is None: raise HTTPException(status_code=503, detail="Database not available") session = pg_repo.get_session() try: conv = pg_repo.get_conversation(session, conversation_id) if conv is None: raise HTTPException(status_code=404, detail="Conversation not found") _ensure_conversation_access(conv.user_id, user_id) rows = pg_repo.get_conversation_messages(session, conversation_id, limit=200) return [ { "id": str(row.id), "role": row.role, "content": row.content, "mode": row.mode, "created_at": row.created_at.isoformat() if row.created_at is not None else None, "sources": (row.metadata_ or {}).get("sources"), "timings": (row.metadata_ or {}).get("timings"), "steps": (row.metadata_ or {}).get("steps"), } for row in rows ] finally: session.close()
[docs] @router.delete("/{conversation_id}") async def delete_conversation( conversation_id: str, components: RagComponents = Depends(get_components), user_id: Optional[str] = None, ): """Delete a conversation (with ownership check).""" pg_repo = components.pg_repo if pg_repo is None: raise HTTPException(status_code=503, detail="Database not available") session = pg_repo.get_session() try: conv = pg_repo.get_conversation(session, conversation_id) if conv is None: raise HTTPException(status_code=404, detail="Conversation not found") _ensure_conversation_access(conv.user_id, user_id) pg_repo.delete_conversation(session, conversation_id) session.commit() return {"ok": True} except HTTPException: raise except Exception: session.rollback() raise finally: session.close()