import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from db.models import Intent, IntentStep from db.session import get_session from models.requests import ( IntentDocumentsUpdateRequest, IntentStepUpdateRequest, IntentToggleRequest, ) from models.responses import ( IntentDocumentsResponse, IntentInfo, IntentListResponse, IntentStepInfo, IntentStepListResponse, ) from services import config_service, intent_document_service, intent_service, intent_step_service logger = logging.getLogger(__name__) router = APIRouter(prefix="/intents", tags=["intents"]) async def _to_info(session: AsyncSession, intent: Intent) -> IntentInfo: active_cfg = await config_service.get_active_config_for_intent(session, intent.id) return IntentInfo( id=intent.id, code=intent.code, name=intent.name, description=intent.description or "", is_enabled=intent.is_enabled, order_index=intent.order_index, active_config_id=active_cfg.id if active_cfg else None, active_config_version=active_cfg.version if active_cfg else None, ) @router.get("", response_model=IntentListResponse) async def list_intents(session: AsyncSession = Depends(get_session)): intents = await intent_service.list_intents(session) infos = [await _to_info(session, i) for i in intents] return IntentListResponse(intents=infos, total=len(infos)) @router.get("/{code}", response_model=IntentInfo) async def get_intent(code: str, session: AsyncSession = Depends(get_session)): intent = await intent_service.get_intent_by_code(session, code) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") return await _to_info(session, intent) @router.patch("/{code}", response_model=IntentInfo) async def toggle_intent( code: str, req: IntentToggleRequest, session: AsyncSession = Depends(get_session), ): intent = await intent_service.set_intent_enabled(session, code, req.is_enabled) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") return await _to_info(session, intent) def _step_to_info(step: IntentStep, intent_code: str) -> IntentStepInfo: return IntentStepInfo( id=step.id, intent_id=step.intent_id, intent_code=intent_code, code=step.code, name=step.name, order_index=step.order_index, system_prompt=step.system_prompt or "", allowed_next=intent_step_service.parse_allowed_next(step), guards=intent_step_service.parse_guards(step), updated_at=step.updated_at.isoformat(), ) @router.get("/{code}/steps", response_model=IntentStepListResponse) async def list_intent_steps(code: str, session: AsyncSession = Depends(get_session)): intent = await intent_service.get_intent_by_code(session, code) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") steps = await intent_step_service.list_steps_for_intent(session, intent.id) return IntentStepListResponse( intent_code=intent.code, steps=[_step_to_info(s, intent.code) for s in steps], total=len(steps), ) @router.patch("/{code}/steps/{step_code}", response_model=IntentStepInfo) async def update_intent_step( code: str, step_code: str, req: IntentStepUpdateRequest, session: AsyncSession = Depends(get_session), ): intent = await intent_service.get_intent_by_code(session, code) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") step = await intent_step_service.get_step_by_code(session, intent.id, step_code) if step is None: raise HTTPException(status_code=404, detail="Step not found") updated = await intent_step_service.update_step( session, step, name=req.name, system_prompt=req.system_prompt, allowed_next=req.allowed_next, guards=req.guards, ) return _step_to_info(updated, intent.code) @router.get("/{code}/documents", response_model=IntentDocumentsResponse) async def list_intent_documents(code: str, session: AsyncSession = Depends(get_session)): intent = await intent_service.get_intent_by_code(session, code) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") document_ids = await intent_document_service.list_documents_for_intent(session, intent.id) return IntentDocumentsResponse(intent_code=intent.code, document_ids=document_ids) @router.put("/{code}/documents", response_model=IntentDocumentsResponse) async def set_intent_documents( code: str, req: IntentDocumentsUpdateRequest, session: AsyncSession = Depends(get_session), ): intent = await intent_service.get_intent_by_code(session, code) if intent is None: raise HTTPException(status_code=404, detail="Intent not found") document_ids = await intent_document_service.set_documents_for_intent( session, intent.id, req.document_ids, ) return IntentDocumentsResponse(intent_code=intent.code, document_ids=document_ids)