import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from db.session import get_session from models.requests import ThreadRenameRequest from models.responses import ( MessageInfo, SourceInfo, ThreadDeleteResponse, ThreadDetailResponse, ThreadInfo, ThreadListResponse, ThreadStateInfo, ) from services import chat_service logger = logging.getLogger(__name__) router = APIRouter(prefix="/threads", tags=["threads"]) @router.get("", response_model=ThreadListResponse) async def list_threads(session: AsyncSession = Depends(get_session)): threads = await chat_service.list_threads(session) return ThreadListResponse( threads=[ThreadInfo(**t) for t in threads], total=len(threads), ) @router.get("/{thread_id}", response_model=ThreadDetailResponse) async def get_thread(thread_id: int, session: AsyncSession = Depends(get_session)): data = await chat_service.get_thread_detail(session, thread_id) if data is None: raise HTTPException(status_code=404, detail="Thread not found") state = data.get("thread_state") or {} return ThreadDetailResponse( id=data["id"], name=data["name"], created_at=data["created_at"], updated_at=data["updated_at"], messages=[ MessageInfo( id=m["id"], role=m["role"], text=m["text"], created_at=m["created_at"], sources=[SourceInfo(**s) for s in m["sources"]], assembled_prompt=m["assembled_prompt"], intent_code=m.get("intent_code", ""), intent_name=m.get("intent_name", ""), meta=m.get("meta"), escalation_reason=m.get("escalation_reason"), ) for m in data["messages"] ], thread_state=ThreadStateInfo( current_intent_code=state.get("current_intent_code"), current_step=state.get("current_step", 0), current_step_code=state.get("current_step_code"), slots=state.get("slots", {}), handoff_count=state.get("handoff_count", 0), soft_insertion_count=state.get("soft_insertion_count", 0), suspended_intent=state.get("suspended_intent"), resumable_step_code=state.get("resumable_step_code"), resumable_slots=state.get("resumable_slots", {}), pending_guard=state.get("pending_guard"), ), ) @router.patch("/{thread_id}", response_model=ThreadInfo) async def rename_thread( thread_id: int, req: ThreadRenameRequest, session: AsyncSession = Depends(get_session), ): data = await chat_service.rename_thread(session, thread_id, req.name) if data is None: raise HTTPException(status_code=404, detail="Thread not found") return ThreadInfo(**data) @router.delete("/{thread_id}", response_model=ThreadDeleteResponse) async def delete_thread(thread_id: int, session: AsyncSession = Depends(get_session)): deleted = await chat_service.delete_thread(session, thread_id) if deleted is None: raise HTTPException(status_code=404, detail="Thread not found") return ThreadDeleteResponse(ok=True, deleted_messages=deleted)