fix(coding-agent): make config env references explicit

closes #5095
This commit is contained in:
Armin Ronacher
2026-05-28 11:57:10 +02:00
parent 5b31ffd744
commit 3e9f717445
17 changed files with 930 additions and 71 deletions

View File

@@ -101,7 +101,7 @@ Use `google-generative-ai` with a `baseUrl` to add models from Google AI Studio,
"my-google": {
"baseUrl": "https://generativelanguage.googleapis.com/v1beta",
"api": "google-generative-ai",
"apiKey": "GEMINI_API_KEY",
"apiKey": "$GEMINI_API_KEY",
"models": [
{
"id": "gemma-4-31b-it",
@@ -143,22 +143,31 @@ Set `api` at provider level (default for all models) or model level (override pe
### Value Resolution
The `apiKey` and `headers` fields support three formats:
The `apiKey` and `headers` fields support command execution, environment interpolation, and literals:
- **Shell command:** `"!command"` executes and uses stdout
- **Shell command:** `"!command"` at the start executes the whole value as a command and uses stdout
```json
"apiKey": "!security find-generic-password -ws 'anthropic'"
"apiKey": "!op read 'op://vault/item/credential'"
```
- **Environment variable:** Uses the value of the named variable
- **Environment interpolation:** `"$ENV_VAR"` or `"${ENV_VAR}"` uses the value of the named variable. Interpolation works inside larger literals.
```json
"apiKey": "MY_API_KEY"
"apiKey": "$MY_API_KEY"
"apiKey": "${KEY_PREFIX}_${KEY_SUFFIX}"
```
`$FOO_BAR` is the variable `FOO_BAR`; use `${FOO}_BAR` when `BAR` is literal text. Missing environment variables make the value unresolved.
- **Escapes:** `"$$"` emits a literal `"$"`; `"$!"` emits a literal `"!"` without triggering command execution.
```json
"apiKey": "$$literal-dollar-prefix"
"apiKey": "$!literal-bang-prefix"
```
- **Literal value:** Used directly
```json
"apiKey": "sk-..."
```
Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup.
For `models.json`, shell commands are resolved at request time. pi intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and pi cannot infer the right one.
If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
@@ -172,10 +181,10 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
"providers": {
"custom-proxy": {
"baseUrl": "https://proxy.example.com/v1",
"apiKey": "MY_API_KEY",
"apiKey": "$MY_API_KEY",
"api": "anthropic-messages",
"headers": {
"x-portkey-api-key": "PORTKEY_API_KEY",
"x-portkey-api-key": "$PORTKEY_API_KEY",
"x-secret": "!op read 'op://vault/item/secret'"
},
"models": [...]
@@ -268,7 +277,7 @@ To merge custom models into a built-in provider, include the `models` array:
"providers": {
"anthropic": {
"baseUrl": "https://my-proxy.example.com/v1",
"apiKey": "ANTHROPIC_API_KEY",
"apiKey": "$ANTHROPIC_API_KEY",
"api": "anthropic-messages",
"models": [...]
}
@@ -327,7 +336,7 @@ Some Anthropic models require adaptive thinking (`thinking.type: "adaptive"` plu
"anthropic-proxy": {
"baseUrl": "https://proxy.example.com",
"api": "anthropic-messages",
"apiKey": "ANTHROPIC_PROXY_KEY",
"apiKey": "$ANTHROPIC_PROXY_KEY",
"compat": {
"supportsEagerToolInputStreaming": false,
"supportsLongCacheRetention": true,
@@ -405,7 +414,7 @@ Example:
"providers": {
"openrouter": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": "OPENROUTER_API_KEY",
"apiKey": "$OPENROUTER_API_KEY",
"api": "openai-completions",
"models": [
{
@@ -455,7 +464,7 @@ Vercel AI Gateway example:
"providers": {
"vercel-ai-gateway": {
"baseUrl": "https://ai-gateway.vercel.sh/v1",
"apiKey": "AI_GATEWAY_API_KEY",
"apiKey": "$AI_GATEWAY_API_KEY",
"api": "openai-completions",
"models": [
{