06 · Providers and Models
This chapter explains how pi-web discovers available models, how to connect built-in providers and custom OpenAI-compatible gateways, and the data source and filtering mechanism behind the dropdowns on the settings page.
1. Model Sources: Two Kinds of Providers
| Type | Credential storage | Typical providers |
|---|---|---|
| Built-in provider | ~/.pi/agent/auth.json (written by pi login) | anthropic, openai, google, etc. |
| Custom provider | ~/.pi/agent/models.json (hand-authored) | NewAPI gateway, local inference services, DashScope, etc. |
Both kinds of providers are managed uniformly by the pi SDK’s ModelRegistry. ModelRegistry.getAvailable() returns only the models that have credentials—built-in provider credentials come from auth.json, while custom provider credentials are embedded in the apiKey field of models.json.
Relevant entry point: packages/server/src/config/model-options.ts:listModelOptions
const authStorage = AuthStorage.create(join(agentDir, "auth.json"));
const registry = ModelRegistry.create(authStorage, join(agentDir, "models.json"));
const models = registry.getAvailable();2. Connecting Built-in Providers (auth.json)
Log in once and the credentials are persisted—no extra configuration needed:
pi # follow the prompts to complete OAuth / API key setupAfter login, ~/.pi/agent/auth.json is maintained by the pi SDK; pi-web reads it at runtime, with no need to set env variables.
To inject credentials non-interactively on a different machine or in a container, you can also pass them through via env variables (see 05-configuration.md for the relevant variables):
ANTHROPIC_API_KEY=sk-ant-... pnpm dev
OPENAI_API_KEY=sk-... pnpm devThe env variables and auth.json take effect additively; precedence follows the pi SDK’s internal logic.
3. Custom OpenAI-compatible Gateway (models.json)
Custom providers must be written in ~/.pi/agent/models.json, never in auth.json.
The agent config directory defaults to
~/.pi/agentand can be overridden via the envPI_CODING_AGENT_DIR(seelib/app/config.ts:resolveAgentDir); after overriding,models.jsonmust be placed in the corresponding directory.
3.1 Minimal Configuration
{
"providers": {
"my-gateway": {
"name": "My Gateway",
"baseUrl": "https://example.com/v1",
"apiKey": "sk-...",
"api": "openai-completions",
"models": [
{
"id": "some-model",
"name": "Some Model",
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
}
}
}Required fields:
| Field | Description |
|---|---|
baseUrl | OpenAI-compatible endpoint prefix (up to /v1, not including /chat/completions) |
apiKey | Gateway authentication token |
api | Connection protocol identifier; for OpenAI-compatible chat completions gateways, set "openai-completions" (can be specified at the provider level or per individual model) |
models | At least one model description; id is the model name used when calling |
The optional fields (cost, contextWindow, maxTokens) are recommended for the pi SDK’s cost estimation; when omitted, the SDK uses 0 or default values.
3.2 Multi-provider Example
{
"providers": {
"newapi": {
"name": "NewAPI Gateway",
"baseUrl": "https://newapi.example.com/v1",
"apiKey": "sk-newapi-...",
"api": "openai-completions",
"models": [
{
"id": "gpt-4o",
"name": "GPT-4o (via NewAPI)",
"input": ["text", "image"],
"contextWindow": 128000,
"maxTokens": 16384,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
},
"local-llm": {
"name": "Local Ollama",
"baseUrl": "http://localhost:11434/v1",
"apiKey": "ollama",
"api": "openai-completions",
"models": [
{
"id": "llama3.2",
"name": "Llama 3.2",
"input": ["text"],
"contextWindow": 32768,
"maxTokens": 4096,
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
}
]
}
}
}3.3 Verifying the Configuration Took Effect
pi --list-modelsIf the output includes the models under my-gateway, the configuration succeeded. This command reads the same ModelRegistry data that pi-web reads, making it the most direct way to verify.
Don’t see your custom model? It’s usually because the config is in the wrong location or a required field is missing—see the troubleshooting checklist at 18-troubleshooting-faq.md · 2.1 Custom provider auth 401.
4. Model Dropdown on the Settings Page: Data Source and Filtering
4.1 Endpoint
The searchable provider/model dropdown on the settings page is driven by the frontend calling the following endpoint:
GET /api/config/modelsResponse format:
{
"providers": ["anthropic", "openai", "my-gateway"],
"models": [
{ "provider": "anthropic", "id": "claude-opus-4-5", "name": "Claude Opus 4.5" },
{ "provider": "my-gateway", "id": "some-model", "name": "Some Model" }
]
}The server mounts this route in packages/server/src/config/config-routes.ts (the path /config/models must come before /config/:domain so it isn’t treated as an unknown domain).
The data comes from listModelOptions(agentDir) (packages/server/src/config/model-options.ts), i.e. an in-process call to ModelRegistry.getAvailable()—only models with configured credentials appear in the dropdown.
4.2 Hiding Specific Providers
Use the env variable PI_WEB_HIDE_PROVIDERS to remove providers you don’t want to expose from the dropdown:
PI_WEB_HIDE_PROVIDERS=anthropic,openai pnpm devThe filtering logic lives in packages/server/src/config/model-options-filter.ts:33 (excludeProviders), matching provider names exactly (case-sensitive) and removing them from both the providers list and the models list. When PI_WEB_HIDE_PROVIDERS is empty, no filtering is applied (zero-copy fast path). The call site on the /config/models route is at lib/app/pi-handler.ts:338.
The in-session get_available_models RPC (GET /sessions/:id/models) applies the same switch—filtered via the sibling function excludeProviderModels (model-options-filter.ts:49), with the call site at packages/server/src/http/routes/query-routes.ts:113, ensuring the dropdown and the runtime selectable set stay consistent.
5. Real-world Cases and Common Pitfalls
5.1 NewAPI Gateway
NewAPI is a typical OpenAI-compatible aggregation gateway. The api field is fixed to "openai-completions", and baseUrl should be set to the /v1 prefix (the gateway then routes internally to each upstream):
{
"providers": {
"newapi": {
"name": "NewAPI",
"baseUrl": "https://your-newapi-host/v1",
"apiKey": "sk-...",
"api": "openai-completions",
"models": [ ... ]
}
}
}5.2 The DashScope Key-Endpoint Binding Pitfall
DashScope’s (Alibaba Cloud Bailian) image generation has its key tightly bound to the service endpoint: using the wrong key for an endpoint results in a direct 401. This pitfall was discovered through real-world testing of the AIGC image tools; see the endpoint constant at packages/tool-kit/src/aigc/tools/image-generation.ts:29.
- The image API uses the native DashScope protocol (
input/parametersstructure), not the OpenAI-compatible/imagesendpoint. - The synchronous image endpoint path is
/api/v1/services/aigc/multimodal-generation/generation(the base defaults to the token plan domainhttps://token-plan.cn-beijing.maas.aliyuncs.com/api/v1, configurable via the envDASHSCOPE_TOKENPLAN_BASE_URL). - Symptom: the image generation request returns 401, with an error message that may contain “channel does not exist” or an empty model name.
Cause: the token plan key (DASHSCOPE_API_KEY) is invalid against the official domain dashscope.aliyuncs.com and must hit the token plan endpoint; conversely, a MAAS token from the official console hitting the token plan endpoint also returns 401.
Solution:
- Confirm the key pairs with the endpoint: token plan key with token plan base, official key with official domain—never mix them.
- The base/endpoint of the custom provider (or the AIGC tool’s env) must match the service endpoint corresponding to the key in use.
- We recommend splitting text chat and image generation into two separate provider entries, each configured with its corresponding key.
See 11-aigc-tools.md for the full explanation of endpoints and keys; see 18-troubleshooting-faq.md · 2.1 Custom provider auth 401 for the troubleshooting steps on the 401 symptom.
6. Full Connection Steps
-
Confirm pi is logged in (built-in providers):
pi --list-models # you should see built-in models such as anthropic/openai -
Author
~/.pi/agent/models.json(custom provider; see the examples in Section 3). -
Verify the model appears in the list:
pi --list-models # the custom provider's models should appear -
Start pi-web (development mode):
pnpm dev # next dev — http://localhost:3000 -
Confirm the dropdown on the settings page: open Settings → model dropdown → the models under the custom provider should be searchable.
-
(Optional) Hide providers you don’t want to expose:
PI_WEB_HIDE_PROVIDERS=anthropic pnpm dev
If any step fails (401, model not showing up, empty dropdown), first cross-reference 18-troubleshooting-faq.md · 2. Provider / Model Issues.
Related Links
- 05-configuration.md — Full env variable table, including
PI_WEB_DEFAULT_PROVIDER/PI_WEB_DEFAULT_MODEL - 12-config-ui.md — Frontend implementation of the searchable provider/model dropdown on the settings page
- 11-aigc-tools.md — AIGC image tools, with detailed explanation of DashScope endpoints and keys
- 13-http-api-reference.md — Complete API documentation for the
GET /api/config/modelsendpoint - 18-troubleshooting-faq.md — Troubleshooting for provider auth 401, models not showing up, and other common issues