# Configuring providers and models in EONYX


EONYX talks to **any OpenAI-compatible API**.
A provider is the way to reach a backend.
A model binds an id to a provider.
Here is how to connect one, hide the secrets and set price, window and retries.

<!--more-->

## 🔧 Providers and models

### Provider

There is one type so far: `"openai"`.
It covers any OpenAI-compatible endpoint.
`base_url` is required.

```json
"providers": {
  "openai":     { "type": "openai", "base_url": "https://api.openai.com/v1",     "api_key": "$OPENAI_API_KEY" },
  "openrouter": { "type": "openai", "base_url": "https://openrouter.ai/api/v1",  "api_key": "$OPENROUTER_API_KEY" },
  "deepseek":   { "type": "openai", "base_url": "https://api.deepseek.com",       "api_key": "$DEEPSEEK_API_KEY" },
  "moonshot":   { "type": "openai", "base_url": "https://api.moonshot.cn/v1",     "api_key": "$MOONSHOT_API_KEY" }
}
```

{{< admonition type="warning" title="Secrets go through environment variables" >}}
`api_key` and `base_url` may reference variables such as `$OPENAI_API_KEY`.
They are expanded **after** the config layers merge.
So the key never lands in a committed file.
The same holds for `report.database_url` and MCP headers.
{{< /admonition >}}

### Model

A model links an id, as the API understands it, to a provider:

```json
"models": [
  { "id": "gpt-5",            "provider": "openai",     "context_window": 400000, "input_price": 1.25, "output_price": 10 },
  { "id": "deepseek-v4-pro",  "provider": "deepseek",   "context_window": 1000000, "input_price": 0.435, "output_price": 0.87 }
],
"default_model": "deepseek-v4-pro"
```

| Field            | Purpose                                                             |
| ---------------- | ------------------------------------------------------------------- |
| `id`             | the model name at the provider                                      |
| `provider`       | a key from the `providers` block                                    |
| `context_window` | window size in tokens — **enables history auto-summarization**      |
| `input_price`    | price per 1M input tokens (USD) — for the cost counter              |
| `output_price`   | price per 1M output tokens (USD)                                    |

Prices only feed the cost indicator in the TUI header.
They change no behaviour.
`context_window` matters more: without it there is no auto-summarization.

### Switching the model on the fly

- In the TUI, press `Ctrl+N` or type `/model`. The configured models are listed, the current one marked.
- Headless, pass `-m/--model`:

```bash
eonyx run -m deepseek-v4-pro "fix the failing test"
```

Keeping several models around pays off.
A cheap fast one for rough work.
A strong one for the hard problems.

## 🛡️ Reliability and verification

### Retries and timeouts

The `retry` block controls transient failures and the wait for the first response:

```json
"retry": {
  "max_retries": 3,
  "base_delay_ms": 500,
  "response_header_timeout_sec": 60
}
```

| Field                         | Default | Meaning                                                            |
| ----------------------------- | ------- | ------------------------------------------------------------------ |
| `max_retries`                 | 3       | extra attempts on 429/5xx and transport errors (0 disables them)    |
| `base_delay_ms`               | 500     | the first backoff step; it doubles, capped at 30s; honors `Retry-After` |
| `response_header_timeout_sec` | 60      | how long to wait for the first response header before streaming starts |

There is deliberately no global `Client.Timeout`.
A long token stream must never be cut off by a clock.

### Checking the configuration

```bash
eonyx config   # providers (keys hidden), models, default, limits
eonyx info     # where the config, data, cache and project root live
```

{{< admonition type="note" title="Strict schema" >}}
The config is parsed strictly.
An unknown field, a wrong type or broken JSON is **fatal** at startup.
For an unknown field you get its name and line number.
EONYX would rather not start than quietly swallow a typo.
{{< /admonition >}}

## 📋 Summary

{{< admonition type="abstract" title="Cheat sheet" >}}
- **Provider**
  - `type: "openai"` + `base_url`
  - covers OpenAI, OpenRouter, DeepSeek, Moonshot and local servers
- **Secrets**
  - via `$ENV` references in `api_key`/`base_url`
  - expanded after merging
- **Model**
  - `id` + `provider`
  - `context_window` enables summarization, prices drive the cost counter
- **Switching**
  - `Ctrl+N` / `/model` in the TUI or `-m` in headless mode
- **`retry`**
  - `max_retries` (3), `base_delay_ms` (500), `response_header_timeout_sec` (60)
{{< /admonition >}}

