ForcePilot/backend/server/routers/subagent_router.py
2026-05-29 22:19:58 +08:00

173 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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)