feat: Introduce unified tooling system for plugins and MCP
- Added a new `tooling` module to define a unified model for tool declarations, invocations, and execution results, facilitating compatibility between plugins, legacy actions, and MCP tools. - Implemented `ToolProvider` interface for various tool providers including built-in tools, MCP tools, and plugin runtime tools. - Enhanced `MCPManager` and `MCPConnection` to support unified tool invocation and execution results. - Updated `ComponentRegistry` and related classes to accommodate the new tool specifications and descriptions. - Refactored existing components to utilize the new tooling system, ensuring backward compatibility with legacy actions. - Improved error handling and logging for tool invocations across different providers.
This commit is contained in:
@@ -29,6 +29,19 @@ class _RuntimeComponentManagerProtocol(Protocol):
|
||||
|
||||
def _build_api_unavailable_error(self, entry: "APIEntry") -> str: ...
|
||||
|
||||
def _collect_api_reference_matches(
|
||||
self,
|
||||
caller_plugin_id: str,
|
||||
normalized_api_name: str,
|
||||
normalized_version: str,
|
||||
) -> tuple[List[tuple["PluginSupervisor", "APIEntry"]], List[tuple["PluginSupervisor", "APIEntry"]], bool]: ...
|
||||
|
||||
def _collect_api_toggle_reference_matches(
|
||||
self,
|
||||
normalized_name: str,
|
||||
normalized_version: str,
|
||||
) -> List[tuple["PluginSupervisor", "APIEntry"]]: ...
|
||||
|
||||
def _get_supervisor_for_plugin(self, plugin_id: str) -> Optional["PluginSupervisor"]: ...
|
||||
|
||||
def _resolve_api_target(
|
||||
@@ -136,7 +149,10 @@ class RuntimeComponentCapabilityMixin:
|
||||
str: 统一转为大写后的组件类型名。
|
||||
"""
|
||||
|
||||
return str(component_type or "").strip().upper()
|
||||
normalized_component_type = str(component_type or "").strip().upper()
|
||||
if normalized_component_type == "ACTION":
|
||||
return "TOOL"
|
||||
return normalized_component_type
|
||||
|
||||
@classmethod
|
||||
def _is_api_component_type(cls, component_type: str) -> bool:
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
"""插件运行时统一组件查询服务。
|
||||
|
||||
该模块统一从插件运行时的 Host ComponentRegistry 中聚合只读视图,
|
||||
供 HFC/PFC、Planner、ToolExecutor 和运行时能力层查询与调用。
|
||||
供 HFC、ToolExecutor 和运行时能力层查询与调用。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Tuple
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional, Tuple, cast
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.core.tooling import (
|
||||
ToolExecutionContext,
|
||||
ToolExecutionResult,
|
||||
ToolInvocation,
|
||||
ToolSpec,
|
||||
build_tool_detailed_description,
|
||||
)
|
||||
from src.core.types import ActionActivationType, ActionInfo, CommandInfo, ComponentInfo, ComponentType, ToolInfo
|
||||
from src.llm_models.payload_content.tool_option import normalize_tool_option
|
||||
|
||||
@@ -240,12 +247,38 @@ class ComponentQueryService:
|
||||
|
||||
return ToolInfo(
|
||||
name=entry.name,
|
||||
description=entry.description,
|
||||
description=entry.brief_description or entry.description,
|
||||
enabled=bool(entry.enabled),
|
||||
plugin_name=entry.plugin_id,
|
||||
parameters_schema=ComponentQueryService._build_tool_parameters_schema(entry),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_tool_spec(entry: "ToolEntry") -> ToolSpec:
|
||||
"""将运行时 Tool 条目转换为统一工具声明。
|
||||
|
||||
Args:
|
||||
entry: 插件运行时中的 Tool 条目。
|
||||
|
||||
Returns:
|
||||
ToolSpec: 统一工具声明。
|
||||
"""
|
||||
|
||||
parameters_schema = ComponentQueryService._build_tool_parameters_schema(entry)
|
||||
return ToolSpec(
|
||||
name=entry.name,
|
||||
brief_description=entry.brief_description or entry.description or f"工具 {entry.name}",
|
||||
detailed_description=entry.detailed_description or build_tool_detailed_description(parameters_schema),
|
||||
parameters_schema=parameters_schema,
|
||||
provider_name=entry.plugin_id,
|
||||
provider_type="plugin",
|
||||
metadata={
|
||||
"plugin_id": entry.plugin_id,
|
||||
"invoke_method": entry.invoke_method,
|
||||
"legacy_component_type": entry.legacy_component_type,
|
||||
},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _log_duplicate_component(component_type: ComponentType, component_name: str) -> None:
|
||||
"""记录重复组件名称冲突。
|
||||
@@ -475,7 +508,12 @@ class ComponentQueryService:
|
||||
return _executor
|
||||
|
||||
@staticmethod
|
||||
def _build_tool_executor(supervisor: "PluginSupervisor", plugin_id: str, component_name: str) -> ToolExecutor:
|
||||
def _build_tool_executor(
|
||||
supervisor: "PluginSupervisor",
|
||||
plugin_id: str,
|
||||
component_name: str,
|
||||
invoke_method: str = "plugin.invoke_tool",
|
||||
) -> ToolExecutor:
|
||||
"""构造工具执行 RPC 闭包。
|
||||
|
||||
Args:
|
||||
@@ -499,7 +537,7 @@ class ComponentQueryService:
|
||||
|
||||
try:
|
||||
response = await supervisor.invoke_plugin(
|
||||
method="plugin.invoke_tool",
|
||||
method=invoke_method,
|
||||
plugin_id=plugin_id,
|
||||
component_name=component_name,
|
||||
args=function_args,
|
||||
@@ -615,7 +653,162 @@ class ComponentQueryService:
|
||||
if matched_entry is None:
|
||||
return None
|
||||
supervisor, entry = matched_entry
|
||||
return self._build_tool_executor(supervisor, entry.plugin_id, entry.name)
|
||||
tool_entry = cast("ToolEntry", entry)
|
||||
return self._build_tool_executor(supervisor, tool_entry.plugin_id, tool_entry.name, tool_entry.invoke_method)
|
||||
|
||||
def get_llm_available_tool_specs(self) -> Dict[str, ToolSpec]:
|
||||
"""获取当前可供 LLM 使用的统一工具声明集合。
|
||||
|
||||
Returns:
|
||||
Dict[str, ToolSpec]: 工具名到工具声明的映射。
|
||||
"""
|
||||
|
||||
collected_specs: Dict[str, ToolSpec] = {}
|
||||
for _supervisor, entry in self._iter_component_entries(ComponentType.TOOL):
|
||||
if entry.name in collected_specs:
|
||||
self._log_duplicate_component(ComponentType.TOOL, entry.name)
|
||||
continue
|
||||
collected_specs[entry.name] = self._build_tool_spec(entry) # type: ignore[arg-type]
|
||||
return collected_specs
|
||||
|
||||
@staticmethod
|
||||
def _build_tool_invocation_payload(
|
||||
entry: "ToolEntry",
|
||||
invocation: ToolInvocation,
|
||||
context: Optional[ToolExecutionContext],
|
||||
) -> Dict[str, Any]:
|
||||
"""构造插件工具执行时发送给 Runner 的参数。
|
||||
|
||||
Args:
|
||||
entry: 目标工具条目。
|
||||
invocation: 统一工具调用请求。
|
||||
context: 统一工具执行上下文。
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 发往 Runner 的参数字典。
|
||||
"""
|
||||
|
||||
payload = dict(invocation.arguments)
|
||||
if entry.invoke_method == "plugin.invoke_action":
|
||||
stream_id = context.stream_id if context is not None else invocation.stream_id
|
||||
reasoning = context.reasoning if context is not None else invocation.reasoning
|
||||
payload = {
|
||||
**payload,
|
||||
"stream_id": stream_id,
|
||||
"chat_id": stream_id,
|
||||
"reasoning": reasoning,
|
||||
"action_data": dict(invocation.arguments),
|
||||
}
|
||||
return payload
|
||||
|
||||
@staticmethod
|
||||
def _parse_tool_invoke_result(
|
||||
entry: "ToolEntry",
|
||||
result: Any,
|
||||
) -> ToolExecutionResult:
|
||||
"""将插件组件返回值转换为统一工具执行结果。
|
||||
|
||||
Args:
|
||||
entry: 目标工具条目。
|
||||
result: 插件组件原始返回值。
|
||||
|
||||
Returns:
|
||||
ToolExecutionResult: 统一执行结果。
|
||||
"""
|
||||
|
||||
if isinstance(result, dict):
|
||||
success = bool(result.get("success", True))
|
||||
content = str(result.get("content", result.get("message", "")) or "").strip()
|
||||
error_message = ""
|
||||
if not success:
|
||||
error_message = str(result.get("error", result.get("message", "插件工具执行失败")) or "").strip()
|
||||
return ToolExecutionResult(
|
||||
tool_name=entry.name,
|
||||
success=success,
|
||||
content=content,
|
||||
error_message=error_message,
|
||||
structured_content=result,
|
||||
metadata={"plugin_id": entry.plugin_id},
|
||||
)
|
||||
|
||||
if isinstance(result, (list, tuple)) and result:
|
||||
if isinstance(result[0], bool):
|
||||
success = bool(result[0])
|
||||
message = "" if len(result) < 2 or result[1] is None else str(result[1]).strip()
|
||||
return ToolExecutionResult(
|
||||
tool_name=entry.name,
|
||||
success=success,
|
||||
content=message if success else "",
|
||||
error_message="" if success else message,
|
||||
structured_content=list(result),
|
||||
metadata={"plugin_id": entry.plugin_id},
|
||||
)
|
||||
|
||||
normalized_content = "" if result is None else str(result).strip()
|
||||
return ToolExecutionResult(
|
||||
tool_name=entry.name,
|
||||
success=True,
|
||||
content=normalized_content,
|
||||
structured_content=result,
|
||||
metadata={"plugin_id": entry.plugin_id},
|
||||
)
|
||||
|
||||
async def invoke_tool_as_tool(
|
||||
self,
|
||||
invocation: ToolInvocation,
|
||||
context: Optional[ToolExecutionContext] = None,
|
||||
) -> ToolExecutionResult:
|
||||
"""按统一工具语义执行插件工具。
|
||||
|
||||
Args:
|
||||
invocation: 统一工具调用请求。
|
||||
context: 执行上下文。
|
||||
|
||||
Returns:
|
||||
ToolExecutionResult: 统一工具执行结果。
|
||||
"""
|
||||
|
||||
matched_entry = self._get_unique_component_entry(ComponentType.TOOL, invocation.tool_name)
|
||||
if matched_entry is None:
|
||||
return ToolExecutionResult(
|
||||
tool_name=invocation.tool_name,
|
||||
success=False,
|
||||
error_message=f"未找到插件工具:{invocation.tool_name}",
|
||||
)
|
||||
|
||||
supervisor, entry = matched_entry
|
||||
tool_entry = cast("ToolEntry", entry)
|
||||
invoke_payload = self._build_tool_invocation_payload(tool_entry, invocation, context)
|
||||
|
||||
try:
|
||||
response = await supervisor.invoke_plugin(
|
||||
method=tool_entry.invoke_method,
|
||||
plugin_id=tool_entry.plugin_id,
|
||||
component_name=tool_entry.name,
|
||||
args=invoke_payload,
|
||||
timeout_ms=30000,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"运行时工具 {tool_entry.plugin_id}.{tool_entry.name} 执行失败: {exc}", exc_info=True)
|
||||
return ToolExecutionResult(
|
||||
tool_name=tool_entry.name,
|
||||
success=False,
|
||||
error_message=str(exc),
|
||||
metadata={"plugin_id": tool_entry.plugin_id},
|
||||
)
|
||||
|
||||
payload = response.payload if isinstance(response.payload, dict) else {}
|
||||
transport_success = bool(payload.get("success", False))
|
||||
result = payload.get("result")
|
||||
if not transport_success:
|
||||
return ToolExecutionResult(
|
||||
tool_name=tool_entry.name,
|
||||
success=False,
|
||||
error_message="" if result is None else str(result),
|
||||
structured_content=result,
|
||||
metadata={"plugin_id": tool_entry.plugin_id},
|
||||
)
|
||||
return self._parse_tool_invoke_result(tool_entry, result)
|
||||
|
||||
def get_llm_available_tools(self) -> Dict[str, ToolInfo]:
|
||||
"""获取当前可供 LLM 选择的工具集合。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Host-side ComponentRegistry
|
||||
"""Host 侧组件注册表。
|
||||
|
||||
对齐旧系统 component_registry.py 的核心能力:
|
||||
- 按类型注册组件(action / command / tool / event_handler / hook_handler / message_gateway)
|
||||
@@ -16,6 +16,7 @@ import contextlib
|
||||
import re
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.core.tooling import build_tool_detailed_description
|
||||
|
||||
logger = get_logger("plugin_runtime.host.component_registry")
|
||||
|
||||
@@ -89,11 +90,81 @@ class ToolEntry(ComponentEntry):
|
||||
"""Tool 组件条目"""
|
||||
|
||||
def __init__(self, name: str, component_type: str, plugin_id: str, metadata: Dict[str, Any]) -> None:
|
||||
self.description: str = metadata.get("description", "")
|
||||
self.description: str = str(metadata.get("description", "") or "").strip()
|
||||
self.brief_description: str = str(
|
||||
metadata.get("brief_description", self.description) or self.description or f"工具 {name}"
|
||||
).strip()
|
||||
self.parameters: List[Dict[str, Any]] = metadata.get("parameters", [])
|
||||
self.parameters_raw: Dict[str, Any] | List[Dict[str, Any]] = metadata.get("parameters_raw", {})
|
||||
detailed_description = str(metadata.get("detailed_description", "") or "").strip()
|
||||
self.detailed_description: str = detailed_description
|
||||
self.invoke_method: str = str(metadata.get("invoke_method", "plugin.invoke_tool") or "plugin.invoke_tool").strip()
|
||||
self.legacy_component_type: str = str(metadata.get("legacy_component_type", "") or "").strip()
|
||||
super().__init__(name, component_type, plugin_id, metadata)
|
||||
|
||||
if not self.detailed_description:
|
||||
parameters_schema = self._get_parameters_schema()
|
||||
self.detailed_description = build_tool_detailed_description(parameters_schema)
|
||||
|
||||
def _get_parameters_schema(self) -> Dict[str, Any] | None:
|
||||
"""获取当前工具条目的对象级参数 Schema。
|
||||
|
||||
Returns:
|
||||
Dict[str, Any] | None: 归一化后的参数 Schema。
|
||||
"""
|
||||
|
||||
if isinstance(self.parameters_raw, dict) and self.parameters_raw:
|
||||
if self.parameters_raw.get("type") == "object" or "properties" in self.parameters_raw:
|
||||
return dict(self.parameters_raw)
|
||||
|
||||
required_names: List[str] = []
|
||||
normalized_properties: Dict[str, Any] = {}
|
||||
for property_name, property_schema in self.parameters_raw.items():
|
||||
if not isinstance(property_schema, dict):
|
||||
continue
|
||||
property_schema_copy = dict(property_schema)
|
||||
if bool(property_schema_copy.pop("required", False)):
|
||||
required_names.append(str(property_name))
|
||||
normalized_properties[str(property_name)] = property_schema_copy
|
||||
|
||||
schema: Dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": normalized_properties,
|
||||
}
|
||||
if required_names:
|
||||
schema["required"] = required_names
|
||||
return schema
|
||||
|
||||
if isinstance(self.parameters, list) and self.parameters:
|
||||
properties: Dict[str, Any] = {}
|
||||
required_names: List[str] = []
|
||||
for parameter in self.parameters:
|
||||
if not isinstance(parameter, dict):
|
||||
continue
|
||||
parameter_name = str(parameter.get("name", "") or "").strip()
|
||||
if not parameter_name:
|
||||
continue
|
||||
if bool(parameter.get("required", False)):
|
||||
required_names.append(parameter_name)
|
||||
properties[parameter_name] = {
|
||||
key: value
|
||||
for key, value in parameter.items()
|
||||
if key not in {"name", "required", "param_type"}
|
||||
}
|
||||
properties[parameter_name]["type"] = str(
|
||||
parameter.get("type", parameter.get("param_type", "string")) or "string"
|
||||
)
|
||||
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": properties,
|
||||
}
|
||||
if required_names:
|
||||
schema["required"] = required_names
|
||||
return schema
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class EventHandlerEntry(ComponentEntry):
|
||||
"""EventHandler 组件条目"""
|
||||
@@ -282,7 +353,7 @@ class MessageGatewayEntry(ComponentEntry):
|
||||
|
||||
|
||||
class ComponentRegistry:
|
||||
"""Host-side 组件注册表
|
||||
"""Host 侧组件注册表。
|
||||
|
||||
由 Supervisor 在收到 plugin.register_components 时调用。
|
||||
供业务层查询可用组件、匹配命令、调度 action/event 等。
|
||||
@@ -300,6 +371,86 @@ class ComponentRegistry:
|
||||
# 按插件索引
|
||||
self._by_plugin: Dict[str, List[ComponentEntry]] = {}
|
||||
|
||||
@staticmethod
|
||||
def _convert_action_metadata_to_tool_metadata(
|
||||
name: str,
|
||||
metadata: Dict[str, Any],
|
||||
) -> Dict[str, Any]:
|
||||
"""将旧 Action 元数据转换为统一 Tool 元数据。
|
||||
|
||||
Args:
|
||||
name: 组件名称。
|
||||
metadata: Action 原始元数据。
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 转换后的 Tool 元数据。
|
||||
"""
|
||||
|
||||
action_parameters = metadata.get("action_parameters")
|
||||
parameters_schema: Dict[str, Any] | None = None
|
||||
if isinstance(action_parameters, dict) and action_parameters:
|
||||
properties: Dict[str, Any] = {}
|
||||
for parameter_name, parameter_description in action_parameters.items():
|
||||
normalized_name = str(parameter_name or "").strip()
|
||||
if not normalized_name:
|
||||
continue
|
||||
properties[normalized_name] = {
|
||||
"type": "string",
|
||||
"description": str(parameter_description or "").strip() or "兼容旧 Action 参数",
|
||||
}
|
||||
if properties:
|
||||
parameters_schema = {
|
||||
"type": "object",
|
||||
"properties": properties,
|
||||
}
|
||||
|
||||
detailed_parts: List[str] = []
|
||||
if parameters_schema is not None:
|
||||
parameter_description = build_tool_detailed_description(parameters_schema)
|
||||
if parameter_description:
|
||||
detailed_parts.append(parameter_description)
|
||||
|
||||
action_require = [
|
||||
str(item).strip()
|
||||
for item in (metadata.get("action_require") or [])
|
||||
if str(item).strip()
|
||||
]
|
||||
if action_require:
|
||||
detailed_parts.append("使用建议:\n" + "\n".join(f"- {item}" for item in action_require))
|
||||
|
||||
associated_types = [
|
||||
str(item).strip()
|
||||
for item in (metadata.get("associated_types") or [])
|
||||
if str(item).strip()
|
||||
]
|
||||
if associated_types:
|
||||
detailed_parts.append(f"适用消息类型:{'、'.join(associated_types)}。")
|
||||
|
||||
activation_type = str(metadata.get("activation_type", "always") or "always").strip()
|
||||
activation_keywords = [
|
||||
str(item).strip()
|
||||
for item in (metadata.get("activation_keywords") or [])
|
||||
if str(item).strip()
|
||||
]
|
||||
activation_lines = [f"兼容旧 Action 激活方式:{activation_type}。"]
|
||||
if activation_keywords:
|
||||
activation_lines.append(f"激活关键词:{'、'.join(activation_keywords)}。")
|
||||
if str(metadata.get("action_prompt", "") or "").strip():
|
||||
activation_lines.append(f"原始 Action 提示语:{str(metadata['action_prompt']).strip()}。")
|
||||
detailed_parts.append("\n".join(activation_lines))
|
||||
|
||||
brief_description = str(metadata.get("brief_description", metadata.get("description", "") or f"工具 {name}")).strip()
|
||||
return {
|
||||
**metadata,
|
||||
"description": brief_description,
|
||||
"brief_description": brief_description,
|
||||
"detailed_description": "\n\n".join(part for part in detailed_parts if part).strip(),
|
||||
"parameters_raw": parameters_schema or {},
|
||||
"invoke_method": "plugin.invoke_action",
|
||||
"legacy_action": True,
|
||||
"legacy_component_type": "ACTION",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _normalize_component_type(component_type: str) -> ComponentTypes:
|
||||
"""规范化组件类型输入。
|
||||
@@ -338,18 +489,20 @@ class ComponentRegistry:
|
||||
"""
|
||||
try:
|
||||
normalized_type = self._normalize_component_type(component_type)
|
||||
normalized_metadata = dict(metadata)
|
||||
if normalized_type == ComponentTypes.ACTION:
|
||||
comp = ActionEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
normalized_metadata = self._convert_action_metadata_to_tool_metadata(name, normalized_metadata)
|
||||
comp = ToolEntry(name, ComponentTypes.TOOL.value, plugin_id, normalized_metadata)
|
||||
elif normalized_type == ComponentTypes.COMMAND:
|
||||
comp = CommandEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
comp = CommandEntry(name, normalized_type.value, plugin_id, normalized_metadata)
|
||||
elif normalized_type == ComponentTypes.TOOL:
|
||||
comp = ToolEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
comp = ToolEntry(name, normalized_type.value, plugin_id, normalized_metadata)
|
||||
elif normalized_type == ComponentTypes.EVENT_HANDLER:
|
||||
comp = EventHandlerEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
comp = EventHandlerEntry(name, normalized_type.value, plugin_id, normalized_metadata)
|
||||
elif normalized_type == ComponentTypes.HOOK_HANDLER:
|
||||
comp = HookHandlerEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
comp = HookHandlerEntry(name, normalized_type.value, plugin_id, normalized_metadata)
|
||||
elif normalized_type == ComponentTypes.MESSAGE_GATEWAY:
|
||||
comp = MessageGatewayEntry(name, normalized_type.value, plugin_id, metadata)
|
||||
comp = MessageGatewayEntry(name, normalized_type.value, plugin_id, normalized_metadata)
|
||||
else:
|
||||
raise ValueError(f"组件类型 {component_type} 不存在")
|
||||
except ValueError:
|
||||
|
||||
48
src/plugin_runtime/tool_provider.py
Normal file
48
src/plugin_runtime/tool_provider.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""插件运行时工具 Provider。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolProvider, ToolSpec
|
||||
|
||||
from .component_query import component_query_service
|
||||
|
||||
|
||||
class PluginToolProvider(ToolProvider):
|
||||
"""将插件 Tool 与兼容旧 Action 暴露为统一工具 Provider。"""
|
||||
|
||||
provider_name = "plugin_runtime"
|
||||
provider_type = "plugin"
|
||||
|
||||
async def list_tools(self) -> list[ToolSpec]:
|
||||
"""列出插件运行时当前可用的工具声明。"""
|
||||
|
||||
return list(component_query_service.get_llm_available_tool_specs().values())
|
||||
|
||||
async def invoke(
|
||||
self,
|
||||
invocation: ToolInvocation,
|
||||
context: Optional[ToolExecutionContext] = None,
|
||||
) -> ToolExecutionResult:
|
||||
"""执行插件工具或兼容旧 Action 的工具调用。
|
||||
|
||||
Args:
|
||||
invocation: 工具调用请求。
|
||||
context: 执行上下文。
|
||||
|
||||
Returns:
|
||||
ToolExecutionResult: 工具执行结果。
|
||||
"""
|
||||
|
||||
return await component_query_service.invoke_tool_as_tool(
|
||||
invocation=invocation,
|
||||
context=context,
|
||||
)
|
||||
|
||||
async def close(self) -> None:
|
||||
"""关闭 Provider。
|
||||
|
||||
插件运行时工具 Provider 不持有独立资源。
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user