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:
@@ -1,124 +1,163 @@
|
||||
"""
|
||||
MaiSaka built-in tool definitions.
|
||||
"""
|
||||
"""Maisaka 内置工具声明。"""
|
||||
|
||||
from typing import List
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from src.llm_models.payload_content.tool_option import ToolOption, ToolParamType
|
||||
from src.core.tooling import ToolSpec, build_tool_detailed_description
|
||||
from src.llm_models.payload_content.tool_option import ToolDefinitionInput
|
||||
|
||||
|
||||
def create_builtin_tools() -> List[ToolOption]:
|
||||
"""Create built-in tools exposed to the main chat-loop model."""
|
||||
from src.llm_models.payload_content.tool_option import ToolOptionBuilder
|
||||
def _build_tool_spec(
|
||||
name: str,
|
||||
brief_description: str,
|
||||
parameters_schema: Dict[str, Any] | None = None,
|
||||
detailed_description: str = "",
|
||||
) -> ToolSpec:
|
||||
"""构建单个内置工具声明。
|
||||
|
||||
tools: List[ToolOption] = []
|
||||
Args:
|
||||
name: 工具名称。
|
||||
brief_description: 简要描述。
|
||||
parameters_schema: 参数 Schema。
|
||||
detailed_description: 详细描述;为空时自动根据参数生成。
|
||||
|
||||
wait_builder = ToolOptionBuilder()
|
||||
wait_builder.set_name("wait")
|
||||
wait_builder.set_description("Pause speaking and wait for the user to provide more input.")
|
||||
wait_builder.add_param(
|
||||
name="seconds",
|
||||
param_type=ToolParamType.INTEGER,
|
||||
description="How many seconds to wait before timing out.",
|
||||
required=True,
|
||||
enum_values=None,
|
||||
Returns:
|
||||
ToolSpec: 构建完成的工具声明。
|
||||
"""
|
||||
|
||||
normalized_schema = deepcopy(parameters_schema) if parameters_schema is not None else None
|
||||
return ToolSpec(
|
||||
name=name,
|
||||
brief_description=brief_description,
|
||||
detailed_description=(
|
||||
detailed_description.strip()
|
||||
or build_tool_detailed_description(normalized_schema)
|
||||
),
|
||||
parameters_schema=normalized_schema,
|
||||
provider_name="maisaka_builtin",
|
||||
provider_type="builtin",
|
||||
)
|
||||
tools.append(wait_builder.build())
|
||||
|
||||
reply_builder = ToolOptionBuilder()
|
||||
reply_builder.set_name("reply")
|
||||
reply_builder.set_description(
|
||||
"Generate and emit a visible reply based on the current thought. "
|
||||
"You must specify the target user msg_id to reply to."
|
||||
)
|
||||
reply_builder.add_param(
|
||||
name="msg_id",
|
||||
param_type=ToolParamType.STRING,
|
||||
description="The msg_id of the specific user message that this reply should target.",
|
||||
required=True,
|
||||
enum_values=None,
|
||||
)
|
||||
reply_builder.add_param(
|
||||
name="quote",
|
||||
param_type=ToolParamType.BOOLEAN,
|
||||
description="Whether the visible reply should be sent as a quoted reply to the target msg_id.",
|
||||
required=False,
|
||||
enum_values=None,
|
||||
)
|
||||
reply_builder.add_param(
|
||||
name="unknown_words",
|
||||
param_type=ToolParamType.ARRAY,
|
||||
description="Optional list of words or phrases that may need jargon lookup before replying.",
|
||||
required=False,
|
||||
enum_values=None,
|
||||
items_schema={"type": "string"},
|
||||
)
|
||||
tools.append(reply_builder.build())
|
||||
|
||||
query_jargon_builder = ToolOptionBuilder()
|
||||
query_jargon_builder.set_name("query_jargon")
|
||||
query_jargon_builder.set_description(
|
||||
"Query the meanings of one or more jargon words in the current chat context."
|
||||
)
|
||||
query_jargon_builder.add_param(
|
||||
name="words",
|
||||
param_type=ToolParamType.ARRAY,
|
||||
description="A list of words or phrases to query from the jargon store.",
|
||||
required=True,
|
||||
enum_values=None,
|
||||
items_schema={"type": "string"},
|
||||
)
|
||||
tools.append(query_jargon_builder.build())
|
||||
|
||||
query_person_info_builder = ToolOptionBuilder()
|
||||
query_person_info_builder.set_name("query_person_info")
|
||||
query_person_info_builder.set_description(
|
||||
"Query profile and memory information about a specific person by person name, nickname, or user ID."
|
||||
)
|
||||
query_person_info_builder.add_param(
|
||||
name="person_name",
|
||||
param_type=ToolParamType.STRING,
|
||||
description="The person's name, nickname, or user ID to search for.",
|
||||
required=True,
|
||||
enum_values=None,
|
||||
)
|
||||
query_person_info_builder.add_param(
|
||||
name="limit",
|
||||
param_type=ToolParamType.INTEGER,
|
||||
description="Maximum number of matched person records to return. Defaults to 3.",
|
||||
required=False,
|
||||
enum_values=None,
|
||||
)
|
||||
tools.append(query_person_info_builder.build())
|
||||
|
||||
no_reply_builder = ToolOptionBuilder()
|
||||
no_reply_builder.set_name("no_reply")
|
||||
no_reply_builder.set_description("Do not emit a visible reply this round and continue thinking.")
|
||||
tools.append(no_reply_builder.build())
|
||||
|
||||
stop_builder = ToolOptionBuilder()
|
||||
stop_builder.set_name("stop")
|
||||
stop_builder.set_description("Stop the current inner loop and return control to the outer chat flow.")
|
||||
tools.append(stop_builder.build())
|
||||
|
||||
send_emoji_builder = ToolOptionBuilder()
|
||||
send_emoji_builder.set_name("send_emoji")
|
||||
send_emoji_builder.set_description(
|
||||
"Send an emoji sticker to help express emotions. "
|
||||
"You should specify the emotion type to select an appropriate emoji."
|
||||
)
|
||||
send_emoji_builder.add_param(
|
||||
name="emotion",
|
||||
param_type=ToolParamType.STRING,
|
||||
description="The emotion type for selecting an appropriate emoji (e.g., 'happy', 'sad', 'angry', 'surprised', etc.).",
|
||||
required=False,
|
||||
enum_values=None,
|
||||
)
|
||||
tools.append(send_emoji_builder.build())
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
def get_builtin_tools() -> List[ToolOption]:
|
||||
"""Return built-in tools."""
|
||||
return create_builtin_tools()
|
||||
def create_builtin_tool_specs() -> List[ToolSpec]:
|
||||
"""创建 Maisaka 内置工具声明列表。
|
||||
|
||||
Returns:
|
||||
List[ToolSpec]: 内置工具声明列表。
|
||||
"""
|
||||
|
||||
return [
|
||||
_build_tool_spec(
|
||||
name="wait",
|
||||
brief_description="暂停当前对话并等待用户新的输入。",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"seconds": {
|
||||
"type": "integer",
|
||||
"description": "等待的秒数。",
|
||||
},
|
||||
},
|
||||
"required": ["seconds"],
|
||||
},
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="reply",
|
||||
brief_description="根据当前思考生成并发送一条可见回复。",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"msg_id": {
|
||||
"type": "string",
|
||||
"description": "要回复的目标用户消息编号。",
|
||||
},
|
||||
"quote": {
|
||||
"type": "boolean",
|
||||
"description": "是否以引用回复的方式发送。",
|
||||
"default": True,
|
||||
},
|
||||
"unknown_words": {
|
||||
"type": "array",
|
||||
"description": "回复前可能需要查询的黑话或词条列表。",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
},
|
||||
"required": ["msg_id"],
|
||||
},
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="query_jargon",
|
||||
brief_description="查询当前聊天上下文中的黑话或词条含义。",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"words": {
|
||||
"type": "array",
|
||||
"description": "要查询的词条列表。",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
},
|
||||
"required": ["words"],
|
||||
},
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="query_person_info",
|
||||
brief_description="查询某个人的档案和相关记忆信息。",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"person_name": {
|
||||
"type": "string",
|
||||
"description": "人物名称、昵称或用户 ID。",
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "最多返回多少条匹配记录。",
|
||||
"default": 3,
|
||||
},
|
||||
},
|
||||
"required": ["person_name"],
|
||||
},
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="no_reply",
|
||||
brief_description="本轮不发送可见回复,继续下一步思考。",
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="stop",
|
||||
brief_description="暂停当前内部循环,等待新的外部消息。",
|
||||
),
|
||||
_build_tool_spec(
|
||||
name="send_emoji",
|
||||
brief_description="发送一个合适的表情包来辅助表达情绪。",
|
||||
parameters_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emotion": {
|
||||
"type": "string",
|
||||
"description": "希望表达的情绪,例如 happy、sad、angry 等。",
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def get_builtin_tool_specs() -> List[ToolSpec]:
|
||||
"""获取 Maisaka 内置工具声明。
|
||||
|
||||
Returns:
|
||||
List[ToolSpec]: 内置工具声明列表。
|
||||
"""
|
||||
|
||||
return create_builtin_tool_specs()
|
||||
|
||||
|
||||
def get_builtin_tools() -> List[ToolDefinitionInput]:
|
||||
"""获取兼容旧模型层的内置工具定义。
|
||||
|
||||
Returns:
|
||||
List[ToolDefinitionInput]: 可直接传给模型层的工具定义。
|
||||
"""
|
||||
|
||||
return [tool_spec.to_llm_definition() for tool_spec in create_builtin_tool_specs()]
|
||||
|
||||
Reference in New Issue
Block a user