将本地工具贡献至 ToolUniverse¶
本指南介绍了如何将本地 Python 工具贡献到 ToolUniverse 仓库。本地工具在 ToolUniverse 进程中运行,并可供所有用户使用。
备注
关键区别:向代码库贡献内容相比于本地使用工具需要额外的步骤。最关键的步骤是修改 __init__.py 文件中的 4 个特定位置。
备注
For Local Development Only: If you just want to use a tool locally without contributing to the repository, see the single-file example in examples/my_new_tool/single_file_example.py. This approach doesn’t require modifying any core ToolUniverse files (__init__.py, default_config.py, or data/ directory). The complete working examples are available in examples/my_new_tool/ directory with a README explaining both approaches.
快速概览¶
贡献本地工具的10个步骤:
分步指南¶
步骤 1:环境设置¶
# Fork the repository on GitHub first
git clone https://github.com/yourusername/ToolUniverse.git
cd ToolUniverse
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install development dependencies
pip install -e ".[dev]"
# Install pre-commit hooks
./setup_precommit.sh
步骤 2:创建工具文件¶
在``src/tooluniverse/xxx_tool.py``中创建您的工具文件:
from tooluniverse.tool_registry import register_tool
from tooluniverse.base_tool import BaseTool
from typing import Dict, Any
@register_tool('MyNewTool') # Note: No config here for contributions
class MyNewTool(BaseTool):
"""My new tool for ToolUniverse."""
def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Execute the tool."""
# Your tool logic here
input_value = arguments.get('input', '')
return {
"result": input_value.upper(),
"success": True
}
def validate_input(self, **kwargs) -> None:
"""Validate input parameters."""
input_val = kwargs.get('input')
if not input_val:
raise ValueError("Input is required")
步骤 3:注册工具¶
@register_tool('MyNewTool') 装饰器用于注册您的工具类。请注意,对于贡献部分,我们不会在装饰器中包含配置——配置应放在单独的 JSON 文件中。
步骤 4:创建配置文件¶
创建或编辑 src/tooluniverse/data/xxx_tools.json:
[
{
"name": "my_new_tool",
"type": "MyNewTool",
"description": "Convert text to uppercase",
"parameter": {
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "Text to convert to uppercase"
}
},
"required": ["input"]
},
"examples": [
{
"description": "Convert text to uppercase",
"arguments": {"input": "hello world"}
}
],
"tags": ["text", "utility"],
"author": "Your Name <your.email@example.com>",
"version": "1.0.0"
}
]
Step 5: No Modifications Needed in __init__.py!¶
With the new automated discovery system, you do NOT need to modify `src/tooluniverse/__init__.py`.
The system will automatically find your tool class if it is decorated with @register_tool and located inside the src/tooluniverse package tree.
Verification:
# Test that your tool can be imported immediately
from tooluniverse import MyNewTool
print(MyNewTool) # Should show the class or lazy proxy
步骤 6:编写测试¶
在``tests/unit/test_my_new_tool.py``中创建测试:
import pytest
from tooluniverse.my_new_tool import MyNewTool
class TestMyNewTool:
def setup_method(self):
self.tool = MyNewTool()
def test_success(self):
"""Test successful execution."""
result = self.tool.run({"input": "hello"})
assert result["success"] is True
assert result["result"] == "HELLO"
def test_validation(self):
"""Test input validation."""
with pytest.raises(ValueError):
self.tool.validate_input(input="")
def test_empty_input(self):
"""Test empty input handling."""
result = self.tool.run({"input": ""})
assert result["success"] is True
assert result["result"] == ""
运行带覆盖率的测试:.. code-block:: bash
pytest tests/unit/test_my_new_tool.py –cov=tooluniverse –cov-report=html
第七步:代码质量检查(自动)¶
在提交代码时,预提交钩子会自动运行:
git add .
git commit -m "feat: add MyNewTool"
# Pre-commit will run: Black, Flake8, Ruff, etc.
# If checks fail, fix the issues and commit again
步骤 8:文档编制¶
为您的工具类添加全面的文档字符串:
class MyNewTool(BaseTool):
"""
Convert text to uppercase.
This tool takes a string input and returns it converted to uppercase.
Useful for text processing workflows.
Args:
input (str): The text to convert to uppercase
Returns:
dict: Result dictionary with 'result' and 'success' keys
Example:
>>> tool = MyNewTool()
>>> result = tool.run({"input": "hello"})
>>> print(result["result"])
HELLO
"""
请提供需要翻译的具体文本内容,我将为您进行翻译。
步骤 9:创建示例¶
Create examples/my_new_tool/my_new_tool_example.py:
"""Example usage of MyNewTool.
This example follows the documentation pattern for contributing tools to the
repository. It demonstrates the multi-file structure:
- my_new_tool.py: Tool class definition
- my_new_tool_tools.json: Tool configuration
- my_new_tool_example.py: Example usage
Note: In a real contribution, these files would be placed in:
- src/tooluniverse/my_new_tool.py
- src/tooluniverse/data/my_new_tool_tools.json
- examples/my_new_tool_example.py
And you would need to modify __init__.py in 4 locations.
"""
import os
import sys
# Import the tool class to register it
from my_new_tool import MyNewTool # noqa: E402, F401
from tooluniverse import ToolUniverse # noqa: E402
def main():
# Initialize ToolUniverse
tu = ToolUniverse()
# Load tools with the config file
# In a real contribution, this would be in default_tool_files
current_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(current_dir, 'my_new_tool_tools.json')
tu.load_tools(tool_config_files={"my_new_tool": config_path})
# Use the tool
result = tu.run({
"name": "my_new_tool",
"arguments": {"input": "hello world"}
})
print(f"Result: {result}")
# Test with different inputs
test_inputs = ["hello", "world", "python"]
for text in test_inputs:
result = tu.run({
"name": "my_new_tool",
"arguments": {"input": text}
})
print(f"'{text}' -> '{result.get('result', 'ERROR')}'")
if __name__ == "__main__":
main()
Note: A complete working example can be found in examples/my_new_tool/ directory,
which includes both the multi-file structure (for contributions) and a single-file
example (for local development). See examples/my_new_tool/README.md for details.
第10步:提交拉取请求¶
# Create feature branch
git checkout -b feature/add-my-new-tool
# Add all files
git add src/tooluniverse/my_new_tool.py
git add src/tooluniverse/data/xxx_tools.json
git add src/tooluniverse/__init__.py
git add tests/unit/test_my_new_tool.py
git add examples/my_new_tool_example.py
# Commit with descriptive message
git commit -m "feat: add MyNewTool for text processing
- Implement MyNewTool class with uppercase conversion
- Add comprehensive unit tests with >95% coverage
- Include usage examples and documentation
- Support input validation and error handling
Closes #[issue-number]"
# Push and create PR
git push origin feature/add-my-new-tool
PR 模板: .. code-block:: markdown
## 描述
此PR新增了MyNewTool,一款用于文本处理的本地新工具。
## 修改内容
✅ 工具实施:完成 MyNewTool 类
✅ 测试:单元测试覆盖率超过 95%
✅ 文档:详尽的文档字符串和示例
✅ 配置:位于 data/xxx_tools.json 的 JSON 配置
✅ 集成:在 4 个位置修改了 __init__.py
## 测试
`bash pytest tests/unit/test_my_new_tool.py --cov=tooluniverse python examples/my_new_tool/my_new_tool_example.py `## 检查清单
[x] 测试在本地通过
[x] 代码遵循项目风格指南
[x] 文档已完成
[x] __init__.py 已在所有 4 个位置进行了修改
[x] 示例按预期运行
常见错误¶
❌ Most Common: Missing @register_tool decorator
- Tool won’t be discovered if not decorated
- Solution: Add @register_tool("MyTool")
❌ Config in wrong place
- Don’t put config in @register_tool() decorator (for contributions)
- Put it in data/my_new_tool_tools.json instead
- Note: For local development only, you CAN put config in the decorator (see examples/my_new_tool/single_file_example.py)
❌ 文件位置错误 - 工具文件必须位于 src/tooluniverse/ - 不应放在您的项目目录中
❌ 缺少测试 - 覆盖率必须 >90% - 测试成功和错误情况
❌ 导入错误 - 检查模块名称是否与文件名称匹配 - 检查类名称是否完全一致(区分大小写)
故障排除¶
ImportError: 无法导入名称 ‘MyNewTool’ .. code-block:: python
# 检查工具是否在 __all__ 列表中 from tooluniverse import __all__ print(“MyNewTool” in __all__) # 应为 True
# 检查是否存在导入语句 # 查找:from .my_new_tool import MyNewTool
AttributeError: 模块 ‘tooluniverse’ 没有属性 ‘MyNewTool’ - 请确认工具名称是否在 __all__ 列表中 - 检查工具名称是否与类名完全匹配
使用 ToolUniverse 时未找到工具 .. code-block:: python
# 验证工具是否正确加载 from tooluniverse import ToolUniverse tu = ToolUniverse() tu.load_tools()
# Check if tool is in the loaded tools print(“my_new_tool” in tu.all_tool_dict) # Should be True
下一步¶
在成功提交您的本地工具后:
🚀 远程工具: 将远程工具贡献给ToolUniverse - 了解如何贡献远程工具
🔍 架构: ToolUniverse 架构 - 了解 ToolUniverse 的内部结构
📊 比较:请查看 Contributing to ToolUniverse 中的工具类型比较表
小技巧
成功提示:从简单的工具开始,进行充分测试,如果遇到问题,可以在 GitHub 讨论中寻求帮助!