Skip to main content
ChatCLI supports tool calls via native API for OpenAI and Anthropic, replacing the XML-embedded-in-prompt approach with structured API calls. This improves accuracy, reduces token consumption, and enables cache optimizations.

Why Native Tool Use?

AspectXML in PromptNative API
AccuracyDepends on the model parsing XMLStructured by the API, unambiguous
TokensXML consumes context window tokensSeparate fields, more efficient
CacheNo optimizationcache_control:ephemeral on Anthropic
ValidationManualAutomatic by the provider API
CompatibilityAny providerOpenAI and Anthropic (others continue with XML)

Architecture

ToolAwareClient Interface

The ToolAwareClient interface extends the base LLMClient with tool support:
type ToolAwareClient interface {
    LLMClient
    SendPromptWithTools(ctx context.Context, prompt string, history []models.Message,
        tools []models.ToolDefinition, maxTokens int) (*models.LLMResponse, error)
    SupportsNativeTools() bool
}

Automatic Detection

Detection is done via type assertion, with no configuration required:
if client.IsToolAware(c) {
    tac, _ := client.AsToolAware(c)
    resp, err := tac.SendPromptWithTools(ctx, prompt, history, tools, maxTokens)
    // resp.ToolCalls contains the structured calls
}
Providers that do not implement ToolAwareClient continue to work normally via SendPrompt.

Data Types

Defines a tool available to the model:
type ToolDefinition struct {
    Type     string          `json:"type"`     // "function"
    Function ToolFunctionDef `json:"function"`
}

type ToolFunctionDef struct {
    Name        string                 `json:"name"`
    Description string                 `json:"description"`
    Parameters  map[string]interface{} `json:"parameters"` // JSON Schema
}
Represent a tool call by the model and its result:
type ToolCall struct {
    ID        string                 `json:"id"`
    Type      string                 `json:"type"`       // "function"
    Name      string                 `json:"name"`
    Arguments map[string]interface{} `json:"arguments"`
}

type ToolResult struct {
    ToolCallID string `json:"tool_call_id"`
    Content    string `json:"content"`
    IsError    bool   `json:"is_error,omitempty"`
}
Unified response that can contain text and/or tool calls:
type LLMResponse struct {
    Content    string     `json:"content"`
    ToolCalls  []ToolCall `json:"tool_calls,omitempty"`
    Usage      *UsageInfo `json:"usage,omitempty"`
    StopReason string     `json:"stop_reason,omitempty"`
}

Provider Implementations

Uses the tools field in the Chat Completions API:
  • Sends tools as a tools array with tool_choice: "auto"
  • Processes tool_calls in choices[0].message
  • tool messages in the history link results to the tool_call_id

ContentBlock with Cache Control

For Anthropic, the system prompt is split into blocks with cache control:
type ContentBlock struct {
    Type         string        `json:"type"`
    Text         string        `json:"text"`
    CacheControl *CacheControl `json:"cache_control,omitempty"`
}

type CacheControl struct {
    Type string `json:"type"` // "ephemeral"
}
The cache_control:ephemeral tells Anthropic that the system prompt block can be cached between requests, significantly reducing latency and cost in long conversations.

Fallback Integration

The fallback chain (llm/fallback) supports SendPromptWithTools automatically. Providers without native tool use support are skipped in the tool call chain but remain available for plain text requests.