Custom Models

Add custom model providers via ~/.pi/agent/models.json. This file defines providers, their API endpoints, authentication, and available models.

Minimal Example

A local Ollama provider with one model:

{
  "providers": {
    "ollama": {
      "baseUrl": "http://localhost:11434/v1",
      "api": "openai-completions",
      "models": {
        "llama3.1": {}
      }
    }
  }
}

Full Example

A provider definition with all available fields:

{
  "providers": {
    "my-provider": {
      "baseUrl": "https://api.example.com/v1",
      "api": "openai-completions",
      "apiKey": "sk-xxx",
      "authHeader": "X-Api-Key",
      "headers": {
        "X-Custom-Header": "value"
      },
      "models": {
        "my-model-large": {
          "name": "My Model (Large)",
          "api": "openai-completions",
          "reasoning": true,
          "input": ["text", "image"],
          "contextWindow": 128000,
          "maxTokens": 8192,
          "cost": {
            "input": 3.0,
            "output": 15.0,
            "cacheRead": 0.3,
            "cacheWrite": 3.75
          }
        },
        "my-model-small": {
          "name": "My Model (Small)"
        }
      },
      "modelOverrides": {
        "gpt-4o": {
          "maxTokens": 16384
        }
      }
    }
  }
}

Supported APIs

APIDescription
openai-completionsOpenAI Chat Completions API
openai-responsesOpenAI Responses API
anthropic-messagesAnthropic Messages API
google-generative-aiGoogle Generative AI (AI Studio)

Provider Configuration

Fields

FieldTypeRequiredDescription
baseUrlstringYesAPI base URL
apistringYesAPI type (see table above)
apiKeystringNoAPI key for authentication
authHeaderstringNoCustom auth header name (default: Authorization with Bearer prefix)
headersobjectNoAdditional HTTP headers for all requests
modelsobjectNoMap of model ID to model configuration
modelOverridesobjectNoPer-model overrides applied to built-in models

Value Resolution

The apiKey and headers values support three formats:

FormatExampleDescription
Shell command"!op read op://vault/key"Runs command, uses stdout (prefix with !)
Environment variable"$MY_API_KEY"Reads from environment (prefix with $)
Literal"sk-abc123"Used as-is

Custom Headers Example

{
  "providers": {
    "custom-api": {
      "baseUrl": "https://api.example.com/v1",
      "api": "openai-completions",
      "apiKey": "$CUSTOM_API_KEY",
      "headers": {
        "X-Organization": "my-org",
        "X-Project": "!cat .project-id"
      },
      "models": {
        "custom-model": {}
      }
    }
  }
}

Model Configuration

Fields

FieldTypeDefaultDescription
idstring(key)Model identifier (defaults to the object key)
namestring(id)Human-readable display name
apistring(provider)Override the provider-level API type
reasoningbooleanfalseWhether the model supports extended thinking
inputstring[]["text"]Supported input types: "text", "image"
contextWindownumber128000Maximum context window in tokens
maxTokensnumber8192Maximum output tokens per response
costobject-Cost per million tokens

Cost Object

FieldTypeDescription
inputnumberCost per 1M input tokens (USD)
outputnumberCost per 1M output tokens (USD)
cacheReadnumberCost per 1M cache-read tokens (USD)
cacheWritenumberCost per 1M cache-write tokens (USD)

Overriding Built-in Providers

You can override built-in providers to route traffic through a proxy or modify defaults. The custom configuration is merged with the built-in definition:

{
  "providers": {
    "anthropic": {
      "baseUrl": "https://my-proxy.example.com/anthropic/v1"
    }
  }
}

This keeps all built-in Anthropic models but routes requests through your proxy. Only the fields you specify are overridden; everything else retains its default value.

Per-Model Overrides

Use modelOverrides to adjust specific models within a provider without redefining them entirely:

{
  "providers": {
    "openai": {
      "modelOverrides": {
        "gpt-4o": {
          "maxTokens": 16384,
          "contextWindow": 256000
        }
      }
    }
  }
}

OpenAI Compatibility

Many providers offer OpenAI-compatible APIs with slight differences. Use the compat field to handle these:

{
  "providers": {
    "my-compat-provider": {
      "baseUrl": "https://api.example.com/v1",
      "api": "openai-completions",
      "apiKey": "$API_KEY",
      "compat": {
        "disableStreaming": false,
        "disableTools": false,
        "disableSystemMessages": false,
        "disableVision": false,
        "forceSimpleToolResults": true,
        "skipProviderMetadata": false,
        "forceMaxTokens": false
      },
      "models": {
        "compat-model": {}
      }
    }
  }
}

Compat Flags

FlagDefaultDescription
disableStreamingfalseFall back to non-streaming requests
disableToolsfalseRemove tool definitions from requests
disableSystemMessagesfalseConvert system messages to user messages
disableVisionfalseStrip image content from messages
forceSimpleToolResultsfalseSimplify tool result format for compatibility
skipProviderMetadatafalseSkip parsing provider-specific metadata
forceMaxTokensfalseAlways include max_tokens in requests

OpenRouter

{
  "providers": {
    "openrouter": {
      "baseUrl": "https://openrouter.ai/api/v1",
      "api": "openai-completions",
      "apiKey": "$OPENROUTER_API_KEY",
      "models": {
        "anthropic/claude-sonnet-4-20250514": {
          "name": "Claude Sonnet (via OpenRouter)",
          "contextWindow": 200000,
          "maxTokens": 8192
        },
        "google/gemini-2.5-pro-preview": {
          "name": "Gemini 2.5 Pro (via OpenRouter)",
          "contextWindow": 1048576,
          "maxTokens": 65536
        }
      }
    }
  }
}

Vercel AI Gateway

{
  "providers": {
    "vercel-gateway": {
      "baseUrl": "https://gateway.vercel.ai/v1",
      "api": "openai-completions",
      "apiKey": "$VERCEL_API_KEY",
      "headers": {
        "X-Vercel-AI-Gateway-Provider": "anthropic"
      },
      "models": {
        "claude-sonnet-4-20250514": {
          "name": "Claude Sonnet (via Vercel)",
          "api": "anthropic-messages",
          "contextWindow": 200000,
          "maxTokens": 8192
        }
      }
    }
  }
}