Add OpenAICompat for openai-completions provider quirks

Fixes #133
This commit is contained in:
Mario Zechner 2025-12-08 19:00:57 +01:00
parent e34e0c503f
commit 87a1a9ded4
7 changed files with 165 additions and 24 deletions

View file

@ -2,6 +2,10 @@
## [Unreleased]
### Added
- **OpenAI compatibility overrides in models.json**: Custom models using `openai-completions` API can now specify a `compat` object to override provider quirks (`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `maxTokensField`). Useful for LiteLLM, custom proxies, and other non-standard endpoints. ([#133](https://github.com/badlogic/pi-mono/issues/133), thanks @fink-andreas for the initial idea and PR)
## [0.13.2] - 2025-12-07
### Changed

View file

@ -315,6 +315,47 @@ You can add custom HTTP headers to bypass Cloudflare bot detection, add authenti
- **Model-level `headers`**: Additional headers for specific models (merged with provider headers)
- Model headers override provider headers when keys conflict
### OpenAI Compatibility Settings
The `openai-completions` API is implemented by many providers with minor differences (Ollama, vLLM, LiteLLM, llama.cpp, etc.). By default, compatibility settings are auto-detected from the `baseUrl`. For custom proxies or unknown endpoints, you can override these via the `compat` field on models:
```json
{
"providers": {
"litellm": {
"baseUrl": "http://localhost:4000/v1",
"apiKey": "LITELLM_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "gpt-4o",
"name": "GPT-4o (via LiteLLM)",
"reasoning": false,
"input": ["text", "image"],
"cost": {"input": 2.5, "output": 10, "cacheRead": 0, "cacheWrite": 0},
"contextWindow": 128000,
"maxTokens": 16384,
"compat": {
"supportsStore": false
}
}
]
}
}
}
```
Available `compat` fields (all optional, auto-detected if not set):
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `supportsStore` | boolean | auto | Whether provider supports the `store` field |
| `supportsDeveloperRole` | boolean | auto | Whether provider supports `developer` role (vs `system`) |
| `supportsReasoningEffort` | boolean | auto | Whether provider supports `reasoning_effort` parameter |
| `maxTokensField` | string | auto | Use `"max_completion_tokens"` or `"max_tokens"` |
If `compat` is partially set, unspecified fields use auto-detected values.
### Authorization Header
Some providers require an explicit `Authorization: Bearer <token>` header. Set `authHeader: true` to automatically add this header using the resolved `apiKey`:

View file

@ -9,6 +9,14 @@ import { loadOAuthCredentials } from "./oauth/storage.js";
// Handle both default and named exports
const Ajv = (AjvModule as any).default || AjvModule;
// Schema for OpenAI compatibility settings
const OpenAICompatSchema = Type.Object({
supportsStore: Type.Optional(Type.Boolean()),
supportsDeveloperRole: Type.Optional(Type.Boolean()),
supportsReasoningEffort: Type.Optional(Type.Boolean()),
maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
});
// Schema for custom model definition
const ModelDefinitionSchema = Type.Object({
id: Type.String({ minLength: 1 }),
@ -32,6 +40,7 @@ const ModelDefinitionSchema = Type.Object({
contextWindow: Type.Number(),
maxTokens: Type.Number(),
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
compat: Type.Optional(OpenAICompatSchema),
});
const ProviderConfigSchema = Type.Object({
@ -201,7 +210,8 @@ function parseModels(config: ModelsConfig): Model<Api>[] {
contextWindow: modelDef.contextWindow,
maxTokens: modelDef.maxTokens,
headers,
});
compat: modelDef.compat,
} as Model<Api>);
}
}