ForcePilot/backend/server/routers/subagent_router.py
Wenjie Zhang c772e5ca3a refactor: Agent 运行时架构重构 - 移除 RuntimeConfigMiddleware,统一上下文准备与工具解析
- 删除 runtime_config_middleware.py,将功能合并到:
  - prepare_agent_runtime_context(): 统一上下文准备入口
  - resolve_configured_runtime_tools(): 运行时工具解析
- context.py: 规范化函数重命名 (_names → _keys),重构 config 加载流程
- chatbot/deep_agent graph: 集成新上下文准备流程,移除 RuntimeConfigMiddleware 引用
- skills_middleware: 抽取 normalize_string_list 为共享工具函数
- subagent_service: get_subagents_from_names → get_subagents_from_slugs,支持 slug 查询
- 对应更新 repositories/services/routers 适配新接口
2026-05-26 17:40:17 +08:00

173 lines
5.9 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.services import subagent_service as 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)