"""Capability gates for Prompt tools in speech-to-speech runtimes.""" from __future__ import annotations from collections.abc import Mapping from services.system_tools import RESERVED_TOOL_NAMES QWEN_PROMPT_TOOL_FEATURES = { "externalTypes": ["http", "mcp", "client"], "builtIns": [ "endConversation", "updateState", "requestHumanHandoff", ], } def realtime_prompt_tool_features(interface_type: str) -> dict[str, list[str]]: """Return the server-trusted runtime capabilities for one provider.""" if interface_type == "qwen-audio-realtime": return { key: list(values) for key, values in QWEN_PROMPT_TOOL_FEATURES.items() } return {"externalTypes": [], "builtIns": []} def validate_runtime_prompt_tools( *, interface_type: str, external_types: set[str], built_in_tools: Mapping[str, object] | None, external_function_names: set[str] | None = None, ) -> None: """Reject stale or inline runtime configurations before opening a socket.""" features = realtime_prompt_tool_features(interface_type) unsupported_external = external_types - set(features["externalTypes"]) reserved_external = set(external_function_names or set()) & RESERVED_TOOL_NAMES aliases = { "end_conversation": "endConversation", "update_state": "updateState", "skip_turn": "skipTurn", "request_human_handoff": "requestHumanHandoff", } requested_built_ins = { aliases.get(str(name), str(name)) for name, config in (built_in_tools or {}).items() if config is not None } unsupported_built_ins = requested_built_ins - set(features["builtIns"]) if unsupported_external or unsupported_built_ins or reserved_external: details = [] if unsupported_external: details.append("外部工具:" + ",".join(sorted(unsupported_external))) if unsupported_built_ins: details.append("内建工具:" + ",".join(sorted(unsupported_built_ins))) if reserved_external: details.append("外部工具使用平台保留名:" + ",".join(sorted(reserved_external))) raise ValueError( f"Realtime 接口 {interface_type or 'unknown'} 不支持 " + ";".join(details) )