feat: 增加工具配置说明展示
This commit is contained in:
parent
fa5a2fa1ae
commit
d282cab00c
@ -20,6 +20,15 @@ from yuxi.utils.question_utils import normalize_questions
|
||||
# Lazy initialization for TavilySearch (only when API key is available)
|
||||
_tavily_search_instance = None
|
||||
|
||||
QWEN_IMAGE_CONFIG_GUIDE = """
|
||||
使用前需要先配置硅基流动的图片生成访问凭证。
|
||||
|
||||
请在后端运行环境中配置环境变量:
|
||||
- `SILICONFLOW_API_KEY`:用于调用 SiliconFlow 的图片生成接口
|
||||
|
||||
配置完成后即可使用该工具生成图片。
|
||||
""".strip()
|
||||
|
||||
|
||||
def _create_tavily_search():
|
||||
"""Create and register TavilySearch tool with metadata."""
|
||||
@ -280,7 +289,12 @@ def query_knowledge_graph(query: Annotated[str, "The keyword to query knowledge
|
||||
return f"知识图谱查询失败: {str(e)}"
|
||||
|
||||
|
||||
@tool(category="buildin", tags=["图片", "生成"], display_name="Qwen-Image")
|
||||
@tool(
|
||||
category="buildin",
|
||||
tags=["图片", "生成"],
|
||||
display_name="Qwen-Image",
|
||||
config_guide=QWEN_IMAGE_CONFIG_GUIDE,
|
||||
)
|
||||
async def text_to_img_qwen_image(
|
||||
prompt: Annotated[str, "用于生成图片的文本描述"],
|
||||
negative_prompt: Annotated[str, "负面提示词,用于指定不想出现在图片中的元素"] = "",
|
||||
|
||||
@ -17,6 +17,22 @@ from .security import MySQLSecurityChecker
|
||||
# 全局连接管理器实例
|
||||
_connection_manager: MySQLConnectionManager | None = None
|
||||
|
||||
MYSQL_CONFIG_GUIDE = """
|
||||
使用前需要先配置 MySQL 连接相关环境变量。
|
||||
|
||||
必填环境变量:
|
||||
- `MYSQL_HOST`
|
||||
- `MYSQL_PORT`
|
||||
- `MYSQL_USER`
|
||||
- `MYSQL_PASSWORD`
|
||||
- `MYSQL_DATABASE`
|
||||
|
||||
可选环境变量:
|
||||
- `MYSQL_DATABASE_DESCRIPTION`:数据库说明,会追加到工具描述中,帮助模型理解库表语义
|
||||
|
||||
请在后端运行环境中完成以上配置后再使用这些 MySQL 工具。
|
||||
""".strip()
|
||||
|
||||
|
||||
def get_connection_manager() -> MySQLConnectionManager:
|
||||
"""获取全局连接管理器"""
|
||||
@ -50,6 +66,7 @@ def get_connection_manager() -> MySQLConnectionManager:
|
||||
category="mysql",
|
||||
tags=["数据库", "查询"],
|
||||
display_name="列出MySQL表",
|
||||
config_guide=MYSQL_CONFIG_GUIDE,
|
||||
name_or_callable="mysql_list_tables",
|
||||
)
|
||||
def mysql_list_tables() -> str:
|
||||
@ -110,6 +127,7 @@ class TableDescribeModel(BaseModel):
|
||||
category="mysql",
|
||||
tags=["数据库", "结构"],
|
||||
display_name="描述MySQL表结构",
|
||||
config_guide=MYSQL_CONFIG_GUIDE,
|
||||
name_or_callable="mysql_describe_table",
|
||||
args_schema=TableDescribeModel,
|
||||
)
|
||||
@ -212,6 +230,7 @@ class QueryModel(BaseModel):
|
||||
category="mysql",
|
||||
tags=["数据库", "SQL"],
|
||||
display_name="执行MySQL查询",
|
||||
config_guide=MYSQL_CONFIG_GUIDE,
|
||||
name_or_callable="mysql_query",
|
||||
args_schema=QueryModel,
|
||||
)
|
||||
|
||||
@ -10,6 +10,7 @@ class ToolExtraMetadata:
|
||||
tags: list[str] = field(default_factory=list)
|
||||
display_name: str = "" # 显示名称(给人看的名字)
|
||||
icon: str = ""
|
||||
config_guide: str = "" # 配置说明(给人看的使用前配置提示)
|
||||
|
||||
|
||||
# 全局注册表: tool_name -> ToolExtraMetadata
|
||||
@ -40,6 +41,7 @@ def tool(
|
||||
tags: list[str] = None,
|
||||
display_name: str = "",
|
||||
icon: str = "",
|
||||
config_guide: str = "",
|
||||
name_or_callable: str | Callable | None = None,
|
||||
description: str | None = None,
|
||||
args_schema: type | None = None,
|
||||
@ -83,6 +85,7 @@ def tool(
|
||||
tags=tags or [],
|
||||
display_name=display_name,
|
||||
icon=icon,
|
||||
config_guide=config_guide,
|
||||
)
|
||||
|
||||
# 自动收集工具实例
|
||||
|
||||
@ -55,6 +55,7 @@ def _ensure_metadata_loaded():
|
||||
extra = extra_meta[tool_name]
|
||||
runtime_info["category"] = extra.category
|
||||
runtime_info["tags"] = extra.tags
|
||||
runtime_info["config_guide"] = extra.config_guide
|
||||
# display_name 优先级高于 tool.name
|
||||
if extra.display_name:
|
||||
runtime_info["name"] = extra.display_name
|
||||
@ -62,6 +63,7 @@ def _ensure_metadata_loaded():
|
||||
# 未注册,设为默认分类
|
||||
runtime_info["category"] = "buildin"
|
||||
runtime_info["tags"] = []
|
||||
runtime_info["config_guide"] = ""
|
||||
|
||||
_metadata_cache.append(runtime_info)
|
||||
|
||||
|
||||
@ -38,3 +38,14 @@ async def test_admin_can_fetch_config_and_reload_info(test_client, admin_headers
|
||||
reload_payload = reload_response.json()
|
||||
assert reload_payload["success"] is True
|
||||
assert "data" in reload_payload
|
||||
|
||||
|
||||
async def test_admin_can_fetch_tools_with_config_guide_field(test_client, admin_headers):
|
||||
response = await test_client.get("/api/system/tools", headers=admin_headers)
|
||||
assert response.status_code == 200, response.text
|
||||
|
||||
payload = response.json()
|
||||
assert payload["success"] is True
|
||||
assert isinstance(payload["data"], list)
|
||||
assert payload["data"]
|
||||
assert "config_guide" in payload["data"][0]
|
||||
|
||||
48
backend/test/unit/services/test_tool_service.py
Normal file
48
backend/test/unit/services/test_tool_service.py
Normal file
@ -0,0 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from yuxi.services import tool_service
|
||||
|
||||
|
||||
def test_get_tool_metadata_includes_config_guide(monkeypatch):
|
||||
tool_service._metadata_cache.clear()
|
||||
|
||||
fake_tool = SimpleNamespace(
|
||||
name="demo_tool",
|
||||
description="demo description",
|
||||
metadata={},
|
||||
args_schema=None,
|
||||
)
|
||||
fake_extra = SimpleNamespace(
|
||||
category="buildin",
|
||||
tags=["demo"],
|
||||
display_name="演示工具",
|
||||
config_guide="请先配置 DEMO_API_KEY",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"yuxi.agents.toolkits.registry.get_all_tool_instances",
|
||||
lambda: [fake_tool],
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"yuxi.agents.toolkits.registry.get_all_extra_metadata",
|
||||
lambda: {"demo_tool": fake_extra},
|
||||
)
|
||||
|
||||
result = tool_service.get_tool_metadata()
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"id": "demo_tool",
|
||||
"name": "演示工具",
|
||||
"description": "demo description",
|
||||
"metadata": {},
|
||||
"args": [],
|
||||
"category": "buildin",
|
||||
"tags": ["demo"],
|
||||
"config_guide": "请先配置 DEMO_API_KEY",
|
||||
}
|
||||
]
|
||||
|
||||
tool_service._metadata_cache.clear()
|
||||
@ -48,6 +48,7 @@
|
||||
- 重构内置 Skills/MCP/Subagents 安装/添加/移除机制:内置 skill 支持按需安装、基于 `version + content_hash` 的更新提示与覆盖确认,不再使用服务器级开关切换
|
||||
- 新增知识库 PDF、图片的预览功能
|
||||
- 重构后端测试目录结构:按 `unit / integration / e2e` 分层迁移现有测试,拆分全局 `conftest.py`,统一测试入口为 `uv run --group test pytest`,并新增独立测试规范文档 `docs/vibe/testing-guidelines.md`
|
||||
- 新增工具元数据 `config_guide` 字段:后端工具列表接口现在可返回“给人看的配置说明”,前端工具详情页会展示该说明,用于提示工具使用前需要配置的环境变量或入口;首批为 MySQL 工具和 `Qwen-Image` 补充了配置指引
|
||||
|
||||
<!-- 添加到这里 -->
|
||||
|
||||
|
||||
@ -101,6 +101,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentTool.config_guide">
|
||||
<div class="section-header">
|
||||
<FileText :size="14" />
|
||||
<span>配置说明</span>
|
||||
</div>
|
||||
<div class="section-content description config-guide">
|
||||
{{ currentTool.config_guide }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-header">
|
||||
<Tag :size="14" />
|
||||
@ -190,7 +200,8 @@ const filteredTools = computed(() => {
|
||||
(t) =>
|
||||
t.name.toLowerCase().includes(q) ||
|
||||
t.id.toLowerCase().includes(q) ||
|
||||
t.description?.toLowerCase().includes(q)
|
||||
t.description?.toLowerCase().includes(q) ||
|
||||
t.config_guide?.toLowerCase().includes(q)
|
||||
)
|
||||
}
|
||||
return result
|
||||
@ -261,4 +272,8 @@ defineExpose({
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.config-guide {
|
||||
white-space: pre-line;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user