import logging from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from db.models import AgentConfig from db.session import get_session from models.requests import AgentConfigCreateRequest from models.responses import ( AgentConfigDeleteResponse, AgentConfigInfo, AgentConfigListResponse, ) from services import config_service, intent_service logger = logging.getLogger(__name__) router = APIRouter(prefix="/configs", tags=["configs"]) def _to_info(cfg: AgentConfig, intent_code: str = "", intent_name: str = "") -> AgentConfigInfo: return AgentConfigInfo( id=cfg.id, intent_id=cfg.intent_id, intent_code=intent_code, intent_name=intent_name, version=cfg.version, name=cfg.name, system_prompt=cfg.system_prompt, rules_text=cfg.rules_text or "", exit_conditions_text=cfg.exit_conditions_text or "", is_active=cfg.is_active, created_at=cfg.created_at.isoformat(), ) async def _resolve_intent_meta(session: AsyncSession, intent_id: int | None) -> tuple[str, str]: if intent_id is None: return "", "" from db.models import Intent intent = await session.get(Intent, intent_id) if intent is None: return "", "" return intent.code, intent.name @router.get("", response_model=AgentConfigListResponse) async def list_configs( intent_code: str = Query(..., description="Код ветки (обязателен): фильтр версий по ветке"), session: AsyncSession = Depends(get_session), ): intent = await intent_service.get_intent_by_code(session, intent_code) if intent is None: raise HTTPException(status_code=404, detail=f"Intent {intent_code!r} not found") configs = await config_service.list_configs_for_intent(session, intent.id) return AgentConfigListResponse( configs=[_to_info(c, intent.code, intent.name) for c in configs], total=len(configs), ) @router.get("/active", response_model=AgentConfigInfo) async def get_active( intent_code: str = Query(..., description="Код ветки"), session: AsyncSession = Depends(get_session), ): intent = await intent_service.get_intent_by_code(session, intent_code) if intent is None: raise HTTPException(status_code=404, detail=f"Intent {intent_code!r} not found") cfg = await config_service.get_active_config_for_intent(session, intent.id) if cfg is None: raise HTTPException(status_code=404, detail=f"No active config for intent {intent_code!r}") return _to_info(cfg, intent.code, intent.name) @router.get("/{config_id}", response_model=AgentConfigInfo) async def get_config(config_id: int, session: AsyncSession = Depends(get_session)): cfg = await config_service.get_config(session, config_id) if cfg is None: raise HTTPException(status_code=404, detail="Config not found") code, name = await _resolve_intent_meta(session, cfg.intent_id) return _to_info(cfg, code, name) @router.post("", response_model=AgentConfigInfo) async def create_config( req: AgentConfigCreateRequest, session: AsyncSession = Depends(get_session), ): from db.models import Intent intent = await session.get(Intent, req.intent_id) if intent is None: raise HTTPException(status_code=404, detail=f"Intent {req.intent_id} not found") cfg = await config_service.create_config( session=session, intent_id=req.intent_id, system_prompt=req.system_prompt, rules_text=req.rules_text, exit_conditions_text=req.exit_conditions_text, name=req.name, activate=req.activate, ) return _to_info(cfg, intent.code, intent.name) @router.post("/{config_id}/activate", response_model=AgentConfigInfo) async def activate_config(config_id: int, session: AsyncSession = Depends(get_session)): cfg = await config_service.activate_config(session, config_id) if cfg is None: raise HTTPException(status_code=404, detail="Config not found or has no intent") code, name = await _resolve_intent_meta(session, cfg.intent_id) return _to_info(cfg, code, name) @router.delete("/{config_id}", response_model=AgentConfigDeleteResponse) async def delete_config(config_id: int, session: AsyncSession = Depends(get_session)): ok, reason = await config_service.delete_config(session, config_id) if not ok: if reason == "not_found": raise HTTPException(status_code=404, detail="Config not found") if reason == "active": raise HTTPException( status_code=400, detail="Нельзя удалить активную версию — сначала активируйте другую", ) return AgentConfigDeleteResponse(ok=True)