@esuyo/pi-esuyo-custom-provider
Register custom OpenAI-compatible providers in Pi.dev via JSON config
Package details
Install @esuyo/pi-esuyo-custom-provider from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@esuyo/pi-esuyo-custom-provider- Package
@esuyo/pi-esuyo-custom-provider- Version
1.0.11- Published
- Sep 4, 2026
- Downloads
- 487/mo · 192/wk
- Author
- migsperez
- License
- MIT
- Types
- extension
- Size
- 32.2 KB
- Dependencies
- 0 dependencies · 0 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@esuyo/pi-esuyo-custom-provider
Add any OpenAI-compatible provider to Pi.dev — local models, corporate proxies, or custom API gateways. Configure everything in a single JSON file, no coding required.
Installation
pi install npm:@esuyo/pi-esuyo-custom-provider
Quick start
Create ~/.pi/agent/custom-providers.json:
{
"providers": [
{
"name": "ollama",
"label": "Ollama Local",
"baseUrl": "http://localhost:11434/v1",
"apiKey": "ollama",
"fetchModels": true
}
]
}
Run /reload in Pi (or restart it), then open the model picker with /model — your provider's models will appear in the list.
Configuration
Provider fields
| Field | Required | Description |
|---|---|---|
name |
✅ | Provider ID used in /model (e.g. my-gateway). |
label |
— | Display name shown in the UI. Defaults to name. |
baseUrl |
✅ | OpenAI-compatible API endpoint (e.g. http://localhost:11434/v1). |
apiKey |
✅ | API key. Supports env vars ($MY_KEY / ${MY_KEY}) and shell commands (!command). |
fetchModels |
— | Auto-discover models from {baseUrl}/models. Default: false. |
models |
— | Static model definitions (merged with discovered models when fetchModels: true). |
contextWindow |
— | Provider-level default context window (tokens). Applied to every model unless the model defines its own. |
maxTokens |
— | Provider-level default max output tokens. Applied to every model unless the model defines its own. |
headers |
— | Extra HTTP headers sent with every request. |
sendSessionHeaders |
— | Send per-conversation x-opencode-session / x-opencode-client headers (see below). Default: false. |
compat |
— | Provider compatibility flags (see below). |
API key resolution
Same syntax as Pi's models.json:
| Syntax | Description |
|---|---|
$ENV_VAR / ${ENV_VAR} |
Read from environment variable |
!command |
Execute shell command, stdout is the value |
$$ |
Literal $ |
$! |
Literal ! |
| Plain string | Used as-is |
Model fields
| Field | Default | Description |
|---|---|---|
id |
— | Model identifier sent to the API (required). |
name |
id |
Human-readable label. |
reasoning |
false |
Supports extended thinking. |
input |
["text"] |
Input types: ["text"] or ["text", "image"]. |
contextWindow |
— | Max context window in tokens. Falls back to the provider-level contextWindow, otherwise Pi.dev decides. |
maxTokens |
— | Max output tokens. Falls back to the provider-level maxTokens, otherwise Pi.dev decides. |
cost |
all zeros | Per-million-token rates { input, output, cacheRead, cacheWrite }. |
Session headers
Pi only sends x-opencode-session for its built-in opencode providers. If your
gateway needs the current conversation id, opt in per provider:
{
"providers": [
{
"name": "gateway",
"baseUrl": "https://gateway.corp.com/v1",
"apiKey": "$GATEWAY_API_KEY",
"sendSessionHeaders": true
}
]
}
With sendSessionHeaders: true, every request for that provider gets
x-opencode-session: <live session id> (read fresh per request, so it survives
new/resume/fork) and x-opencode-client: pi when not already set. Auth headers
are never touched. Providers without the flag are unchanged.
Alternatively, map the session id to any header with $PI_SESSION_ID (or
${PI_SESSION_ID}) in headers — presence of the variable is its own opt-in,
independent of the flag. The extension (not Pi) owns this placeholder:
{
"providers": [
{
"name": "gateway",
"baseUrl": "https://gateway.corp.com/v1",
"apiKey": "$GATEWAY_API_KEY",
"headers": { "x-opencode-session": "$PI_SESSION_ID" }
}
]
}
Headers containing $PI_SESSION_ID are stripped before pi.registerProvider
is called and never reach Pi core — Pi resolves every provider header as an
env-var template and would otherwise throw
Failed to resolve provider "<id>" header "<key>" from environment variable: PI_SESSION_ID (surfaced as API key auth failed). The extension holds these
templates back and substitutes the live session id per request in the
before_provider_headers hook (overwriting unconditionally).
The value is substituted per request with the live session id. With no live session the header
is deleted (null), never sent literally. Authorization is never touched.
Compatibility flags
| Flag | When to use |
|---|---|
supportsDeveloperRole: false |
Servers that don't understand the developer role (Ollama, vLLM). |
supportsReasoningEffort: false |
Servers that don't support reasoning_effort. |
supportsUsageInStreaming: false |
Servers lacking streaming usage support. |
maxTokensField: "max_tokens" |
Use max_tokens instead of max_completion_tokens. |
requiresToolResultName: true |
When tool results need a name field. |
Examples
Local model server (Ollama, vLLM, LM Studio)
{
"providers": [
{
"name": "local",
"baseUrl": "http://localhost:11434/v1",
"apiKey": "ollama",
"fetchModels": true,
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false
}
}
]
}
API gateway with static models
{
"providers": [
{
"name": "gateway",
"label": "Corporate AI Gateway",
"baseUrl": "https://gateway.corp.com/v1",
"apiKey": "$GATEWAY_API_KEY",
"models": [
{
"id": "gpt-4o",
"input": ["text", "image"],
"contextWindow": 128000,
"maxTokens": 16384,
"cost": { "input": 2.5, "output": 10, "cacheRead": 0.5, "cacheWrite": 1.25 }
}
],
"headers": {
"X-Corp-Auth": "$CORP_AUTH_TOKEN"
}
}
]
}
Multiple providers
{
"providers": [
{
"name": "local",
"baseUrl": "http://localhost:11434/v1",
"apiKey": "ollama",
"fetchModels": true
},
{
"name": "proxy",
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "$PROXY_KEY",
"models": [
{ "id": "claude-sonnet-4", "input": ["text", "image"], "contextWindow": 200000 }
]
}
]
}
Environment variables
| Variable | Default | Description |
|---|---|---|
PI_CUSTOM_PROVIDERS_CONFIG |
~/.pi/agent/custom-providers.json |
Custom path to the config file. |
Updating
pi update npm:@esuyo/pi-esuyo-custom-provider
Or run /reload after updating the config file — no reinstall needed for config changes.