Skip to main content

Universal tool-calling wrapper for non-tool-native LLMs — emulates function calling via structured JSON planning

Project description

toolproxy

PyPI version Python Versions License: MIT

Universal Tool-Calling Wrapper for Non-Tool-Native LLMs

A provider-agnostic Python library that adds reliable tool/function calling to any LLM — even models that have no native tool-calling API.


Problem

Many LLM providers (OpenRouter, Ollama, local LLMs) expose models that don't support function calling. This library solves that by:

  • Detecting whether the model supports native tool calling.
  • Using native tool calls when available (OpenAI format).
  • Falling back to a structured JSON planning protocol when not.

The developer always uses the same API regardless of the underlying model.


Installation

pip install toolproxy

Or from source:

pip install -e ".[dev]"

Quick Start

from toolproxy import UniversalAgent, tool

@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"Sunny, 25°C in {city}"

agent = UniversalAgent(
    model="openrouter/mistralai/mistral-7b-instruct",
    tools=[get_weather],
)

result = agent.run("What is the weather in Chennai today?")
print(result.content)

The same code works whether the model supports native tools or not.


How It Works

Developer
    │
    ▼
UniversalAgent.run(prompt)
    │
    ├─ Planner (auto-detects native vs emulated mode)
    │       │
    │       ├── Native mode  → provider tool calls (OpenAI format)
    │       └── Emulated mode → structured JSON Action schema
    │
    ├─ Executor (validates args, runs tool, captures errors)
    │
    └─ LoopController (repeats until final answer or max_steps)

Model Prefixes

Prefix Backend
openrouter/... OpenRouter API
ollama/... Local Ollama server
mock/... MockClient (for testing, no API key needed)
(no prefix) OpenAI / any OpenAI-compatible endpoint

Advanced Options

from toolproxy import UniversalAgent, tool
from toolproxy.config import ExecutionPolicy

agent = UniversalAgent(
    model="openrouter/your-model",
    tools=[get_weather],
    mode="auto",               # "auto" | "native_only" | "emulated_only"
    max_steps=10,
    execution_policy=ExecutionPolicy(
        mode="allow_only",
        allowed_tools=["get_weather"],
    ),
)

result = agent.run("...", return_trace=True)
print(result.content)
for call in result.trace.tool_calls:
    print(call.tool_name, call.arguments)

Callbacks (streaming-style)

result = agent.run(
    "...",
    on_tool_call=lambda step, tc: print(f"Calling: {tc.tool_name}"),
    on_tool_result=lambda step, tr: print(f"Result: {tr.output}"),
    on_model_output=lambda step, text: print(f"Model: {text}"),
)

Emulated Mode Protocol

When the model does not support native tools, the agent injects a system prompt instructing the model to output one of two JSON formats:

// Tool call
{"type": "tool_call", "tool": {"tool_name": "get_weather", "arguments": {"city": "Chennai"}}}

// Final answer
{"type": "final", "content": "The weather is sunny."}

Malformed responses are retried up to parse_retries times (default: 3) with an error explanation.


Project Structure

src/toolproxy/
  __init__.py      # Public API re-exports
  agent.py         # UniversalAgent class
  llm_client.py    # LLMClient + adapters
  tools.py         # @tool decorator + ToolRegistry
  schemas.py       # Pydantic schemas
  planner.py       # Planner logic
  executor.py      # Tool execution + policies
  loop.py          # Loop controller
  exceptions.py    # Custom exceptions
  config.py        # Configuration + capability map
examples/
  basic_chat.py
  openrouter_tools.py
  local_ollama.py
tests/
  test_agent_basic.py
  test_emulated_mode.py
  test_native_mode.py
  test_error_handling.py
  test_tool_registry.py

Publishing to PyPI

# 1. Install build tools
pip install build twine

# 2. Build wheel + sdist
python -m build

# 3. Check the distribution
twine check dist/*

# 4. Upload to PyPI (you will be prompted for credentials)
twine upload dist/*

# Or upload to TestPyPI first
twine upload --repository testpypi dist/*

Running Tests

pytest tests/ -v

Environment Variables

Variable Description
OPENROUTER_API_KEY API key for OpenRouter
OPENAI_API_KEY API key for OpenAI
OLLAMA_BASE_URL Ollama server URL (default: http://localhost:11434)
OLLAMA_MODEL Ollama model name (default: llama3)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

toolproxy-0.1.0.tar.gz (24.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

toolproxy-0.1.0-py3-none-any.whl (22.3 kB view details)

Uploaded Python 3

File details

Details for the file toolproxy-0.1.0.tar.gz.

File metadata

  • Download URL: toolproxy-0.1.0.tar.gz
  • Upload date:
  • Size: 24.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for toolproxy-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c50e44ecd925b85c75639048841b40f1f93e5aa670ee4500c84b5937d2e95c99
MD5 731f6a9ef7da1e79a89a869316a228b9
BLAKE2b-256 be7da918bf59f8c326c858b4ff33abf1840809cbe47ccf5662d6dda41f6d6d7c

See more details on using hashes here.

File details

Details for the file toolproxy-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: toolproxy-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for toolproxy-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 42ca1a212aecd5fc409d0e5513dc04411595393d17e4778d863dd027f41c7eea
MD5 20577173d26cdbd4e87e33f865abb9be
BLAKE2b-256 f095cad40cdafbed76741941dc4a38953a75255c99006acd3e278938afbf71b8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page