Skip to content

Adapters ​

Adapters are the bridge between BobaMixer and AI providers. They handle communication, track usage, and normalize responses across different provider types.

Overview ​

BobaMixer supports three adapter types:

  1. HTTP Adapter - For REST API providers (Anthropic, OpenAI, etc.)
  2. Tool Adapter - For CLI tools and executables
  3. MCP Adapter - For Model Context Protocol integrations

Each adapter implements a common interface, allowing BobaMixer to work seamlessly with any provider.

HTTP Adapter ​

The HTTP adapter communicates with REST API providers.

Supported Providers ​

  • Anthropic - Claude models
  • OpenAI - GPT models
  • OpenRouter - Multi-provider gateway
  • Custom providers - Any REST API

Configuration ​

yaml
profile-name:
  adapter: http
  provider: anthropic|openai|openrouter|custom
  endpoint: https://api.example.com/v1/endpoint
  model: model-name
  max_tokens: 4096
  temperature: 0.7
  headers:
    Header-Name: value
    Authorization: "Bearer secret://api_key"

Anthropic Example ​

yaml
claude-sonnet:
  adapter: http
  provider: anthropic
  endpoint: https://api.anthropic.com/v1/messages
  model: claude-3-5-sonnet-20241022
  max_tokens: 4096
  temperature: 0.7
  headers:
    anthropic-version: "2023-06-01"
    x-api-key: "secret://anthropic_key"
  cost_per_1k:
    input: 0.015
    output: 0.075

OpenAI Example ​

yaml
gpt4-turbo:
  adapter: http
  provider: openai
  endpoint: https://api.openai.com/v1/chat/completions
  model: gpt-4-turbo-preview
  max_tokens: 4096
  temperature: 0.7
  headers:
    Authorization: "Bearer secret://openai_key"
    Content-Type: "application/json"
  cost_per_1k:
    input: 0.01
    output: 0.03

OpenRouter Example ​

yaml
openrouter-claude:
  adapter: http
  provider: openrouter
  endpoint: https://openrouter.ai/api/v1/chat/completions
  model: anthropic/claude-3-5-sonnet
  headers:
    Authorization: "Bearer secret://openrouter_key"
    HTTP-Referer: "https://github.com/yourusername"
  cost_per_1k:
    input: 0.015
    output: 0.075

Custom Provider Example ​

For any custom REST API:

yaml
custom-llm:
  adapter: http
  provider: custom
  endpoint: https://custom-api.example.com/v1/generate
  model: custom-model-v1
  headers:
    X-API-Key: "secret://custom_key"
    X-Custom-Header: "value"
  params:
    custom_param: value

HTTP Adapter Features ​

  • Automatic retry with exponential backoff
  • Request/response logging
  • Timeout handling
  • Usage extraction from API responses
  • Streaming support (coming soon)

Tool Adapter ​

The Tool adapter executes CLI tools and scripts, tracking their usage.

Use Cases ​

  • claude-code - CLI interface for Claude
  • Custom scripts - Your own AI wrappers
  • Local models - LLaMA, Mistral via ollama
  • Specialized tools - Domain-specific AI tools

Configuration ​

yaml
profile-name:
  adapter: tool
  command: /path/to/executable
  args: ["--arg1", "value1", "--arg2"]
  env:
    - VAR_NAME=value
    - API_KEY=secret://key_name
  working_dir: /path/to/workdir
  timeout_seconds: 300

Claude Code Example ​

yaml
claude-code:
  adapter: tool
  command: /usr/local/bin/claude
  args: ["--session", "work", "--format", "jsonl"]
  env:
    - ANTHROPIC_API_KEY=secret://anthropic_key
    - CLAUDE_OUTPUT_FORMAT=jsonl
  cost_per_1k:
    input: 0.015
    output: 0.075

Custom Script Example ​

yaml
my-ai-script:
  adapter: tool
  command: /home/user/scripts/ai-wrapper.sh
  args: ["--model", "best", "--verbose"]
  env:
    - API_KEY=secret://my_api_key
    - LOG_LEVEL=debug
  working_dir: /home/user/workspace
  timeout_seconds: 600

Ollama Example ​

yaml
ollama-llama:
  adapter: tool
  command: ollama
  args: ["run", "llama2", "--format", "json"]
  cost_per_1k:
    input: 0.0
    output: 0.0  # Free local model

Usage Tracking for Tools ​

Tools can report usage in two ways:

1. JSONL Output ​

Output usage as JSON Lines to stdout:

json
{"event":"usage","input_tokens":100,"output_tokens":50}

Example script:

bash
#!/bin/bash
# Your AI logic here
result=$(call_ai_api "$@")

# Output usage
echo '{"event":"usage","input_tokens":100,"output_tokens":50}'

# Output result
echo "$result"

2. Exit Code + Stderr ​

Return token counts via stderr and exit code:

bash
#!/bin/bash
# Your AI logic here

# Output to stderr (parsed by BobaMixer)
echo "INPUT_TOKENS=100" >&2
echo "OUTPUT_TOKENS=50" >&2

exit 0

Tool Adapter Features ​

  • Stdin/stdout/stderr handling
  • Environment variable injection
  • Working directory control
  • Timeout management
  • Automatic usage parsing
  • Error handling

MCP Adapter ​

The MCP (Model Context Protocol) adapter integrates with MCP-compatible tools.

What is MCP? ​

Model Context Protocol is a standard for connecting AI models with external tools and data sources. It enables:

  • File system access
  • Database queries
  • API integrations
  • Custom tool execution

Learn more at Model Context Protocol.

Configuration ​

yaml
profile-name:
  adapter: mcp
  command: command-name
  args: ["arg1", "arg2"]
  transport: stdio|sse
  env:
    - VAR=value

Filesystem MCP Example ​

yaml
mcp-filesystem:
  adapter: mcp
  command: npx
  args: ["@modelcontextprotocol/server-filesystem", "/path/to/docs"]
  transport: stdio
  env:
    - NODE_ENV=production

Database MCP Example ​

yaml
mcp-postgres:
  adapter: mcp
  command: npx
  args: ["@modelcontextprotocol/server-postgres"]
  transport: stdio
  env:
    - DATABASE_URL=secret://postgres_url

Custom MCP Server Example ​

yaml
my-mcp-server:
  adapter: mcp
  command: /path/to/my-mcp-server
  args: ["--config", "/path/to/config.json"]
  transport: stdio

MCP Adapter Features ​

  • Stdio and SSE transport
  • Tool discovery
  • Resource management
  • Prompt templates
  • Automatic context building

Creating Custom Adapters ​

You can create custom adapters by implementing the Adapter interface.

Adapter Interface ​

go
type Adapter interface {
    Name() string
    Execute(ctx context.Context, req Request) (Result, error)
}

HTTP-Based Custom Adapter ​

go
package myadapter

import (
    "context"
    "encoding/json"
    "github.com/royisme/bobamixer/internal/adapters"
    httpadapter "github.com/royisme/bobamixer/internal/adapters/http"
)

type MyAdapter struct {
    *httpadapter.Client
}

func New(name, endpoint string, headers map[string]string) *MyAdapter {
    return &MyAdapter{
        Client: httpadapter.New(name, endpoint, headers),
    }
}

func (a *MyAdapter) Execute(ctx context.Context, req adapters.Request) (adapters.Result, error) {
    // Transform request for your API
    providerReq := map[string]interface{}{
        "model": req.Model,
        "prompt": string(req.Payload),
        "max_tokens": 2048,
    }

    payload, _ := json.Marshal(providerReq)
    req.Payload = payload

    // Execute via base HTTP client
    return a.Client.Execute(ctx, req)
}

Tool-Based Custom Adapter ​

go
package mytooladapter

import (
    "context"
    "os/exec"
    "github.com/royisme/bobamixer/internal/adapters"
)

type MyToolAdapter struct {
    command string
    args    []string
}

func New(command string, args []string) *MyToolAdapter {
    return &MyToolAdapter{
        command: command,
        args:    args,
    }
}

func (a *MyToolAdapter) Name() string {
    return "my-tool"
}

func (a *MyToolAdapter) Execute(ctx context.Context, req adapters.Request) (adapters.Result, error) {
    cmd := exec.CommandContext(ctx, a.command, a.args...)
    cmd.Stdin = bytes.NewReader(req.Payload)

    output, err := cmd.CombinedOutput()
    if err != nil {
        return adapters.Result{Success: false}, err
    }

    return adapters.Result{
        Success: true,
        Output:  output,
        Usage: adapters.Usage{
            // Parse usage from output
        },
    }, nil
}

Testing Custom Adapters ​

go
func TestMyAdapter(t *testing.T) {
    adapter := New("https://api.test.com", map[string]string{
        "Authorization": "Bearer test-key",
    })

    result, err := adapter.Execute(context.Background(), adapters.Request{
        Payload: []byte("test prompt"),
        Model:   "test-model",
    })

    assert.NoError(t, err)
    assert.True(t, result.Success)
    assert.NotEmpty(t, result.Output)
}

Usage Estimation ​

BobaMixer tracks usage with three estimation levels:

1. Exact Estimation ​

From API response usage field:

json
{
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Accuracy: 100%

2. Mapped Estimation ​

From pricing configuration:

yaml
cost_per_1k:
  input: 0.015
  output: 0.075

Accuracy: 95-99% (depends on pricing accuracy)

3. Heuristic Estimation ​

Character-based estimation when no other data available:

  • Input tokens ≈ characters / 4
  • Output tokens ≈ characters / 4

Accuracy: 70-90%

Checking Estimation Level ​

bash
# View estimates in reports
boba report --format json | jq '.[] | select(.estimate != "exact")'

# Statistics by estimation level
boba stats --by-estimate

Best Practices ​

1. Choose the Right Adapter ​

  • HTTP: For production API providers
  • Tool: For local models, custom scripts
  • MCP: For tool-augmented workflows

2. Secure API Keys ​

yaml
# Always use secret:// references
headers:
  x-api-key: "secret://anthropic_key"  # ✅ Good

# Never hardcode
headers:
  x-api-key: "sk-ant-xxxxx"  # ❌ Bad

3. Set Appropriate Timeouts ​

yaml
# For quick tasks
timeout_seconds: 30

# For complex tasks
timeout_seconds: 300

# For very long tasks
timeout_seconds: 600

4. Monitor Usage Accuracy ​

bash
# Check estimation accuracy
boba stats --by-estimate --7d

# If too many heuristics, add pricing config

5. Test Adapters ​

bash
# Verify adapter configuration
boba doctor

# Test profile
boba use profile-name

# Check connectivity
boba route test "test message"

6. Handle Errors Gracefully ​

yaml
# Use fallback profiles
rules:
  - id: primary-with-fallback
    if: "ctx_chars > 0"
    use: primary-profile
    fallback: backup-profile

Troubleshooting ​

HTTP Adapter Issues ​

bash
# Check endpoint connectivity
curl -v https://api.anthropic.com/v1/messages

# Verify headers
boba doctor --verbose

# Check API key
grep "anthropic_key" ~/.boba/secrets.yaml

Tool Adapter Issues ​

bash
# Verify command exists
which claude-code

# Test command manually
/path/to/command --help

# Check permissions
ls -l /path/to/command

MCP Adapter Issues ​

bash
# Test MCP server
npx @modelcontextprotocol/server-filesystem /path --test

# Check logs
tail -f ~/.boba/logs/boba.log

# Verify transport
# stdio: uses stdin/stdout
# sse: uses Server-Sent Events

Usage Tracking Issues ​

bash
# Check estimation level
boba stats --by-estimate

# View detailed usage
boba report --format json | jq '.[] | {profile, estimate, tokens}'

# Add pricing config if needed
boba edit pricing

Examples ​

Multi-Provider Setup ​

yaml
# Production: Anthropic
prod-claude:
  adapter: http
  provider: anthropic
  endpoint: https://api.anthropic.com/v1/messages
  model: claude-3-5-sonnet-20241022
  headers:
    x-api-key: "secret://anthropic_key"

# Development: OpenAI
dev-gpt:
  adapter: http
  provider: openai
  endpoint: https://api.openai.com/v1/chat/completions
  model: gpt-4o-mini
  headers:
    Authorization: "Bearer secret://openai_key"

# Local: Ollama
local-llama:
  adapter: tool
  command: ollama
  args: ["run", "llama2"]
  cost_per_1k:
    input: 0.0
    output: 0.0

Hybrid Workflow ​

yaml
# API for production
api-profile:
  adapter: http
  provider: anthropic
  # ...

# CLI tool for development
tool-profile:
  adapter: tool
  command: claude-code
  # ...

# MCP for data access
mcp-profile:
  adapter: mcp
  command: npx
  # ...

Next Steps ​

Resources ​