from abc import ABC, abstractmethod from dataclasses import dataclass @dataclass class ToolResult: content: str class Tool(ABC): @property @abstractmethod def name(self) -> str: ... @property @abstractmethod def description(self) -> str: ... @property @abstractmethod def schema(self) -> dict[str, object]: ... @abstractmethod def execute(self, arguments: dict[str, object]) -> ToolResult: ... class ToolRegistry: def __init__(self) -> None: self.tools: dict[str, Tool] = {} def register(self, tool: Tool) -> None: self.tools[tool.name] = tool def get(self, name: str) -> Tool | None: return self.tools.get(name) def all(self) -> list[Tool]: return list(self.tools.values()) def schemas(self) -> list[dict[str, object]]: return [ { "type": "function", "function": { "name": tool.name, "description": tool.description, "parameters": tool.schema, }, } for tool in self.tools.values() ] class HelloTool(Tool): @property def name(self) -> str: return "hello" @property def description(self) -> str: return "Say hello to someone." @property def schema(self) -> dict[str, object]: return { "type": "object", "properties": { "name": { "type": "string", "description": "The person to greet.", }, }, "required": ["name"], } def execute(self, arguments: dict[str, object]) -> ToolResult: name = arguments.get("name", "stranger") return ToolResult(content=f"Hello, {name}!") class CalculatorTool(Tool): @property def name(self) -> str: return "calculator" @property def description(self) -> str: return "Perform basic arithemtic" @property def schema(self) -> dict[str, object]: return { "type": "object", "properties": { "a": { "type": "number", "description": "The first number.", }, "b": { "type": "number", "description": "The second number.", }, "operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"], "description": "The arithemtic operation to perform", }, }, "required": ["a", "b", "operation"], } def execute(self, arguments: dict[str, object]) -> ToolResult: a = arguments["a"] b = arguments["b"] operation = arguments["operation"] if not isinstance(a, (int, float)): raise TypeError("a must be a number") if not isinstance(b, (int, float)): raise TypeError("b must be a number") if not isinstance(operation, str): raise TypeError("operation must be a string") if operation == "add": result = a + b elif operation == "subtract": result = a - b elif operation == "multiply": result = a * b elif operation == "divide": if b == 0: raise ValueError("cannot divide by zero") result = a / b else: raise ValueError(f"unknown operation: {operation}") return ToolResult(content=str(result)) if __name__ == "__main__": registry = ToolRegistry() registry.register(HelloTool()) print(registry.schemas()) tool = registry.get("hello") if tool is not None: result = tool.execute({"name": "Joseph"}) print(result.content)