Ver código fonte

fix: /tools and /search_tools crashed on tool.group_ids AttributeError

ToolMeta was refactored to drop the group_ids field (replaced by
provider_ids / capability_ids / knowledge_ids), but server.py still
read tool.group_ids in two places, causing every /tools call to 500.

The group-to-tool relationship is now owned by ToolGroupManager (each
ToolGroup has a tool_ids list), not by the tool itself. Reverse-lookup
via ToolGroupManager.list_all() produces the same group_ids array the
old API contract promised, so clients (the agent framework's
toolhub_search wrapper) need no changes.

Changes:
- In both list_tools and search_tools, build a tool_id -> group_ids
  reverse-lookup dict once per request using
  router.registry.group_manager.list_all() (O(N+M), not O(N*M))
- Replace `tool.group_ids` with `tool_to_groups.get(tool.tool_id, [])`
Talegorithm 3 dias atrás
pai
commit
c81698c592
1 arquivos alterados com 17 adições e 2 exclusões
  1. 17 2
      src/tool_agent/router/server.py

+ 17 - 2
src/tool_agent/router/server.py

@@ -91,6 +91,13 @@ def create_app(router: Router, session_manager: SessionManager = None) -> FastAP
                 "usage_example": g.usage_example,
             })
 
+
+        # 构建 tool_id -> group_ids 反查表(避免 N*M 复杂度)
+        tool_to_groups: dict[str, list[str]] = {}
+        for g in router.registry.group_manager.list_all():
+            for tid in (g.tool_ids or []):
+                tool_to_groups.setdefault(tid, []).append(g.group_id)
+
         tools = []
         for tool in router.registry.list_all():
             source = router.status_manager.get_primary_source(tool.tool_id)
@@ -101,7 +108,7 @@ def create_app(router: Router, session_manager: SessionManager = None) -> FastAP
                 "description": tool.description,
                 "category": tool.category,
                 "backend_runtime": tool.backend_runtime.value if tool.backend_runtime else "unknown",
-                "group_ids": tool.group_ids,
+                "group_ids": tool_to_groups.get(tool.tool_id, []),
                 "input_schema": tool.input_schema,
                 "output_schema": tool.output_schema,
                 "state": route.state.value if route else "stopped",
@@ -123,6 +130,14 @@ def create_app(router: Router, session_manager: SessionManager = None) -> FastAP
     @app.post("/search_tools")
     async def search_tools(request: SearchToolsRequest):
         kw = (request.keyword or "").lower()
+
+
+        # 构建 tool_id -> group_ids 反查表(避免 N*M 复杂度)
+        tool_to_groups: dict[str, list[str]] = {}
+        for g in router.registry.group_manager.list_all():
+            for tid in (g.tool_ids or []):
+                tool_to_groups.setdefault(tid, []).append(g.group_id)
+
         tools = []
         for tool in router.registry.list_all():
             if kw:
@@ -155,7 +170,7 @@ def create_app(router: Router, session_manager: SessionManager = None) -> FastAP
                 "description": tool.description,
                 "category": tool.category,
                 "backend_runtime": tool.backend_runtime.value if tool.backend_runtime else "unknown",
-                "group_ids": tool.group_ids,
+                "group_ids": tool_to_groups.get(tool.tool_id, []),
                 "input_schema": tool.input_schema,
                 "output_schema": tool.output_schema,
                 "params": params,