From ce6a67fc948c63f991f7f649032ee237d530146b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 23 Jun 2026 11:51:33 +0200 Subject: [PATCH] fix(coding-agent): allow custom providers to use stored auth closes #5953 --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/docs/models.md | 6 ++++-- packages/coding-agent/src/core/model-registry.ts | 6 ++---- packages/coding-agent/test/model-registry.test.ts | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e2650200..b71110ca 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixed - Fixed `pi --resume` to load user package themes and resolve automatic light/dark theme settings. +- Fixed `models.json` custom providers so stored credentials can satisfy auth without a redundant provider-level `apiKey` ([#5953](https://github.com/earendil-works/pi/issues/5953)). ## [0.79.10] - 2026-06-22 diff --git a/packages/coding-agent/docs/models.md b/packages/coding-agent/docs/models.md index 5679981a..d17e9928 100644 --- a/packages/coding-agent/docs/models.md +++ b/packages/coding-agent/docs/models.md @@ -34,7 +34,7 @@ For local models (Ollama, LM Studio, vLLM), only `id` is required per model: } ``` -The `apiKey` is required but Ollama ignores it, so any value works. +The `apiKey` value is a placeholder because Ollama ignores it. pi still treats models as requiring auth before they appear in `/model`, so keyless local servers should keep a dummy value, save a key for that provider with `/login`, or pass `--api-key` when selecting the model. Some OpenAI-compatible servers do not understand the `developer` role used for reasoning-capable models. For those providers, set `compat.supportsDeveloperRole` to `false` so pi sends the system prompt as a `system` message instead. If the server also does not support `reasoning_effort`, set `compat.supportsReasoningEffort` to `false` too. @@ -135,12 +135,14 @@ Set `api` at provider level (default for all models) or model level (override pe |-------|-------------| | `baseUrl` | API endpoint URL | | `api` | API type (see above) | -| `apiKey` | API key (see value resolution below) | +| `apiKey` | Optional API key config (see value resolution below). Omit it when auth is provided by `/login`/`auth.json` or CLI `--api-key`. | | `headers` | Custom headers (see value resolution below) | | `authHeader` | Set `true` to add `Authorization: Bearer ` automatically | | `models` | Array of model configurations | | `modelOverrides` | Per-model overrides for built-in models on this provider | +For providers with `models`, non-built-in provider configs need `baseUrl` and an `api` value at either provider or model level. `apiKey` is not required to load the file: models become available when auth is configured through `/login`/`auth.json`, CLI `--api-key`, or provider `apiKey`. If no auth is configured, the models load but stay unavailable in `/model` and `--list-models`. + ### Value Resolution The `apiKey` and `headers` fields support command execution, environment interpolation, and literals: diff --git a/packages/coding-agent/src/core/model-registry.ts b/packages/coding-agent/src/core/model-registry.ts index 8f86bf6c..70f39394 100644 --- a/packages/coding-agent/src/core/model-registry.ts +++ b/packages/coding-agent/src/core/model-registry.ts @@ -546,13 +546,11 @@ export class ModelRegistry { ); } } else if (!isBuiltIn) { - // Non-built-in providers with custom models require endpoint + auth. + // Non-built-in providers with custom models require an endpoint. + // Auth can come from auth.json, --api-key, or provider request config. if (!providerConfig.baseUrl) { throw new Error(`Provider ${providerName}: "baseUrl" is required when defining custom models.`); } - if (!providerConfig.apiKey) { - throw new Error(`Provider ${providerName}: "apiKey" is required when defining custom models.`); - } } // Built-in providers with custom models: baseUrl/apiKey/api are optional, // inherited from built-in models. Auth comes from env vars / auth storage. diff --git a/packages/coding-agent/test/model-registry.test.ts b/packages/coding-agent/test/model-registry.test.ts index 2405e557..fb559e1d 100644 --- a/packages/coding-agent/test/model-registry.test.ts +++ b/packages/coding-agent/test/model-registry.test.ts @@ -246,9 +246,10 @@ describe("ModelRegistry", () => { expect(model?.baseUrl).toBe("https://openrouter.ai/api/v1"); }); - test("non-built-in provider custom models still require baseUrl and apiKey", () => { + test("non-built-in provider custom models still require baseUrl", () => { writeRawModelsJson({ "my-custom-provider": { + apiKey: "test-key", models: [ { id: "my-model",