# Auto Router

Send `liquid.auto` as the model and Auto chooses for you. It ranks the models that pass your rules. It
compares a published score for each model with the estimated charge, within your cap. The auction then
runs as usual, and the receipt records why Auto chose that model.

Artificial Analysis publishes the score of each model. It does not measure the deployment that answers
your request, so a higher score does not guarantee a better answer.

## Send a request

Add a preference to the name: `liquid.auto:premium`, `liquid.auto:balanced` or `liquid.auto:economy`.
Set a cap, because Auto pays more for a higher score and needs a price bound.

```bash
curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer $LIQUID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "liquid.auto:balanced",
    "messages": [{"role": "user", "content": "Write a SQL query that finds duplicate emails."}],
    "exchange": {"cap_usd": 0.05}
  }'
```

The response says which model answered. The `x-liquid-auto-task` header says which score ranked the
request. A bare `liquid.auto` ranks as the exchange sets it, by price or by Auto. See
[A router model that ranks by Auto](#a-router-model-that-ranks-by-auto).

### Price bounds

One bound is enough:

- `exchange.rank.cost_tier` other than `any`: only models whose published rates are within the tier compete.
- `exchange.limits.price.output_usd_per_mtok`: only offers at or below this output rate compete.
- `cap_usd`, or the `x-liquid-cap-usd` header: offers with a higher worst case for this request do not
  compete. A cap also refuses an unusually large request.

A bound in the preset that the request names, or in the preset linked to your API key, also counts. If no
bound applies, `liquid.auto` uses its own `cap_usd`, shown in `GET /v1/exchange/routers`. The receipt
shows the cap that applied. A basket that asks for Auto has no cap of its own, so the exchange refuses
its request with `auto_requires_price_bound`.

## Choose a preference

The preference sets how many score points each doubling of the estimated charge must buy. A model that
costs twice as much wins only if its score is at least that many points higher.

| Preference | Points for each doubling |
| ---------- | ------------------------ |
| `premium`  | 0.5                      |
| `balanced` | 2                        |
| `economy`  | 5                        |

To rank by score alone, send `liquid.auto:smartest`. It ignores the charge, so it needs a price bound.
To rank by speed, send `liquid.auto:fastest`. See [Axis routing](https://inference.ai.exchange/docs/axis-routing.md).

Without a suffix, set the preference in a strategy:

```json
{ "exchange": { "rank": { "strategy": { "kind": "auto", "preference": "economy" } } } }
```

A client that can send only headers, such as Claude Code, can use the `x-liquid-policy` header, or a
preset whose strategy is Auto. See "Presets" in [Purchasing rules](https://inference.ai.exchange/docs/rules.md). If Auto is off, the
exchange refuses to save such a preset with `400 auto_unavailable`. If a router model ranks by Auto and
you send no preference, it uses `balanced`.

## Choose the models

`liquid.auto` ranks every listed model, and the models of your own cloud accounts for your workspace only.
To rank fewer, send `exchange.candidates.models.allow`: a model name, a deployment symbol or a pattern with `*`. See "The
basket" in [Purchasing rules](https://inference.ai.exchange/docs/rules.md).

```json
{
  "model": "liquid.auto:balanced",
  "messages": [{ "role": "user", "content": "Summarise this contract clause." }],
  "exchange": {
    "candidates": { "models": { "allow": ["gpt-oss-*"] } },
    "cap_usd": 0.05
  }
}
```

A preference suffix and `"rank": {"strategy": {"kind": "price"}}` in one request conflict, and the exchange
refuses the request with `axis_conflict`.

To rank your own basket, send `architect.basket` with `exchange.candidates.models.allow` and
`"rank": {"strategy": {"kind": "auto"}}`.

## What Auto reads from your prompt

Auto ranks by one of two Artificial Analysis scores: the intelligence index for a general request, or
the coding index for a coding request. To choose, Auto can ask Jev, a hosted classifier. Jev reads the
system prompt and your messages. It does not read assistant turns, tool calls or tool results.

Auto calls Jev only if your data rules allow it:

- `exchange.privacy.retention` is not `none`. If you do not set it, Auto sends the request to Jev.
- No region restriction on the request, or on the preset linked to your key, excludes the processing
  region of the classifier. If the exchange has not named that region, any region restriction stops Auto
  from calling Jev.

If the classifier or its vendor must not see your requests, set `exchange.privacy.retention` to `none`. Auto then
ranks by the general score, and `none` also limits the offers to those that can obey it.

If a conversation is too long for Jev, Auto cuts the middle of each long message and keeps its start and
end. If Auto does not call Jev, or cannot use the answer, it ranks by the general score. The receipt names
the reason. See [Classifier fallbacks](#classifier-fallbacks).

## Sessions and the cache

Send the same `prompt_cache_key` on every turn of a conversation. Without a key, the exchange uses a
digest of the first block of the prompt. The exchange records which deployment served the previous turn.
If the cache that turn wrote has more value than another model, Auto keeps the next turn on that
deployment, and the receipt records `kept_for_cache`. Under `exchange.cache.mode: "off"` the exchange drops the key and keeps no session. See
[Prompt caching](https://inference.ai.exchange/docs/caching.md).

## Response

Two headers arrive before the first token:

- `x-liquid-auto-task`: the score that ranked the request, `general` or `coding`.
- `x-liquid-auto-fallback`: present after a fallback. It holds a classifier fallback reason, such as
  `timeout`, or `no_eligible_coding_score`.

The receipt records the decision in `exchange.addressing.auto`. See [Receipt fields](#receipt-fields).

## Errors

- `503 auto_unavailable`: Auto is off, or the exchange has no score snapshot yet.
- `503 auto_scores_stale`: the exchange's copy of the scores is older than its limit.
- `503 no_eligible_offer`: no offer passed. `eligibility.excluded` lists why, and `error.auto` holds the
  decision.
- `400 auto_version_unknown`: the version is not 1 to 8.
- `400 auto_requires_price_bound`: the request, its preset and the key set no cap, no cost tier other than
  `any` and no output-rate ceiling.
- `400 auto_preference_unsupported`: the version does not accept the preference.
- `400 auto_policy_unsupported`: the version does not accept the preference sent as `exchange.policy` or
  `x-liquid-policy`.
- `400 strategy_conflicts_with_address`: a model, or a router model with no cap, was sent with an `auto`
  strategy.
- `400 auto_pin_unsupported`: the request sent `x-liquid-pin-offer`.
- `400 auto_reasoning_unsupported`: Auto has no scores for the reasoning effort. Send another effort, a
  token budget, or none.

Two exclusion reasons are specific to Auto: `score_unmapped` (no score for the offer's deployment at the
request's reasoning setting) and `score_missing` (no score for the task). See [Errors](https://inference.ai.exchange/docs/errors.md).

## Reference

### A router model that ranks by Auto

A router model, such as `liquid.auto`, ranks its offers by price or by Auto when a request names no
ranking. `GET /v1/exchange/routers` shows this as `ranking`, and shows the `cap_usd` of a router model
that offers Auto.

When it ranks by Auto, a request with no strategy gets Auto at version 8 and `balanced`. Your choices
always win:

- `:premium`, `:balanced` and `:economy` rank by Auto at that preference, even where the router model
  ranks by price.
- A strategy, `exchange.policy` or the `x-liquid-policy` header sets the ranking that you name.
- `:smartest` ranks by score alone. `:cheapest`, `:fastest` and `{"kind": "price"}` take the call out of
  Auto.

#### The work of the request

If the router model chose the preference, the work of the request can move it one step. Two reads decide
the effort:

- **Jev.** It rates how much reasoning the latest turn needs: light, standard or heavy. The exchange
  asks in the same call as the task. It needs a retention policy that is not `none`. An answer with a
  probability of 0.6 or more decides. A light answer does not lower a request that has tool results.
- **The envelope.** Without a confident answer, the exchange reads token counts, tools, tool results and
  the reasoning setting, and not the messages. Heavy work in the envelope stays heavy whatever Jev says.

- `heavy`: a prompt of 30,000 tokens or more, 4 or more tool results, a reasoning effort of `high`,
  `xhigh` or `max`, or a budget of 8,000 or more. The preference moves one step toward `premium`.
- `light`: a prompt of 800 tokens or less, no tools, no reasoning setting and 3 messages or fewer. It
  moves one step toward `economy`.
- `standard`: anything else. It stays.

The preference never moves past `premium` or `economy`. It does not move if you chose it or sent a
suffix. The receipt records `effort`, `effort_source` (`classifier` or `envelope`) and, if the preference
moved, `preference_before_effort`.

#### When Auto cannot rank the call

A bare call to a router model that ranks by Auto ranks by price, and does not fail, when Auto is off, the
exchange has no current scores, the request sends `x-liquid-pin-offer`, or the reasoning setting has no
scores. The receipt shows the reason in `exchange.addressing.auto_fallback`, such as `auto_unavailable`
or `auto_scores_stale`. If you ask for Auto with a suffix or a strategy, the exchange refuses the request
with the code in [Errors](#errors).

#### Jobs that are not priced per token

A router model that ranks by Auto buys an image, speech or video job at the lowest estimated charge.
Scores are measured on text, so Auto does not rank these jobs, and the cost tier and first-token limit of
the router model do not apply. A router model that ranks by price is refused on these endpoints. An
embedding job is refused for both. See [Modalities](https://inference.ai.exchange/docs/modalities.md).

### Classifier fallbacks

The receipt names the reason when Auto ranks by the general score because Jev did not decide: `disabled`,
`not_permitted`, `input_too_large`, `unsupported_content`, `overloaded`, `timeout`, `unavailable`,
`invalid_response`, `model_mismatch` or `low_confidence`. A Jev answer of `coding` below the confidence
threshold is `low_confidence`.

In version 2, a coding request ranks by the general score if offers pass your rules and none has a
coding score. The receipt records `routing_fallback: "no_eligible_coding_score"`. Version 1 finds no
offer and excludes each offer that has no score with `score_missing`.

### Versions

A released version never changes, and a pinned version keeps its rules. Choose one in
`exchange.rank.strategy.version`, such as `{"kind": "auto", "version": "2"}`. If you choose none, Auto uses
version 8. The exchange refuses a version other than 1 to 8 with `auto_version_unknown`.

- **1**: score alone. Accepts only `premium`. `balanced` or `economy` returns `auto_preference_unsupported`.
- **2**: preferences of 0.5, 2 and 5 points. Default `balanced`.
- **3**: available only when the exchange enables it. It has the preferences of version 2 and scores each
  exact model. An exact model with no score cannot receive an Auto award. A deployment with no score of
  its own uses the score of its exact model. `exchange.addressing.auto.score_origin` records the source.
- **4**: score alone, and the general score when a coding request has no coding score. `:smartest` and
  `{"kind":"smartest"}` select it. Accepts only `premium`.
- **5**: the preferences of version 2, and a [session pin](#session-pin).
- **6**: version 5, and a few more points for a faster group. See [Speed](#speed).
- **7**: version 6, with three changed rules. See [Price guard](#price-guard) and
  [Effort in version 7](#effort-in-version-7).
- **8**: the rules of version 7, ranked by more task classes. See [Task classes](#task-classes). The
  exchange lists it only when an operator turns it on.

Versions 2 and later default to `balanced`. Versions 1 and 4 default to `premium`.

### Price guard

In versions before 7, a free offer made every other model look very dear. A charge below $0.000000001
counted as that amount, so a model at $0.008 was 23 doublings above a free model and lost 11 points at
`premium`. Version 7 counts a charge below $0.00001 as $0.00001 and stops the price term at 30 points.
A strong model that costs almost nothing now beats a weak free one. These
numbers are a first setting that no pilot has measured.

### Effort in version 7

Versions 2 to 6 move the preference one step for heavy or light work. Version 7 keeps the preference and
changes how much price counts. For heavy work it also sets a floor on the score.

| Effort     | `economy`             | `balanced`            | `premium`               |
| ---------- | --------------------- | --------------------- | ----------------------- |
| `light`    | 5 points per doubling | 4 points per doubling | 1 point per doubling    |
| `standard` | 5 points per doubling | 2 points per doubling | 0.5 points per doubling |
| `heavy`    | 2, and a floor of 8   | 0.5, and a floor of 4 | 0, and a floor of 0     |

A floor removes every model that scores more than that many points below the best model that can win.
The best model always stays. The numbers are a first setting that no pilot has measured. They apply only
when the router model chose the preference.

The envelope can raise the effort to `heavy`, and it cannot lower it to `light`, because the size of a
prompt cannot show that a question is easy. Only a Jev answer of 0.6 or more gives `light`. If Jev is off
or unsure, the effort is `standard`. The receipt shows `envelope_floor` as the source when this stopped a
`light` reading.

### Task classes

Version 8 ranks by the score that fits the request. If a class has a score for fewer than half of the
models, or no offer that passed your rules has it, Auto ranks by the general score and the receipt says
so.

- `general`: Jev, or any fallback. Artificial Analysis intelligence index.
- `coding`, `math`: Jev. Artificial Analysis coding and math indexes.
- `reasoning`: Jev. The mean of GPQA and HLE.
- `knowledge`, `instruction`: Jev. MMLU-Pro and IFBench.
- `agentic`: a tool definition and at least one tool result. The mean of tau2-bench and Terminal-Bench Hard.
- `long_context`: a prompt of 30,000 tokens or more. Artificial Analysis long-context reasoning.

A coding request keeps `coding`. A tool loop is `agentic`, and a long prompt with no tool loop is
`long_context`. A model needs every benchmark of its class to rank. A Jev label needs a probability of at
least 0.5, because it has no held-out measurement yet. Writing, translation and vision have no class,
because no published score covers them.

### Speed

Version 6 reads the median output speed that the exchange measured for each provider and model over the
last 7 days, when at least 5 timed jobs back it. A provider's own claim earns nothing. A group earns 0.5
points for each doubling of its speed over the slowest measured group, and never more than 1 point. A
group with no measured speed earns nothing and loses nothing. So speed breaks a near tie and cannot
outbid a gap of more than 1 point. These numbers are a first setting that no pilot has measured.

### Session pin

A long conversation is cheap only while the same deployment holds its cache. Versions 5 and later keep a
session on the deployment that served the previous turn. The session needs a stable key: a
`prompt_cache_key`, an `x-session-id` header or the first block of the prompt. The deployment stays first
while all of these are true:

- It passes your rules, cap and budget, and its offer can win an award.
- It sells cache reads at a rate below its input rate.
- Jev reads the same task class as on the previous turn.
- Its estimated charge is at most 10% above the same charge at the rates of the previous turn.
- No other deployment has a value more than the margin above it. The margin is 2 score points, added to
  the value of the pinned deployment.

When one condition is false, the session is released and the normal order decides. The receipt's `pin`
field says what happened.

Versions 5 and later also price the cache from your workspace. When a deployment has too little history,
Auto uses the share of repeated input that providers served from cache for your workspace in the last 7
days. It assumes 50% only when the workspace has no history either.

Two rules in the `exchange` block control the pin:

- `exchange.session.pin`: `on` (the default of versions 5 to 8) or `off`. `off` ranks every turn again.
- `exchange.session.margin`: score points, 0 to 50. A rival must have a value this far above the pinned
  deployment before the session moves.

Versions before 5 ignore both rules.

### Discovery

If Auto is available, two endpoints list it with the same descriptor:

- `GET /v1/models` has a card for each router model that offers Auto, with `match_type: "router"` and
  an `auto` object.
- `GET /v1/exchange/routers` has an entry with an `auto` object, its `ranking` and its `cap_usd`.

`default_policy` and `policies` are the version's default preference and accepted preferences. `tasks`
lists the task classes. `effort` is `preference_step` or `weight_and_floor`. `models` lists the models that
unpinned Auto can choose now. If Auto is off, neither endpoint lists it.

```json
{
  "name": "liquid.auto",
  "default_version": "8",
  "versions": [
    { "version": "1", "default_policy": "premium", "policies": ["premium"] },
    {
      "version": "7",
      "default_policy": "balanced",
      "policies": ["premium", "balanced", "economy"],
      "tasks": ["general", "coding"],
      "effort": "weight_and_floor"
    }
  ],
  "price_bounds": ["cap_usd", "cost_tier", "max_output_usd_per_mtok"],
  "models": ["gpt-oss-120b"]
}
```

### Receipt fields

The `exchange.addressing.auto` object on the receipt records the decision:

- `mode_version`, `policy`, `points_per_doubling`: the version, the preference and the points per
  doubling that ranked the request.
- `effort`, `effort_source`, `preference_before_effort`: see
  [A router model that ranks by Auto](#a-router-model-that-ranks-by-auto).
- `task`, `score_field`: the score that ranked the request (`general` or `coding`) and its Artificial
  Analysis field.
- `classification`: `task`, and `outcome`: `{"kind": "classified"}` or
  `{"kind": "fallback", "reason": …}`. `evidence` holds Jev's answer, when it gave one.
- `routing_fallback`: `no_eligible_coding_score`, when a coding request ranked by the general score.
- `snapshot_id`, `scores_validated_at`: the score snapshot, and when the exchange last validated its copy.

- `score`, `estimated_charge_usd`: the awarded model's score and the estimated charge that ranked it. The
  actual charge is `charge.total_usd`.
- `higher_score`: a higher score that was passed over, its estimated charge, and `reason`: `value`,
  `competition` or `unavailable`.
- `kept_for_cache`: `expected_cached_tokens`, `hit_rate` and `cold_charge_usd`, when the cache kept the
  award on the previous deployment. The estimated saving is `cold_charge_usd` minus
  `estimated_charge_usd`.
- `pin`: present when the session pin applied. `outcome` is `held` or `released`; `deployment_id` and
  `margin` say which. `reason` says why the session left: `incumbent_unavailable`, `no_cache_discount`,
  `task_changed`, `price_rose`, `rival_cheaper` or `rival_above_margin`.

A receipt written before these fields existed lacks the fields after `task`. If a request finds no offer,
`error.auto` holds the same decision, without `score`, `estimated_charge_usd`, `higher_score` or
`kept_for_cache`.
