feat: organize tools and enable qwen realtime calls
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user