03. Defining Tool Functions
Tool definitions must translate into actual executable code. The mapping between JSON Schema definitions and Python functions forms the foundation of your function calling system.
A tool registry maps function names to callable implementations. Each entry contains the JSON Schema definition, the actual function, and any authentication or rate-limiting requirements.
import json
from typing import Callable, Any
class ToolRegistry:
def __init__(self):
self.tools: dict[str, dict] = {}
def register(self, name: str, func: Callable, schema: dict) -> None:
self.tools[name] = {
"function": func,
"schema": schema
}
def get_schema(self, name: str) -> dict | None:
return self.tools.get(name, {}).get("schema")
def execute(self, name: str, arguments: dict) -> Any:
if name not in self.tools:
raise ValueError(f"Unknown tool: {name}")
return self.tools[name]["function"](**arguments)
Register tools with descriptive schemas:
def get_weather(city: str, units: str = "celsius") -> dict:
# Implementation placeholder
return {"city": city, "temp": 22, "units": units}
registry = ToolRegistry()
registry.register("get_weather", get_weather, {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name for weather lookup"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city"]
})
Execution requires argument validation against the schema before calling:
def execute_tool(name: str, arguments: dict, schema: dict) -> dict:
try:
# Validate required fields
for required_field in schema.get("required", []):
if required_field not in arguments:
return {
"success": False,
"error": f"Missing required field: {required_field}"
}
# Validate types
for field, value in arguments.items():
field_schema = schema.get("properties", {}).get(field, {})
expected_type = field_schema.get("type")
if expected_type == "number" and not isinstance(value, (int, float)):
return {"success": False, "error": f"{field} must be a number"}
elif expected_type == "string" and not isinstance(value, str):
return {"success": False, "error": f"{field} must be a string"}
result = registry.execute(name, arguments)
return {"success": True, "result": result}
except Exception as e:
return {"success": False, "error": str(e)}
Security considerations matter for tool execution. Validate file paths to prevent directory traversal, sanitize SQL inputs to prevent injection, implement timeouts for long-running operations, and log all tool executions for audit purposes.
Create a tool registry with three tools: a file reader, a web search simulator, and a calculator. Implement argument validation and handle execution errors gracefully.