Skip to content

streed/tooly

Repository files navigation

Tooly

A Go library for HTTP-based tool calling with Ollama integration and a simple test UI.

Features

  • HTTP Tool Client: Call HTTP endpoints as tools with JSON configuration
  • Ollama Integration: Seamlessly integrate tools with Ollama's chat API
  • System Prompts: Configure custom system prompts for consistent AI behavior
  • Structured Logging: Configurable log levels and formats (JSON/text) using Go's slog
  • Test UI: Simple web interface for testing tools and chat interactions
  • Flexible Configuration: JSON-based tool definitions

Quick Start

Option 1: Using Make (Recommended)

# Quick setup - install deps and build
make quick-start

# Start development server  
make dev

# View all available commands
make help

Option 2: Manual Setup

# 1. Run the development setup script
./scripts/dev-setup.sh

# 2. Start the server
go run cmd/server/main.go

# 3. Open your browser
open http://localhost:8080

Option 3: Docker

# Build and run with Docker
make docker-build
make docker-run

# Or use docker-compose (includes Ollama)
docker-compose up

Configuration

Copy config.example.json to tooly.json and customize:

  • Server settings: Port, host, timeouts
  • Ollama integration: Model, URL, iterations, system prompt
  • Tool configuration: File paths, timeouts
  • Logging: Log levels, output format

Library Usage

Basic Tool Client

package main

import (
    "context"
    "fmt"
    "time"
    "tooly/pkg/tooly"
)

func main() {
    // Create client using simple constructor
    client := tooly.NewClient(30 * time.Second)
    
    // Or create client using options pattern for more flexibility
    client := tooly.NewClientWithOptions(
        tooly.WithTimeout(60 * time.Second),
        tooly.WithVersion("1.0.0"),
    )
    
    // Load tools from configuration
    err := client.LoadToolsFromFile("examples/tools.json", 30 * time.Second)
    if err != nil {
        panic(err)
    }
    
    // Call a tool
    call := tooly.ToolCall{
        Name: "get_weather",
        Arguments: map[string]interface{}{
            "q": "London,UK",
            "appid": "your-api-key",
        },
    }
    
    result, err := client.CallTool(context.Background(), call)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Result: %+v\n", result)
}

Ollama Integration

package main

import (
    "context"
    "fmt"
    "time"
    "tooly/pkg/ollama"
    "tooly/pkg/tooly"
)

func main() {
    // Setup tool client
    toolClient := tooly.NewClientWithOptions(
        tooly.WithTimeout(30 * time.Second),
        tooly.WithVersion("1.0.0"),
    )
    toolClient.LoadToolsFromFile("examples/tools.json", 30 * time.Second)
    
    // Setup Ollama client using options pattern (recommended)
    ollamaClient := ollama.NewClientWithOptions(toolClient,
        ollama.WithBaseURL("http://localhost:11434"),
        ollama.WithTimeout(60 * time.Second),
        ollama.WithMaxIterations(10),
        ollama.WithAvailableModels([]string{"llama3.2", "qwen2.5:3b", "codellama:7b"}),
        ollama.WithDefaultModel("llama3.2"),
        ollama.WithSystemPrompt("You are a helpful assistant. Be concise and use tools when appropriate."),
    )
    
    // Or use the traditional constructor
    ollamaClient = ollama.NewClient("http://localhost:11434", toolClient, 60 * time.Second, 10, 
        []string{"llama3.2", "qwen2.5:3b"}, "llama3.2", 
        "You are a helpful assistant.")
    
    // Chat with tool execution
    messages := []ollama.Message{
        {Role: "user", Content: "What's the weather in London?"},
    }
    
    result, err := ollamaClient.ChatWithToolExecution(
        context.Background(), 
        "llama3.2", 
        messages,
    )
    if err != nil {
        panic(err)
    }
    
    for _, msg := range result {
        fmt.Printf("[%s]: %s\n", msg.Role, msg.Content)
    }
}

Tool Configuration

Tools are defined in JSON format with support for system parameters that are managed automatically:

{
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a location",
      "url": "https://api.openweathermap.org/data/2.5/weather",
      "method": "GET",
      "headers": {
        "User-Agent": "Tooly/1.0"
      },
      "parameters": {
        "type": "object",
        "properties": {
          "q": {
            "type": "string",
            "description": "City name"
          },
          "appid": {
            "type": "string",
            "description": "API key for OpenWeatherMap",
            "default": "your-api-key-here",
            "system": true
          },
          "units": {
            "type": "string",
            "description": "Temperature units",
            "enum": ["standard", "metric", "imperial"],
            "default": "metric"
          }
        },
        "required": ["q"]
      }
    }
  ]
}

Tool Properties

  • name: Unique identifier for the tool
  • description: Human-readable description sent to the LLM
  • url: HTTP endpoint URL
  • method: HTTP method (GET, POST, PUT, PATCH, DELETE)
  • headers: Optional HTTP headers to include in requests
  • parameters: JSON Schema defining expected parameters
  • timeout: Optional timeout duration (defaults to 30s)

Parameter Properties

Each parameter in the properties object supports:

  • type: Parameter data type (string, number, boolean, object, array)
  • description: Human-readable description sent to the LLM
  • default: Default value used when parameter is not provided
  • system: Boolean - if true, parameter is managed by system config (see System Parameters)
  • enum: Array of allowed values for the parameter
  • required: Specify in the parent required array if parameter must be provided

System Parameters

System parameters are a powerful feature that allows you to manage sensitive configuration (like API keys) automatically while keeping them hidden from the LLM:

{
  "parameters": {
    "properties": {
      "api_key": {
        "type": "string",
        "default": "sk-your-actual-api-key-here",
        "system": true
      },
      "query": {
        "type": "string", 
        "description": "Search query"
      }
    },
    "required": ["query"]
  }
}

How System Parameters Work:

  1. Hidden from LLM: System parameters are completely removed from the tool schema sent to the LLM
  2. Automatic injection: The configured default value is automatically used for all requests
  3. LLM cannot override: Even if the LLM tries to provide a value, it will be ignored
  4. Security focused: Prevents LLM from generating placeholder API keys or sensitive values
  5. No required constraint: System parameters should not be listed in required since the LLM cannot provide them

Benefits:

  • API Key Management: Store real API keys in configuration, LLM never sees or generates them
  • Consistent Authentication: Guaranteed use of proper credentials for all requests
  • Cleaner Tool Calls: LLM focuses only on user-relevant parameters
  • Security: Eliminates risk of LLM generating invalid or placeholder credentials

Example System Parameter Log Output:

Applied system parameter value (always overrides LLM) 
param_name=api_key system_value=sk-real-key-here llm_value=<nil>

API Endpoints

The test server provides the following endpoints:

  • GET / - Test UI interface
  • GET /api/tools - List available tools
  • POST /api/tools/call - Execute a tool
  • GET /api/models - List available models
  • POST /api/chat - Chat with Ollama using tools

Tool Execution

curl -X POST http://localhost:8080/api/tools/call \
  -H "Content-Type: application/json" \
  -d '{
    "name": "get_weather",
    "arguments": {
      "q": "London,UK",
      "appid": "your-api-key"
    }
  }'

Chat with Tools

curl -X POST http://localhost:8080/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llama3.2",
    "message": "What is the weather in London?"
  }'

Development

Make Targets

Common development tasks:

make dev           # Start development server
make build         # Build binary  
make test          # Run tests
make fmt           # Format code
make lint          # Run linter
make check         # Run all checks
make clean         # Clean build artifacts

Build for different platforms:

make build-linux     # Linux AMD64
make build-macos     # macOS AMD64  
make build-macos-arm # macOS ARM64
make build-windows   # Windows AMD64
make build-all       # All platforms

Docker development:

make docker-build  # Build Docker image
make docker-run    # Run container
make docker-dev    # Development container

Scripts

  • scripts/dev-setup.sh - Set up development environment
  • scripts/release.sh - Create release builds

Project Structure

tooly/
├── pkg/
│   ├── config/         # Configuration management
│   ├── tooly/          # Core HTTP tool client
│   └── ollama/         # Ollama integration
├── cmd/
│   └── server/         # Test server
├── web/
│   └── static/         # Test UI
├── examples/           # Configuration examples
├── scripts/            # Development scripts  
├── build/              # Build output
├── Makefile           # Development tasks
├── Dockerfile         # Container definition
└── docker-compose.yml # Multi-service setup

Configuration

The application can be configured using a JSON configuration file. By default, it looks for:

  1. tooly.json in the current directory
  2. config.json in the current directory
  3. config/tooly.json
  4. File specified in TOOLY_CONFIG environment variable

Configuration Options

{
  "server": {
    "port": "8080",
    "host": "localhost",
    "read_timeout": "30s",
    "write_timeout": "30s", 
    "static_dir": "web/static"
  },
  "ollama": {
    "base_url": "http://localhost:11434",
    "default_model": "llama3.2",
    "available_models": [
      "llama3.2",
      "llama3.2:3b",
      "qwen2.5:3b",
      "codellama:7b",
      "mistral:7b"
    ],
    "timeout": "60s",
    "max_iterations": 10,
    "system_prompt": "You are a helpful assistant. Be concise and professional in your responses. Always use available tools when they can help answer questions."
  },
  "tools": {
    "config_file": "examples/tools.json",
    "default_timeout": "30s"
  },
  "logging": {
    "level": "info",
    "format": "text"
  }
}

Model Configuration

Configure which models users can select in the UI:

  • available_models: Array of model names that can be used
  • default_model: Model selected by default (must be in available_models)
  • Models are validated - requests with unlisted models will be rejected
  • The UI automatically populates a dropdown with available models

System Prompts

Configure custom system prompts to provide consistent AI behavior and guardrails:

{
  "ollama": {
    "system_prompt": "You are a helpful assistant specialized in technical support. Be concise, accurate, and always suggest using available tools when appropriate."
  }
}

The system prompt is automatically prepended to all chat conversations. If a conversation already starts with a system message, the configured prompt is ignored to avoid duplication.

Logging Configuration

Control logging output with configurable levels and formats:

{
  "logging": {
    "level": "info",    // debug, info, warn, error
    "format": "text"    // text, json
  }
}

Log Levels:

  • debug: Verbose output including HTTP requests, tool execution details, and internal operations
  • info: General application information including startup, configuration, and major operations
  • warn: Warning messages about potential issues
  • error: Error messages only (minimal output)

Log Formats:

  • text: Human-readable format for development and debugging
  • json: Structured JSON format for production log aggregation and analysis

Examples:

Text format (info level):

2025/09/11 18:46:19 INFO Server configuration host=localhost port=8080
2025/09/11 18:46:19 INFO Ollama configuration base_url=http://localhost:11434

JSON format (debug level):

{"time":"2025-09-11T18:46:19.145667482-07:00","level":"INFO","msg":"Server configuration","host":"localhost","port":"8080"}
{"time":"2025-09-11T18:46:19.145676426-07:00","level":"DEBUG","msg":"HTTP request received","method":"POST","path":"/api/chat","remote_addr":"127.0.0.1:54321"}

Configuration Fields

Server:

  • port: Server port (default: 8080)
  • host: Server host (default: localhost)
  • read_timeout: HTTP read timeout
  • write_timeout: HTTP write timeout
  • static_dir: Directory containing static files

Ollama:

  • base_url: Ollama API base URL
  • default_model: Default model for chat requests
  • available_models: List of models that can be used via the API
  • timeout: Request timeout for Ollama calls
  • max_iterations: Maximum tool execution iterations per chat
  • system_prompt: System prompt to include in all chat requests (optional)

Tools:

  • config_file: Path to tools configuration file
  • default_timeout: Default timeout for tool HTTP requests

Logging:

  • level: Log level (debug, info, warn, error)
  • format: Log output format (text, json)

Requirements

  • Go 1.19+
  • Ollama running locally (for chat features)
  • Network access for HTTP tools

License

GNU GPLv3

About

A lightweight http tool calling proxy for ollama

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors