初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+137
View File
@@ -0,0 +1,137 @@
"""ToolSpec → OpenAI 函数 schema 的转换与默认工具集选取。
直接产出 OpenAI function schema dict(``{"type": "function", "function": {...}}``),
与 ChatResult.tool_calls 里的 name/args 回填闭环天然对齐。
ToolSpec 的 name/description 直接映射;参数 schema 因 ToolSpec 未内嵌 JSON Schema,
这里按工具 name 查表给出最小参数 schema(builtins 内工具参数固定,可选参数也显式列出)。
未知工具默认空参数 schema(``{"type": "object", "properties": {}}``),模型按需传参。
"""
from __future__ import annotations
from typing import Any
from app.modules.tool_runtime.definition import ToolSpec
# 内置工具的参数 schema(name → JSON Schema)。新增工具时扩展此表即可。
# 保持最小集合:只声明 known 工具的参数;调用方传入未登记工具时回退空对象 schema。
_PARAM_SCHEMAS: dict[str, dict[str, Any]] = {
"datetime": {"type": "object", "properties": {}, "required": []},
"read_file": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "workspace 内的文件相对路径。"},
"offset": {
"type": "integer",
"description": "起始行号(1-based),默认 1。大文件分段读取后续内容时使用。",
},
"limit": {
"type": "integer",
"description": "读取的行数上限;不传则读到末尾。大文件分段读取时使用。",
},
},
"required": ["path"],
},
"list_dir": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "workspace 内的目录相对路径,缺省为 '.'。",
},
},
"required": [],
},
"write_file": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "workspace 内文件相对路径。"},
"content": {"type": "string", "description": "要写入的文本内容。"},
},
"required": ["path"],
},
"shell": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要在沙箱中执行的 shell 命令。工作目录为沙箱隔离的 workspace 目录(并非 /home/user 或 /tmp 等 Linux 路径,这些路径不存在,不要 cd 到它们)。read_file/write_file 操作的也是这个 workspace 目录,shell 里 cd 到别的目录将看不到这些文件。",
},
"timeout_ms": {"type": "integer", "description": "可选超时(毫秒),默认 30000。"},
"stdin": {"type": "string", "description": "可选标准输入。"},
},
"required": ["command"],
},
"fetch_url": {
"type": "object",
"properties": {
"url": {"type": "string", "description": "要抓取的网页 URL。"},
"max_chars": {
"type": "integer",
"description": "正文最大字符数,默认 8000。",
},
},
"required": ["url"],
},
}
_EMPTY_SCHEMA: dict[str, Any] = {"type": "object", "properties": {}, "required": []}
def to_ai_provider_tools(tool_specs: list[ToolSpec]) -> list[dict[str, Any]]:
"""把 ToolSpec 列表转成 OpenAI function schema dict 列表。
先查 ``_PARAM_SCHEMAS``(内置工具),未命中则用 ``spec.input_schema``(MCP 工具),
仍为空则回退无参占位 ``_EMPTY_SCHEMA``。
"""
tools: list[dict[str, Any]] = []
for spec in tool_specs:
params = _PARAM_SCHEMAS.get(spec.name)
if not params and spec.name.startswith("skill:"):
params = _PARAM_SCHEMAS.get(spec.name.rsplit("/", 1)[-1])
if not params:
params = spec.input_schema or _EMPTY_SCHEMA
tools.append(
{
"type": "function",
"function": {
"name": spec.name,
"description": spec.description,
"parameters": params,
},
}
)
return tools
# 核心工具恒绑(沙箱类,始终可用)。
_CORE_TOOL_NAMES = ["datetime", "read_file", "list_dir", "write_file", "shell"]
def default_tools(
tool_specs_by_name: dict[str, ToolSpec] | None = None,
*,
allowed_tools: tuple[str, ...] = (),
) -> list[dict[str, Any]]:
"""返回工具对应的 OpenAI function schema 列表。
- 未传 ``tool_specs_by_name`` 时,从 ``builtin_specs()`` 加载仅返回核心工具
(datetime/read_file/list_dir/write_file/shell),共 5 个。
- 传入自定义工具池时(如 ``tool_runtime._resolve_specs()``),返回池中所有工具,
含 MCP 注册的工具,不限于核心工具。
"""
if tool_specs_by_name is None:
from app.application.tool_runtime.builtins import builtin_specs
tool_specs_by_name = builtin_specs()
chosen = [
tool_specs_by_name[name] for name in _CORE_TOOL_NAMES if name in tool_specs_by_name
]
else:
chosen = list(tool_specs_by_name.values())
if allowed_tools:
allowed = set(allowed_tools)
chosen = [spec for spec in chosen if spec.name in allowed]
return to_ai_provider_tools(chosen)