feat: 新增外部系统工具加载能力,补充数据库初始化和异常处理
1. 在agent资源解析中新增从数据库加载外部工具的逻辑 2. 新增全局外部适配器注册和数据库外部schema初始化 3. 添加外部系统异常统一处理中间件 4. 在运行时工具解析中追加外部系统工具加载
This commit is contained in:
parent
824471454b
commit
825a5e9769
@ -320,12 +320,26 @@ async def resolve_agent_resource_options(
|
||||
|
||||
if "tools" in fields_to_load:
|
||||
from yuxi.agents.toolkits.service import get_tool_metadata
|
||||
from yuxi.external_systems.infrastructure.container import (
|
||||
create_use_cases_from_db,
|
||||
)
|
||||
from yuxi.external_systems.use_cases.dto.tool import (
|
||||
ListToolOptionsInput,
|
||||
)
|
||||
|
||||
options["tools"] = [
|
||||
_resource_option(tool["slug"], tool.get("name"), tool.get("description"))
|
||||
for tool in get_tool_metadata(category="buildin")
|
||||
if tool.get("slug")
|
||||
]
|
||||
try:
|
||||
use_cases = create_use_cases_from_db(db)
|
||||
output = await use_cases.tool_service.list_tool_options(
|
||||
ListToolOptionsInput(),
|
||||
)
|
||||
options["tools"].extend(output.items)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load external tool options: {e}")
|
||||
if "knowledges" in fields_to_load:
|
||||
from yuxi.knowledge import knowledge_base
|
||||
|
||||
|
||||
@ -96,6 +96,13 @@ def get_tool_instances_by_category(category: str) -> list[Any]:
|
||||
|
||||
async def resolve_configured_runtime_tools(context) -> list[Any]:
|
||||
from yuxi.agents.mcp.service import get_enabled_mcp_tools
|
||||
from yuxi.external_systems.infrastructure.container import (
|
||||
create_use_cases_from_db,
|
||||
)
|
||||
from yuxi.external_systems.use_cases.dto.tool import (
|
||||
BuildRuntimeToolsInput,
|
||||
)
|
||||
from yuxi.storage.postgres.manager import pg_manager
|
||||
|
||||
selected_tools = []
|
||||
selected_tool_names: set[str] = set()
|
||||
@ -130,4 +137,24 @@ async def resolve_configured_runtime_tools(context) -> list[Any]:
|
||||
selected_tools.append(tool)
|
||||
selected_tool_names.add(tool.name)
|
||||
|
||||
# 追加加载外部系统工具(按 slug 匹配 context.tools 中的选择)
|
||||
external_tool_names = [
|
||||
name
|
||||
for name in getattr(context, "tools", None) or []
|
||||
if isinstance(name, str) and name not in selected_tool_names
|
||||
]
|
||||
if external_tool_names:
|
||||
async with pg_manager.get_async_session_context() as db:
|
||||
use_cases = create_use_cases_from_db(db)
|
||||
output = await use_cases.tool_service.build_runtime_tools(
|
||||
BuildRuntimeToolsInput(slugs=external_tool_names),
|
||||
)
|
||||
selected_tools.extend(output.items)
|
||||
selected_tool_names.update(tool.name for tool in output.items)
|
||||
# 构建失败的工具 slug 必须显式记录,避免用户配置的工具被静默丢失
|
||||
if output.failed_slugs:
|
||||
logger.warning(
|
||||
f"Failed to build external runtime tools, skipped: {output.failed_slugs}"
|
||||
)
|
||||
|
||||
return selected_tools
|
||||
|
||||
@ -20,6 +20,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from yuxi.external_systems.exceptions import ExternalSystemError
|
||||
from server.routers import router
|
||||
from server.utils.lifespan import lifespan
|
||||
from server.utils.auth_middleware import is_public_path
|
||||
@ -41,6 +42,27 @@ app = FastAPI(lifespan=lifespan)
|
||||
# 所有业务接口统一挂载到 /api,具体分组在 server.routers 中集中注册。
|
||||
app.include_router(router, prefix="/api")
|
||||
|
||||
|
||||
@app.exception_handler(ExternalSystemError)
|
||||
async def external_system_error_handler(_request: Request, exc: ExternalSystemError) -> JSONResponse:
|
||||
"""外部系统限界上下文异常统一映射为 HTTP 响应。
|
||||
|
||||
- ``status_code`` 来自异常类。
|
||||
- ``details`` 透传到响应体,供前端结构化处理。
|
||||
- 429/503 且 ``details`` 含 ``retry_after`` 时,自动设置 ``Retry-After`` 响应头。
|
||||
"""
|
||||
headers: dict[str, str] = {}
|
||||
if exc.status_code in (429, 503):
|
||||
retry_after = exc.details.get("retry_after")
|
||||
if retry_after is not None:
|
||||
headers["Retry-After"] = str(retry_after)
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={"detail": exc.message, "details": exc.details},
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
|
||||
# CORS 设置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
|
||||
@ -9,6 +9,7 @@ from yuxi.agents.mcp.service import ensure_builtin_mcp_servers_in_db
|
||||
from yuxi.models.providers.service import ensure_builtin_model_providers_in_db
|
||||
from yuxi.services.run_queue_service import close_queue_clients, get_redis_client
|
||||
from yuxi.storage.postgres.manager import pg_manager
|
||||
from yuxi.external import register_adapters
|
||||
from yuxi.knowledge import knowledge_base
|
||||
from yuxi.utils import logger
|
||||
from yuxi.agents.backends.sandbox import init_sandbox_provider, shutdown_sandbox_provider
|
||||
@ -17,6 +18,14 @@ from yuxi import get_version
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
|
||||
# 注册外部系统适配器(避免模块导入副作用)
|
||||
try:
|
||||
register_adapters()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to register external system adapters during startup: {e}")
|
||||
|
||||
|
||||
"""FastAPI lifespan事件管理器"""
|
||||
# 初始化数据库连接
|
||||
try:
|
||||
@ -24,6 +33,7 @@ async def lifespan(app: FastAPI):
|
||||
await pg_manager.create_tables()
|
||||
await pg_manager.ensure_business_schema()
|
||||
await pg_manager.ensure_knowledge_schema()
|
||||
await pg_manager.ensure_external_schema()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize database during startup: {e}")
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user