Custom providers
Connect any OpenAI-compatible API to Empryo with one config block: set baseURL, key env var, models, dynamic model lists, and reasoning options.
Add any OpenAI-compatible API as a provider. No code, no plugin.
Minimal example
Add this to ~/.empryo/config.json (or .empryo/config.json for project scope):
{
"providers": [
{
"id": "together",
"name": "Together AI",
"baseURL": "https://api.together.xyz/v1",
"envVar": "TOGETHER_API_KEY",
"models": ["meta-llama/Llama-3-70b-chat-hf"]
}
]
}Set the key and use it:
empryo --set-key together <your-key>
empryo --model together/meta-llama/Llama-3-70b-chat-hfFields
| Field | Required | What it does |
|---|---|---|
id | yes | Used in model strings (together/llama-3) |
baseURL | yes | OpenAI-compatible endpoint |
name | - | Display name |
envVar | - | Env var for the key (omit for auth-less servers) |
models | - | Fallback model list (strings or {id, name, contextWindow}) |
modelsAPI | - | URL returning OpenAI /v1/models format |
reasoning | - | Thinking/reasoning config (see below) |
More examples
Thinking / reasoning
For models that support reasoning over an OpenAI-compatible endpoint, add a reasoning block. Three styles are supported - use the one your provider accepts:
{
"providers": [{
"id": "dashscope",
"baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"envVar": "DASHSCOPE_API_KEY",
"models": ["qwen-plus"],
"reasoning": {
"enabled": true,
"budget": 8192
}
}]
}| Style | Fields | When to use | ||||
|---|---|---|---|---|---|---|
| OpenAI-style | `effort: "low" \ | "medium" \ | "high" \ | "xhigh" \ | "none"` | OpenAI-compatible reasoning models |
| DashScope-style | enabled: true, budget: 8192 | Alibaba Qwen, DashScope APIs | ||||
| Raw | extraParams: { ... } | Anything else - fields are forwarded verbatim |
The fields are injected into every request body. Raw extraParams override all other keys on collision.
How effort is sent
By default effort goes out as the flat OpenAI field, { "reasoning_effort": "high" } — the field every OpenAI-compatible endpoint speaks. If your backend instead wants the nested OpenRouter shape, set effortStyle:
{
"reasoning": {
"effort": "high",
"effortStyle": "nested"
}
}effortStyle | Sends |
|---|---|
"flat" *(default)* | { "reasoning_effort": "high" } |
"nested" | { "reasoning": { "effort": "high" } } |
"both" | both keys at once |
Only reach for "both" if a router genuinely needs it. Strictly-validating backends reject a request carrying both keys outright — *"Pass either reasoning (nested object) or reasoning_effort (flat field), not both."*
Scope
- Global -
~/.empryo/config.jsonapplies everywhere. - Project -
.empryo/config.jsonoverrides global entries with the sameid.
Conflicts
If your id matches a built-in (e.g. "anthropic"), Empryo renames it to {id}-custom - the built-in is never replaced. Custom providers always show [custom] in /keys and --list-providers.