RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /Function Calling for Local Models
  6. /Ch. 3
Function Calling for Local Models

03. Defining Tool Functions

Chapter 3 of 18 · 20 min
KEY INSIGHT

The registry pattern separates tool definitions from implementations, allowing you to modify code without changing the schema sent to the model.

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.

EXERCISE

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.

← Chapter 2
JSON Schema for Tools
Chapter 4 →
Ollama Function Calling