Conversation
此提交添加了一系列新的工具相关的模块和API接口,包括客户端模板、模型定义以及各种控制和OpenAPI接口等组件。同时更新了依赖版本并完善了初始化配置。 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
There was a problem hiding this comment.
Pull request overview
该 PR 为 SDK 新增了完整的 Tool 相关模块与 API(管控链路 + 数据链路),并补齐了对应的单元测试,同时更新了底层依赖版本以支持新能力。
Changes:
- 新增
agentrun.tool模块:包含 Tool 资源类、客户端、模型定义,以及 Control/MCP/OpenAPI 三类 API 组件 - 新增 Tool 模块的单元测试覆盖(model / mcp / openapi / tool & client)
- 更新
alibabacloud-agentrun20250910依赖版本要求
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
pyproject.toml |
升级底层 alibabacloud-agentrun20250910 版本以支持新 Tool 能力 |
agentrun/tool/__init__.py |
聚合导出 tool 模块公共 API |
agentrun/tool/model.py |
新增 ToolType/McpConfig/ToolSchema/ToolInfo 等模型定义 |
agentrun/tool/api/__init__.py |
Tool API 子模块初始化 |
agentrun/tool/api/control.py |
新增 Tool 管控链路 API(基于底层 SDK 获取 Tool 资源) |
agentrun/tool/api/mcp.py |
新增 MCP 数据链路会话封装(SSE/Streamable) |
agentrun/tool/api/openapi.py |
新增 FunctionCall(OpenAPI) 数据链路解析与 HTTP 调用客户端 |
agentrun/tool/client.py |
新增 ToolClient(get/get_async) |
agentrun/tool/tool.py |
新增 Tool 资源类:支持 get、list_tools、call_tool(sync/async) |
agentrun/tool/__client_async_template.py |
codegen 模板:ToolClient(async) |
agentrun/tool/__tool_async_template.py |
codegen 模板:Tool 资源类(async) |
agentrun/__init__.py |
顶层导出 ToolResource/ToolResourceClient/ToolResourceControlAPI(避免与 server.Tool 冲突) |
tests/unittests/tool/__init__.py |
Tool 测试包初始化 |
tests/unittests/tool/test_model.py |
Tool 模型单元测试 |
tests/unittests/tool/test_mcp.py |
MCP 会话单元测试 |
tests/unittests/tool/test_openapi.py |
OpenAPI 客户端单元测试 |
tests/unittests/tool/test_tool.py |
Tool 资源类与 ToolClient 单元测试 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| url = f"{base_url.rstrip('/')}{target_operation['path']}" | ||
| method = target_operation["method"] | ||
|
|
||
| logger.debug( | ||
| f"Calling FunctionCall tool: {method} {url} with args={arguments}" | ||
| ) | ||
|
|
||
| with httpx.Client(headers=self.headers, timeout=30.0) as client: | ||
| if method in ("POST", "PUT", "PATCH"): | ||
| response = client.request(method, url, json=arguments or {}) | ||
| else: | ||
| response = client.request(method, url, params=arguments or {}) | ||
|
|
There was a problem hiding this comment.
call_tool()/call_tool_async() currently build the request URL by concatenating server_url and the raw OpenAPI path (e.g. /users/{id}) without substituting path parameters. For endpoints that use templated path params, this will send requests to URLs containing {id} literally and pass id as query/body instead, which is incorrect.
Consider rendering the path by replacing {param} placeholders with values from arguments (and removing those keys from query/body), similar to how agentrun/toolset/api/openapi.py handles path rendering/joining.
| param_schema = param.get("schema", {"type": "string"}) | ||
| param_schema["description"] = param.get( | ||
| "description", "" | ||
| ) |
There was a problem hiding this comment.
In _parse_operations(), parameter handling assumes every parameter has a dict schema and then mutates it (param_schema["description"] = ...). In valid OpenAPI 3, a Parameter may omit schema and instead provide content, and schema can also be None/non-dict. This will raise at runtime.
Suggest defaulting to a new dict schema when missing/non-dict, avoiding in-place mutation of the original spec, and optionally supporting content['application/json'].schema for parameters.
| param_schema = param.get("schema", {"type": "string"}) | |
| param_schema["description"] = param.get( | |
| "description", "" | |
| ) | |
| # Prefer explicit schema, fall back to content-based schema, then a default. | |
| raw_schema = param.get("schema") | |
| resolved_schema: Dict[str, Any] = {} | |
| if isinstance(raw_schema, dict): | |
| # Try to resolve $ref or composed schemas; fall back to the raw dict. | |
| maybe_resolved = self._resolve_schema(raw_schema) | |
| if isinstance(maybe_resolved, dict): | |
| resolved_schema = dict(maybe_resolved) | |
| else: | |
| resolved_schema = dict(raw_schema) | |
| else: | |
| # OpenAPI 3 allows parameters to define schema under content. | |
| param_content = param.get("content", {}) | |
| raw_content_schema = None | |
| if isinstance(param_content, dict): | |
| json_content = param_content.get("application/json", {}) | |
| if isinstance(json_content, dict): | |
| raw_content_schema = json_content.get("schema") | |
| if raw_content_schema is not None: | |
| maybe_resolved = self._resolve_schema(raw_content_schema) | |
| if isinstance(maybe_resolved, dict): | |
| resolved_schema = dict(maybe_resolved) | |
| if not isinstance(resolved_schema, dict) or not resolved_schema: | |
| resolved_schema = {"type": "string"} | |
| # Do not mutate the original spec; work on our own copy. | |
| param_schema = dict(resolved_schema) | |
| param_schema["description"] = param.get("description", "") |
| return asyncio.get_event_loop().run_until_complete( | ||
| self.list_tools_async() | ||
| ) |
There was a problem hiding this comment.
The synchronous wrappers list_tools() / call_tool() use asyncio.get_event_loop().run_until_complete(...). This is fragile on Python 3.10+ (can raise when no default loop is set) and will fail if called from within an already-running event loop.
Elsewhere in the repo (e.g. agentrun/toolset/api/mcp.py) sync wrappers use asyncio.run(...). Consider aligning with that approach or otherwise handling the running-loop case safely.
| return asyncio.get_event_loop().run_until_complete( | |
| self.list_tools_async() | |
| ) | |
| try: | |
| # If this succeeds, we're already inside an event loop | |
| asyncio.get_running_loop() | |
| except RuntimeError: | |
| # No running loop: safe to create and manage one with asyncio.run | |
| return asyncio.run(self.list_tools_async()) | |
| else: | |
| # Called from async context: must use the async API instead | |
| raise RuntimeError( | |
| "list_tools() cannot be called from an async context. " | |
| "Use list_tools_async() instead." | |
| ) |
| import asyncio | ||
| from typing import Any, Dict, List, Optional | ||
|
|
||
| from agentrun.tool.model import ToolInfo, ToolSchema |
There was a problem hiding this comment.
ToolSchema is imported but never used in this module. With ruff/pylint enabled in the repo, this will likely fail linting.
Remove the unused import (or use it if intended).
| from agentrun.tool.model import ToolInfo, ToolSchema | |
| from agentrun.tool.model import ToolInfo |
此提交添加了对SKILL类型工具的支持,包括获取下载URL和异步下载解压的功能。同时更新了相关单元测试。 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
在多个集成模块中添加了 `tool_resource` 函数的支持,允许用户将 ToolResource 封装为不同 AI 框架所需的工具格式,包括 CrewAI、LangChain、PydanticAI、LangGraph 和 AgentScope。同时更新了相关初始化文件以导出新功能。 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
在多个集成框架中添加了 `skill_tools` 函数,用于将 Skill 封装为不同 AI 框架的工具列表,包括 CrewAI、LangChain、PydanticAI、LangGraph、AgentScope 和 Google ADK。同时更新了相关的初始化文件和工具加载器。 Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
此提交添加了一系列新的工具相关的模块和API接口,包括客户端模板、模型定义以及各种控制和OpenAPI接口等组件。同时更新了依赖版本并完善了初始化配置。
Co-developed-by: Aone Copilot noreply@alibaba-inc.com
Fix bugs
Bug detail
Pull request tasks
Update docs
Reason for update
Pull request tasks
Add contributor
Contributed content
Content detail
Others
Reason for update