How-to guides / Models and providers
Start here
How-to guides
How-to guides
Models and providers
An agent’s model is set in its bundle’s x-agen/harness.json. This page
covers choosing a provider and model, routing models and their cost tiers,
fallback models, reasoning effort, OpenRouter request settings, and what
happens when a provider account stops working.
Providers
provider |
Calls | API key (default secret) |
|---|---|---|
openrouter |
OpenRouter, any model it lists. Reports cost per call. | OPENROUTER_API_KEY |
openai |
An OpenAI-compatible API: OpenAI itself, or another endpoint with baseUrl. Reports no cost. |
OPENAI_API_KEY |
fake |
Nothing: replies from a script in the bundle. | none |
replay |
Nothing: replays a recorded cassette. | none |
{
"provider": "openrouter",
"model": "deepseek/deepseek-v4-flash",
"maxOutputTokens": 4000
}
apiKeySecret names another secret for the key; declare it in
x-agen/secrets.json. Every field is listed in the
bundle reference.
Each model call is a span in the run’s trace. It records the model that
actually served the call as gen_ai.response.model, which can differ from the
one requested (a routing model, a provider alias or a fallback). The console’s
Costs page groups calls by that model.
Routing models and tiers
A routing model picks the serving model for each request. Agen knows three, all on OpenRouter:
model |
Plugin id | Tiers |
|---|---|---|
typesafe/jev-router |
jev-router |
low, medium, high |
openrouter/auto |
auto-router |
low, medium, high, xhigh, max |
openrouter/auto-beta |
auto-beta-router |
low, medium, high, xhigh, max |
Configure the router in router; Agen sends it as the router’s plugin entry:
{
"provider": "openrouter",
"model": "typesafe/jev-router",
"router": {
"tier": "medium",
"maxTier": "high",
"budgetTier": "low",
"allowedModels": ["deepseek/*", "z-ai/*"],
"excludedModels": ["openai/*"]
},
"fallbackModels": ["deepseek/deepseek-v4-flash"]
}
| Field | Meaning |
|---|---|
tier |
The default cost tier. Without one, the router chooses. |
maxTier |
The highest tier a task may ask for; higher requests are lowered to it. |
budgetTier |
The tier used once the deployment has spent 80% of its daily budget (default low). See Budgets. |
allowedModels, excludedModels |
Model patterns the router may or may not pick, such as deepseek/*. |
A tier is a band, not a cap: the router picks among the models of that band, so a lower tier usually costs less but is not guaranteed to. Set budgets for hard limits.
agen deploy --validate (and the engine at start) refuses router on a model
that is not a routing model, a tier the router does not offer (for example
xhigh on typesafe/jev-router), a tier above maxTier, and the router’s
own plugin in openrouter.plugins (configure it in router instead).
Choosing a tier per task
A task can ask for a tier, within maxTier:
agen run coder "Rename the config loader and fix its tests" --tier high
SubmitTask takes modelTier, and an A2A message takes the metadata key
agen.model_tier. A tier on a task whose agent has no routing model is
ignored. The console’s run form has a tier field too.
What the router decided
Besides the served model, the model-call span records the tier sent
(agen.router.tier) and, when the router reports it, its decision:
agen.router.task (the kind of request it saw), agen.router.reason,
agen.router.effort and agen.router.summary, among others. The trace
inspector lists them under the call’s attributes.
Fallback models
fallbackModels (up to five) are tried in order when the model fails:
- A model call is retried first: rate limits (429) and server errors back off and try again, up to 5 times. If it still fails, or fails with an error that is not retried, the next model in the list is called.
- An empty answer (no text and no tool calls) is asked again once; if it is empty again, the next model is called.
- An answer cut off at the output limit with nothing usable (no text and no
calls, or a call whose arguments were cut off; often the model’s reasoning
used the whole limit) is asked again once with twice the output limit (at
most 32768 tokens). The model-call span records it as
agen.output.retried_with. - If every model gives no answer, the run fails with
no_answer. Afinish_reasonoflengththere usually means reasoning used the whole output limit: raisemaxOutputTokens. - Account errors are never failed over (below).
The engine logs each switch (model X failed (...); trying Y), and the span of
a call served by a fallback records the model sent (gen_ai.request.model)
and agen.fallback. Fallbacks use the same provider and key as the primary
model.
Reasoning effort
reasoningEffort (minimal, low, medium, high) is sent to models that
reason: as reasoning.effort on OpenRouter and reasoning_effort on
openai. A routing model may choose the effort itself; the span records it as
agen.router.effort when it reports one.
OpenRouter request settings
openrouter (provider openrouter only) is sent with every request as given:
{
"provider": "openrouter",
"model": "deepseek/deepseek-v4-flash",
"openrouter": {
"provider": { "sort": "throughput", "ignore": ["some-provider"], "zdr": true },
"plugins": [{ "id": "web" }]
}
}
provider: OpenRouter’s provider routing (order, ignore, sort, data policy and so on).plugins: OpenRouter plugins; each needs anid.
See OpenRouter’s documentation for the values. Agen does not check them.
Account errors and provider holds
A provider answer of 401, 402 or 403 (an invalid or expired key, no credit, a
key limit reached) is an account error. It is not retried and no fallback
model is tried, because another model on the same account will not help. The
task fails with provider account error: .... A 403 because moderation
flagged the input is about that request only: it fails over like other
request errors and does not hold the account.
The Hub then holds the provider account: the namespace, the provider and
the name of its key secret (plus baseUrl if set), such as
default/openrouter:OPENROUTER_API_KEY. Every deployment of that namespace
that uses that account:
- takes no new tasks; queued work waits instead of failing one task at a time,
- does not scale up for queued work and, until the next try, is not woken for A2A calls (callers get an error naming the hold and the next try),
- shows provider held in the console and in
agen ps(providerHoldon the deployment in the API).
After a backoff (5 minutes, doubling with each failed probe up to 1 hour), one probe task goes through. Any run of the account that succeeds after the hold began (a task or an A2A call) ends it. To end it at once, for example after adding credit or fixing the key:
agen providers # held accounts, the reason and the next try
agen providers resume default/openrouter:OPENROUTER_API_KEY # operator in that namespace
The API has ListProviderHolds and ResumeProvider; the console lists held
accounts on the Costs page with Resume. A new hold sends
provider.held, and a hold that a successful run ends sends
provider.resumed, to notification targets that take
every event.