feat: organize tools and enable qwen realtime calls

This commit is contained in:
Xin Wang
2026-08-14 00:15:00 +08:00
parent eb111a3890
commit 5be21f5ccc
71 changed files with 5771 additions and 2026 deletions
+46 -6
View File
@@ -14,6 +14,7 @@ from schemas import McpServerOut, McpServerUpsert, McpSyncResult, ToolOut
from services.auth import require_admin
from services.masking import mask_secrets, merge_secrets
from services.tool_resources import tool_to_out
from services.system_tools import RESERVED_TOOL_NAMES
from services.tools import McpClientError, McpToolClient
from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError
@@ -27,6 +28,47 @@ router = APIRouter(
)
TEMPLATE_PLACEHOLDER = re.compile(r"{{\s*([A-Za-z][A-Za-z0-9_]*)\s*}}")
INPUT_PLACEHOLDER = re.compile(r"{{\s*input__([A-Za-z][A-Za-z0-9_]*)\s*}}")
def _validate_template_placeholders(body: McpServerUpsert) -> None:
source = " ".join([body.url, *[str(value) for value in body.headers.values()]])
invalid = sorted(
name
for name in set(TEMPLATE_PLACEHOLDER.findall(source))
if not name.startswith(("input__", "system__"))
)
if invalid:
raise HTTPException(
400,
"MCP 连接不能直接引用 Assistant 变量;"
"请改为 {{input__name}}: " + ", ".join(invalid),
)
def _template_inputs(server: McpServer) -> list[dict]:
"""Expose declared MCP connection placeholders to every synced tool."""
config = server.config or {}
source = " ".join(
[
server.url,
*[str(value) for value in (config.get("headers") or {}).values()],
]
)
return [
{
"name": name,
"type": "string",
"location": "body",
"description": "MCP 连接模板输入",
"required": True,
}
for name in sorted(set(INPUT_PLACEHOLDER.findall(source)))
]
def _runtime_server(server: McpServer) -> RuntimeMcpServer:
config = server.config or {}
secrets = server.secrets or {}
@@ -79,6 +121,7 @@ def _apply_body(
server: McpServer,
body: McpServerUpsert,
) -> None:
_validate_template_placeholders(body)
server.name = body.name.strip()
server.description = body.description.strip()
server.transport = body.transport
@@ -109,7 +152,7 @@ async def _available_function_name(
base = _function_slug(f"mcp_{server.name}_{remote_name}")
candidate = base
suffix = 2
while (
while candidate in RESERVED_TOOL_NAMES or (
await session.execute(
select(Tool.id).where(Tool.function_name == candidate).limit(1)
)
@@ -231,22 +274,19 @@ async def sync_mcp_tools(
previous_config = (tool.definition or {}).get("config") or {}
tool.description = str(remote.get("description") or tool.description)
tool.definition = {
"schema_version": 1,
"schema_version": 2,
"type": "mcp",
"config": {
"remote_tool_name": remote_name,
"input_schema": remote.get("input_schema") or {},
"schema_hash": remote.get("schema_hash") or "",
"template_inputs": _template_inputs(server),
"allow_interruptions": previous_config.get(
"allow_interruptions", True
),
"execution_mode": previous_config.get(
"execution_mode", "immediate"
),
"dynamic_variable_assignments": previous_config.get(
"dynamic_variable_assignments"
)
or {},
},
}
synchronized.append(tool)