"""Copilot router: chat, action execution, history, and voice endpoints. All endpoints require authentication (get_current_user). Actions are proposed by the AI and only executed after user confirmation. """ from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.dependencies import get_current_user, get_pagination from app.models.user import User from app.schemas.copilot import ( ActionRequest, ActionResponse, ChatHistoryResponse, ChatMessageItem, ChatRequest, ChatResponse, VoiceRequest, VoiceResponse, ) from app.services import copilot_service router = APIRouter(prefix="/copilot", tags=["copilot"]) @router.post("/chat", response_model=ChatResponse, status_code=status.HTTP_200_OK) async def copilot_chat( body: ChatRequest, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): """Send a message to the Copilot and receive a response with action proposals.""" try: result = await copilot_service.chat( db, user_id=current_user.id, message=body.message, session_id=body.session_id, ) except ValueError as exc: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail={"error": {"code": "COPILOT_ERROR", "message": str(exc)}}, ) return ChatResponse(**result) @router.post("/action", response_model=ActionResponse, status_code=status.HTTP_200_OK) async def copilot_action( body: ActionRequest, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): """Execute a user-confirmed action proposed by the Copilot.""" try: result = await copilot_service.execute_confirmed_action( db, action_type=body.action, params=body.params, ) except ValueError as exc: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail={"error": {"code": "INVALID_ACTION", "message": str(exc)}}, ) return ActionResponse(**result) @router.get("/history", response_model=ChatHistoryResponse, status_code=status.HTTP_200_OK) async def copilot_history( db: AsyncSession = Depends(get_db), pagination: dict = Depends(get_pagination), session_id: str | None = Query(None, description="Filter by session ID"), current_user: User = Depends(get_current_user), ): """Get paginated chat history for the current user.""" messages, total = await copilot_service.get_history( db, user_id=current_user.id, page=pagination["page"], page_size=pagination["page_size"], session_id=session_id, ) return ChatHistoryResponse( items=[ChatMessageItem.model_validate(m.to_dict()) for m in messages], total=total, page=pagination["page"], page_size=pagination["page_size"], ) @router.post("/voice", response_model=VoiceResponse, status_code=status.HTTP_200_OK) async def copilot_voice( body: VoiceRequest, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): """Accept audio, transcribe, and return a chat response with action proposals.""" try: result = await copilot_service.voice_chat( db, user_id=current_user.id, audio_b64=body.audio, mime_type=body.mime_type, session_id=body.session_id, ) except ValueError as exc: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail={"error": {"code": "VOICE_ERROR", "message": str(exc)}}, ) return VoiceResponse(**result)