"""SubAgent 管理路由""" from __future__ import annotations from pydantic import BaseModel, Field from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from fastapi import APIRouter, Depends, HTTPException from server.utils.auth_middleware import get_admin_user, get_db from yuxi.agents.subagents import service from yuxi.storage.postgres.models_business import User from yuxi.utils import logger subagents_router = APIRouter(prefix="/system/subagents", tags=["subagents"]) class SubAgentCreateRequest(BaseModel): slug: str = Field(..., description="稳定标识") name: str = Field(..., description="展示名称") description: str = Field(..., description="描述") system_prompt: str = Field(..., description="系统提示词") tools: list[str] = Field(default_factory=list, description="工具名称列表") model: str | None = Field(None, description="可选的模型覆盖") class SubAgentUpdateRequest(BaseModel): name: str | None = Field(None, description="展示名称") description: str | None = Field(None, description="描述") system_prompt: str | None = Field(None, description="系统提示词") tools: list[str] | None = Field(None, description="工具名称列表") model: str | None = Field(None, description="可选的模型覆盖") class SubAgentStatusRequest(BaseModel): enabled: bool = Field(..., description="是否启用") def _raise_from_value_error(e: ValueError) -> None: message = str(e) status_code = 404 if "不存在" in message else 400 raise HTTPException(status_code=status_code, detail=message) def _raise_internal_error(action: str, error: Exception) -> None: logger.exception("SubAgent %s failed: %s", action, error) raise HTTPException(status_code=500, detail=f"{action}失败") def _is_subagent_slug_duplicate_error(error: IntegrityError) -> bool: raw_message = str(getattr(error, "orig", error)).lower() return "duplicate key" in raw_message and "subagents" in raw_message and "(slug)" in raw_message @subagents_router.get("") async def list_subagents_route( _current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """获取 SubAgent 列表(管理员可读)""" try: items = await service.get_all_subagents(db) return {"success": True, "data": items} except Exception as e: _raise_internal_error("获取列表", e) @subagents_router.get("/{slug}") async def get_subagent_route( slug: str, _current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """获取单个 SubAgent(管理员可读)""" try: item = await service.get_subagent(slug, db) if not item: raise HTTPException(status_code=404, detail=f"SubAgent '{slug}' 不存在") return {"success": True, "data": item} except HTTPException: raise except Exception as e: _raise_internal_error("获取", e) @subagents_router.post("") async def create_subagent_route( payload: SubAgentCreateRequest, current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """创建 SubAgent(管理员)""" try: data = payload.model_dump() item = await service.create_subagent(data, created_by=current_user.username, db=db) return {"success": True, "data": item} except IntegrityError as e: if _is_subagent_slug_duplicate_error(e): raise HTTPException(status_code=409, detail=f"SubAgent '{payload.slug}' 已存在") _raise_internal_error("创建", e) except HTTPException: raise except ValueError as e: _raise_from_value_error(e) except Exception as e: _raise_internal_error("创建", e) @subagents_router.put("/{slug}") async def update_subagent_route( slug: str, payload: SubAgentUpdateRequest, current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """更新 SubAgent(管理员)""" try: data = payload.model_dump(exclude_unset=True) item = await service.update_subagent(slug, data, updated_by=current_user.username, db=db) if not item: raise HTTPException(status_code=404, detail=f"SubAgent '{slug}' 不存在") return {"success": True, "data": item} except ValueError as e: _raise_from_value_error(e) except HTTPException: raise except Exception as e: _raise_internal_error("更新", e) @subagents_router.delete("/{slug}") async def delete_subagent_route( slug: str, _current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """删除 SubAgent(管理员)""" try: deleted = await service.delete_subagent(slug, db=db) if not deleted: raise HTTPException(status_code=404, detail=f"SubAgent '{slug}' 不存在") return {"success": True} except ValueError as e: _raise_from_value_error(e) except HTTPException: raise except Exception as e: _raise_internal_error("删除", e) @subagents_router.put("/{slug}/status") async def update_subagent_status_route( slug: str, payload: SubAgentStatusRequest, current_user: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): """更新 SubAgent 启用状态(管理员)。""" try: item = await service.set_subagent_enabled(slug, payload.enabled, updated_by=current_user.username, db=db) if not item: raise HTTPException(status_code=404, detail=f"SubAgent '{slug}' 不存在") return { "success": True, "data": item, "message": f"SubAgent '{slug}' 已{'添加' if payload.enabled else '移除'}", } except HTTPException: raise except Exception as e: _raise_internal_error("更新状态", e)