Escalation
Choosing a cheaper model is only a saving if its answers hold up. When one does not, the call is escalated to a more capable model, and the cost of that second call is charged against the cheap model whose failure bought it.
When escalation happens
Section titled “When escalation happens”When an answer fails its check: the agent’s own check on its output (a schema, a citation, a grounding check, a rule), decided by the agent’s code. The router does not judge answers itself. The agent asks the gateway for the next step:
step = gateway.escalated(decision, env, failed_model=model, spent_usd=result.cost_usd)if step is not None and step.available: result = await gateway.acall(prompt, env=env, model=step.model, system=system)Escalating automatically on a failed check, for agents in the library: Coming
How it works
Section titled “How it works”- Up the capability ladder. The next model is the most capable one this call was allowed to use: not excluded, within your allow list and residency rule, available, and not dropped for a hard reason. Where your policy names an escalation target for the task class (
escalate_to), that model is used instead. - At most a set number of steps: one by default (
escalatein your policy). Set it to zero to turn escalation off. When the steps are used up, or nothing is above, the escalation says so (escalation_exhausted,nothing_above) and nothing more is called. - Through the gateway, like any call: the second call is checked against your caps and kill switch, priced, recorded and screened in the same way.
What it costs, and who is charged
Section titled “What it costs, and who is charged”The escalation’s cost is charged to the cheap model (charge_to). The decision is recorded again with escalation_usd, so:
- the routing lever takes the escalation off the saving that choice bought: a cheap model that needed a dear retry did not save what the first decision estimated;
- the next time the benchmark and the routing figures look at that model, its cost per successful task includes the clean-up its failures caused.
GET /api/v1/routing reports escalated (how many decisions were escalated) and escalation_usd (what the escalations cost) in its totals and in every breakdown. saving_usd is already net of it.
Escalation is priced in before the choice
Section titled “Escalation is priced in before the choice”The router anticipates escalation when it chooses. Each candidate is ranked on its expected cost:
expected = direct + (1 − quality) × the direct cost of the next model upA worked example of the arithmetic, on the card of A decision, worked: Haiku 4.5’s direct cost for the lookup question is $0.011451, and the next model up, Sonnet 4.5, would cost $0.034601. At quality 1.00, Haiku’s expected cost is its direct cost. If Haiku’s measured quality on the class were 0.90, its expected cost would be $0.011451 + 0.10 × $0.034601 = $0.014911: still well below Sonnet’s, so still chosen. A model has to fail often, or be close in price, for the escalation term to change the order.
Note the two “next models” differ. The expected-cost term uses the direct cost of the next model up by capability; an actual escalation goes to the most capable model the call may use, unless escalate_to names one. With one step allowed, the actual escalation can therefore cost more than the term assumed: on the card above, Haiku would escalate to Opus 4.6 ($0.057613), not Sonnet. Set escalate_to for a class to the next model up to make the two the same.