Models and upstreams
An upstream is one model endpoint: OpenAI, an OpenAI-compatible server such as Ollama, vLLM or a gateway like AIGate, or Anthropic. chatz talks to them through the elelem drivers, asks each one for its model list at startup, and merges the lists into the model picker.
The CHATZ_UPSTREAMS format
CHATZ_UPSTREAMS is a JSON array with one object per upstream:
[
{
"name": "openai",
"provider": "openai",
"baseUrl": "https://api.openai.com/v1",
"apiKeyEnv": "OPENAI_API_KEY"
},
{
"name": "anthropic",
"provider": "anthropic",
"apiKeyEnv": "ANTHROPIC_API_KEY"
},
{
"name": "aigate",
"provider": "openai",
"baseUrl": "http://aigate:4000",
"apiKeyEnv": "AIGATE_TOKEN"
},
{
"name": "ollama",
"provider": "openai",
"baseUrl": "http://localhost:11434/v1"
}
]
In .env the whole array goes on one line after CHATZ_UPSTREAMS=.
| Field | Required | Meaning |
|---|---|---|
name | yes | A label for the upstream, unique in the array. It shows up in the model list and in health reports. |
provider | yes | openai for OpenAI and every OpenAI-compatible API, or anthropic. |
baseUrl | no | The API root. Leave it out to use the provider’s own default endpoint. |
apiKeyEnv | no | The name of the environment variable that holds the API key. Leave it out for keyless endpoints such as a local Ollama. |
models | no | Extra metadata for models this upstream lists. See below. |
Keys are only ever read from the variable that apiKeyEnv names, and each upstream uses only its own key and base URL. chatz does not fall back to the provider SDK’s usual variables. If apiKeyEnv names a variable that is unset or empty, startup stops.
The AIGate example assumes chatz and AIGate share a Docker network. Otherwise use an address chatz can reach.
Discovery
At startup chatz calls each upstream’s model list, bounded by CHATZ_UPSTREAM_CONNECT_TIMEOUT and tried twice. The lists are merged in the order the upstreams appear:
- An upstream that fails discovery is skipped and marked degraded. The others still load, and startup continues.
- When two upstreams list the same model ID, the first one in the array serves it and the later one is skipped with a warning.
- Discovery runs only at startup. A model added on the provider side appears after a restart.
The list is served at GET /api/v1/models. A configuration error in the JSON, such as a missing name or an unknown provider, stops startup; Troubleshooting lists each message.
Model metadata
The optional models array attaches display names, limits, capabilities and prices to models the provider lists. An entry never adds a model the provider did not return; an entry for a model that is not discovered is ignored.
{
"name": "openai",
"provider": "openai",
"baseUrl": "https://api.openai.com/v1",
"apiKeyEnv": "OPENAI_API_KEY",
"models": [
{
"id": "analysis-model",
"alias": "Deep",
"contextWindow": 128000,
"maxOutputTokens": 8192,
"supportsTools": true,
"supportsReasoning": true,
"supportsFiles": true,
"expectedFirstTokenLatencyMs": 900,
"inputPricePerMillionTokens": {
"amountSmallestUnit": 15,
"currency": "USD"
},
"outputPricePerMillionTokens": {
"amountSmallestUnit": 60,
"currency": "USD"
},
"fallbackModels": ["backup-model"]
}
]
}
| Field | Meaning |
|---|---|
id | Required. The model ID exactly as the provider lists it. Unique within the upstream. |
alias | A friendly name for the model picker. |
contextWindow, maxOutputTokens | Token limits shown to users. Must not be negative. |
supportsTools, supportsReasoning, supportsVision, supportsFiles | Capability flags. A flag you leave out means unknown, not false. |
expectedFirstTokenLatencyMs | A latency hint shown with the model. Must not be negative. |
inputPricePerMillionTokens, outputPricePerMillionTokens | amountSmallestUnit is an integer in the currency’s smallest unit (cents for USD), never a float. currency is a three-letter uppercase code. |
fallbackModels | Model IDs to try, in order, when this model fails before streaming starts. |
Prices are display data. chatz records token counts per call but does not compute cost.
Fallback models
When a request fails before any output has streamed, and the failure is a first-token timeout, a turn timeout, a rate limit or an unavailable model, chatz tries the next ID in fallbackModels. The stream reports a retrying status while it switches. A failure after output has started is not retried on another model.
Fallback IDs must be discovered models. An empty entry, a model that lists itself, or a duplicate entry stops startup, and so does a fallback that no upstream lists. Fallbacks of a model that is not discovered are ignored.
Default model
CHATZ_DEFAULT_MODEL marks one discovered model as the default for new chats. The web app selects it, or the first model in the list when no default is set. If the default is not discovered, for example because its upstream was down at startup, chatz refuses to start.
Timeouts and concurrency
| Setting | Applies to |
|---|---|
CHATZ_UPSTREAM_CONNECT_TIMEOUT (10s) | Model discovery and the connection setup of each request. |
CHATZ_UPSTREAM_FIRST_TOKEN_TIMEOUT (45s) | The wait for the first streamed token. |
CHATZ_UPSTREAM_TURN_TIMEOUT (5m) | The whole model request. |
CHATZ_UPSTREAM_CONCURRENCY (8) | Requests in flight per upstream. |
Each upstream has its own queue of concurrency slots. The first-token and turn timers start once a request holds a slot. A timed-out request ends the stream with an error the user can retry; see Chats.
Upstream health
Admins can see each upstream’s health at GET /api/v1/upstreams and on the readiness page in the web app. Every entry carries:
| Field | Meaning |
|---|---|
upstream | The upstream name. |
state | unknown before any call, healthy after a success, degraded after a failure. |
lastOperation | model_discovery, queue, stream or complete. |
lastLatencyMs | Latency of the last call. |
lastSuccessAt, lastFailureAt | Timestamps of the last success and failure. |
lastFailureClass | cancelled, deadline_exceeded, first_token_timeout, turn_timeout, discovery_timeout or upstream_error. |
consecutiveFailures | Failures since the last success. |
The snapshot holds no keys, URLs or request contents.