Add custom headers support for models.json

Fixes #39

- Added headers field to Model type (provider and model level)
- Model headers override provider headers when merged
- Supported in all APIs:
  - Anthropic: defaultHeaders
  - OpenAI (completions/responses): defaultHeaders
  - Google: httpOptions.headers
- Enables bypassing Cloudflare bot detection for proxied endpoints
- Updated documentation with examples

Also fixed:
- Mistral/Chutes syntax error (iif -> if)
- process.env.ANTHROPIC_API_KEY bug (use delete instead of = undefined)
This commit is contained in:
Mario Zechner 2025-11-20 17:05:31 +01:00
parent 425890e674
commit de39f1f493
9 changed files with 95 additions and 7 deletions

View file

@ -2,6 +2,15 @@
## [Unreleased]
### Added
- **Custom Headers**: Added support for custom HTTP headers in `models.json` configuration. Headers can be specified at both provider and model level, with model-level headers overriding provider-level ones. This enables bypassing Cloudflare bot detection and other proxy requirements. ([#39](https://github.com/badlogic/pi-mono/issues/39))
### Fixed
- **Chutes AI Provider**: Fixed 400 errors when using Chutes AI provider. Added compatibility fixes for `store` field exclusion, `max_tokens` parameter usage, and system prompt role handling. ([#42](https://github.com/badlogic/pi-mono/pull/42) by [@butelo](https://github.com/butelo))
- **Mistral/Chutes Syntax Error**: Fixed syntax error in merged PR that used `iif` instead of `if`.
## [0.7.25] - 2025-11-20
### Added

View file

@ -206,6 +206,44 @@ This allows both secure env var usage and literal keys for local servers.
This is useful when a provider supports multiple API standards through the same base URL.
### Custom Headers
You can add custom HTTP headers to bypass Cloudflare bot detection, add authentication tokens, or meet other proxy requirements:
```json
{
"providers": {
"custom-proxy": {
"baseUrl": "https://proxy.example.com/v1",
"apiKey": "YOUR_API_KEY",
"api": "anthropic-messages",
"headers": {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"X-Custom-Auth": "bearer-token-here"
},
"models": [
{
"id": "claude-sonnet-4",
"name": "Claude Sonnet 4 (Proxied)",
"reasoning": true,
"input": ["text", "image"],
"cost": {"input": 3, "output": 15, "cacheRead": 0.3, "cacheWrite": 3.75},
"contextWindow": 200000,
"maxTokens": 8192,
"headers": {
"X-Model-Specific-Header": "value"
}
}
]
}
}
}
```
- **Provider-level `headers`**: Applied to all requests for models in that provider
- **Model-level `headers`**: Additional headers for specific models (merged with provider headers)
- Model headers override provider headers when keys conflict
### Model Selection Priority
When starting `pi`, models are selected in this order:

View file

@ -31,6 +31,7 @@ const ModelDefinitionSchema = Type.Object({
}),
contextWindow: Type.Number(),
maxTokens: Type.Number(),
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
});
const ProviderConfigSchema = Type.Object({
@ -44,6 +45,7 @@ const ProviderConfigSchema = Type.Object({
Type.Literal("google-generative-ai"),
]),
),
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
models: Type.Array(ModelDefinitionSchema),
});
@ -174,6 +176,10 @@ function parseModels(config: ModelsConfig): Model<Api>[] {
continue;
}
// Merge headers: provider headers are base, model headers override
const headers =
providerConfig.headers || modelDef.headers ? { ...providerConfig.headers, ...modelDef.headers } : undefined;
models.push({
id: modelDef.id,
name: modelDef.name,
@ -185,6 +191,7 @@ function parseModels(config: ModelsConfig): Model<Api>[] {
cost: modelDef.cost,
contextWindow: modelDef.contextWindow,
maxTokens: modelDef.maxTokens,
headers,
});
}
}