# 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 cloud providers, hide secrets and configure price, context window and retries.

<!--more-->

## Providers and models

Both blocks are defined in `eonyx.json`: the provider describes API access, the model binds a specific id to a provider, and you can switch models right inside a session.

### Provider

There is only 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` can reference environment variables (`$OPENAI_API_KEY`) — they are expanded **after** the config layers are merged. That way the key never ends up in a file committed to the repository. The same applies to `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 are only needed for the cost indicator in the TUI header (`$…`); they do not affect behavior. `context_window` matters more: without it auto-summarization does not work.

### Switching the model on the fly

- In the TUI — `Ctrl+N` or `/model`: a list of configured models, the current one is marked.
- In headless mode — the `-m/--model` flag:

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

It is convenient to keep several models in the config: a cheap fast one for rough work and a powerful one for hard tasks.

## Reliability and verification

How EONYX retries transient failures and how to make sure the config was read without errors.

### Retries and timeouts

The `retry` block configures how the provider retries transient failures and how long it waits 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` — long token streams must not be cut off by a timeout.

### 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 a **fatal** error at startup (for an unknown field — with its name and line number). EONYX would rather not start than silently ignore 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 >}}

