refactor: unify system tools as resources
This commit is contained in:
@@ -17,6 +17,7 @@ from schemas import AssistantOut, AssistantUpsert
|
||||
from services.auth import require_admin
|
||||
from services.masking import mask, resolve_incoming_key
|
||||
from services.node_specs import graph_references, normalize_graph, validate_graph
|
||||
from services.system_tools import system_tool_kind
|
||||
from services.tool_policy import policy_for_tool
|
||||
from services.workflow_engine import WorkflowEngine
|
||||
from sqlalchemy import select
|
||||
@@ -58,13 +59,6 @@ def _validate_workflow(body: AssistantUpsert) -> None:
|
||||
f"Agent 节点 {node_id} 授权了未声明变量:"
|
||||
+ ",".join(unknown)
|
||||
)
|
||||
if (
|
||||
"update_state" in (data.get("systemTools") or [])
|
||||
and not authorized
|
||||
):
|
||||
errors.append(
|
||||
f"Agent 节点 {node_id} 启用更新状态工具时必须授权至少一个变量"
|
||||
)
|
||||
if errors:
|
||||
raise HTTPException(400, "工作流校验失败:" + ";".join(errors))
|
||||
# Graph settings are the source of truth. The flat flag is only a session
|
||||
@@ -141,6 +135,60 @@ async def _validate_workflow_references(
|
||||
raise HTTPException(400, f"Workflow 引用了无效知识库:{knowledge_id}")
|
||||
|
||||
|
||||
async def _validate_system_tool_selection(
|
||||
session: AsyncSession,
|
||||
body: AssistantUpsert,
|
||||
) -> None:
|
||||
"""Validate System resources in the same selection path as every other tool."""
|
||||
selected_ids = list(dict.fromkeys(body.tool_ids))
|
||||
if not selected_ids or body.type not in {"prompt", "workflow"}:
|
||||
return
|
||||
rows = (
|
||||
await session.execute(select(Tool).where(Tool.id.in_(selected_ids)))
|
||||
).scalars().all()
|
||||
tools_by_id = {tool.id: tool for tool in rows if tool.status == "active"}
|
||||
system_kinds = {
|
||||
tool.id: system_tool_kind(tool.definition or {})
|
||||
for tool in rows
|
||||
if tool.status == "active" and tool.type == "system"
|
||||
}
|
||||
invalid = [tool_id for tool_id, kind in system_kinds.items() if kind is None]
|
||||
if invalid:
|
||||
raise HTTPException(400, "系统工具配置无效: " + ", ".join(invalid))
|
||||
|
||||
if body.type == "prompt":
|
||||
if body.runtime_mode == "realtime" and system_kinds:
|
||||
raise HTTPException(400, "Prompt Realtime 模式暂不支持系统工具")
|
||||
if (
|
||||
"update_state" in system_kinds.values()
|
||||
and not body.dynamic_variable_definitions
|
||||
):
|
||||
raise HTTPException(400, "启用更新状态工具前必须声明至少一个动态变量")
|
||||
return
|
||||
|
||||
engine = WorkflowEngine(body.graph)
|
||||
for node_id, node in engine.nodes.items():
|
||||
node_type = node.get("type")
|
||||
data = node.get("data") or {}
|
||||
if node_type == "agent":
|
||||
stage = engine.agent_stage_config(node_id)
|
||||
kinds = {
|
||||
system_kinds[tool_id]
|
||||
for tool_id in stage.tool_ids
|
||||
if tool_id in system_kinds
|
||||
}
|
||||
if "update_state" in kinds and not stage.state_variable_names:
|
||||
raise HTTPException(
|
||||
400,
|
||||
f"Agent 节点 {node_id} 启用更新状态工具时必须授权至少一个变量",
|
||||
)
|
||||
elif node_type == "action":
|
||||
tool_id = str(data.get("toolId") or "")
|
||||
tool = tools_by_id.get(tool_id)
|
||||
if tool and tool.type == "system":
|
||||
raise HTTPException(400, f"Action 节点 {node_id} 不能调用系统工具")
|
||||
|
||||
|
||||
async def _validate_startup_actions(
|
||||
session: AsyncSession,
|
||||
body: AssistantUpsert,
|
||||
@@ -312,7 +360,6 @@ async def _to_out(session: AsyncSession, assistant: Assistant) -> AssistantOut:
|
||||
enable_interrupt=assistant.enable_interrupt,
|
||||
turn_config=assistant.turn_config or {},
|
||||
startup=assistant.startup or {},
|
||||
system_tools=assistant.system_tools or [],
|
||||
vision_enabled=assistant.vision_enabled,
|
||||
vision_model_resource_id=assistant.vision_model_resource_id,
|
||||
model_resource_ids=await _resource_ids(session, assistant.id),
|
||||
@@ -347,6 +394,7 @@ async def create_assistant(
|
||||
):
|
||||
_validate_workflow(body)
|
||||
await _validate_workflow_references(session, body)
|
||||
await _validate_system_tool_selection(session, body)
|
||||
await _validate_startup_actions(session, body)
|
||||
await _validate_vision_model(session, body)
|
||||
await _validate_knowledge_base(session, body)
|
||||
@@ -389,7 +437,6 @@ async def duplicate_assistant(
|
||||
enable_interrupt=source.enable_interrupt,
|
||||
turn_config=dict(source.turn_config or {}),
|
||||
startup=dict(source.startup or {}),
|
||||
system_tools=list(source.system_tools or []),
|
||||
vision_enabled=source.vision_enabled,
|
||||
vision_model_resource_id=source.vision_model_resource_id,
|
||||
knowledge_base_id=source.knowledge_base_id,
|
||||
@@ -423,6 +470,7 @@ async def update_assistant(
|
||||
raise HTTPException(404, "助手不存在")
|
||||
_validate_workflow(body)
|
||||
await _validate_workflow_references(session, body)
|
||||
await _validate_system_tool_selection(session, body)
|
||||
await _validate_startup_actions(session, body)
|
||||
await _validate_vision_model(session, body)
|
||||
await _validate_knowledge_base(session, body)
|
||||
|
||||
Reference in New Issue
Block a user