"""AI Copilot routes — query, execute, history.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.auth import check_permission from app.core.db import get_db from app.deps import get_current_user from app.schemas.ai_copilot import ( CopilotQueryRequest, CopilotExecuteRequest, ) from app.services import ai_copilot_service router = APIRouter(prefix="/api/v1/ai/copilot", tags=["ai-copilot"]) @router.post("/query") async def copilot_query( body: CopilotQueryRequest, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Process a natural language query and return proposed actions. Returns proposed_actions array for user confirmation. Does NOT execute any actions — user must call /execute to confirm. """ tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) result = await ai_copilot_service.process_query( db, tenant_id, user_id, query=body.query, conversation_id=body.conversation_id, context=body.context, ) if "error" in result and result.get("status_code") == 404: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={"detail": result["error"], "code": "conversation_not_found"}, ) return result @router.post("/execute") async def copilot_execute( body: CopilotExecuteRequest, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Execute a proposed action after user confirmation. RBAC is enforced — the user must have permission for the action. Returns 200 with execution result or 403 if RBAC blocks. """ tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) role = current_user.get("role", "viewer") result = await ai_copilot_service.execute_action( db, tenant_id, user_id, role, conversation_id=body.conversation_id, action=body.action.model_dump(), ) if result.get("status_code") == 404: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={"detail": result.get("error", "Not found"), "code": "not_found"}, ) if result.get("status_code") == 403: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={"detail": result.get("error", "Insufficient permissions"), "code": "forbidden"}, ) return result @router.get("/history") async def copilot_history( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Get paginated conversation history for the current user.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) return await ai_copilot_service.get_history( db, tenant_id, user_id, page=page, page_size=page_size, )