router overhead — rolling p95: 11ms. Illustrative model, not measured results.

Score it. Route it. Catch it.

Three mechanisms decide where a request goes: a fingerprint computed before it leaves your process, a budget enforced while the decision is being made, and a ladder that catches whatever the first pick got wrong. This page is the detail you would need to judge whether that can actually work.

Nothing here is adaptive in the hand-wavy sense. Every threshold on this page is a number you set, versioned with the ladder it belongs to, and printed back to you on every decision.

Step 01 — Difficulty fingerprint

TEN MILLISECONDS. FIVE DIMENSIONS. ONE SCORE.

Before a request leaves your application, a small self-hosted classifier scores it on five axes — reasoning depth, context span, format strictness, factual sensitivity, refusal risk — each on a 0–100 scale. Those five collapse into one difficulty score, D.

D is the router’s only input. Not the user, not their plan, not what they asked yesterday. One request, judged on its own text, so the same question always gets the same treatment and the decision stays explainable after the fact.

difficulty D

D = 0.34R + 0.22C + 0.14F + 0.20K + 0.10X

Weights sum to 1.00. They are fixed per deployment and versioned alongside the ladder.

The five axes

The five axes
AxisWhat it measuresWeight
Rreasoning depth0.34
Ccontext span0.22
Fformat strictness0.14
Kfactual sensitivity0.20
Xrefusal risk0.10

Four difficulty bands

D is cut into four bands. The bands, not the raw score, are what the ladder is written against — a threshold you can read in a config file beats a number nobody can picture.

Four difficulty bands
BandRangeShare of traffic
D1 · routineD < 2542%
D2 · standard25–5534%
D3 · complex55–8019%
D4 · high-stakesD ≥ 805%
Three requests, scoredFIG-03ILLUSTRATIVE
weights, fixed and summing to onescored inp95 9 msRreasoning depthw 0.34Ccontext spanw 0.22Fformat strictnessw 0.14Kfactual sensitivityw 0.20Xrefusal riskw 0.10Translate this paragraph into EnglishR12C8F20K15X5difficulty D12BandD1routinerouted toS-1Extract the tables in this PDF as JSONwith schema validationR34C62F96K55X12difficulty D51BandD2standardrouted toM-2Given these three contracts, can we terminate unilaterallyR78C86F45K92X61difficulty D76BandD3complexrouted toL-3
Illustrative model · FIG-03 · five axes per request, weights as printed; D is the weighted sum

The classifier itself

It is a 60M-parameter encoder you run yourself. It never calls out, so the fingerprint costs one local inference and the text of the request does not leave your side to be scored.

The classifier itself
ParameterValue
Size60M encoder, self-hosted
Fine-tuned on1,200,000 labelled requests
Latency (p50 / p95 / p99)5 / 9 / 14 ms
Cost per call$0.00003
Agreement with human labels (κ)0.71
Band accuracy, traffic-weighted84.1%
Where it is wrongrow-normalised, % of traffic in each actual bandFIG-03BILLUSTRATIVE
Predicted bandActual bandD1D2D3D4D191.27.90.80.1D29.479.810.10.7D31.114.677.27.1D40.22.418.379.1rows normalised to 100Band accuracy, traffic-weighted84.1%Under-routed — the ladder catches these7.2%Over-routed — this is the money wasted8.7%Over-routed — this is the money wasted$0.439USD / 1k11% of the routed bill
Illustrative model · FIG-03B · row-normalised, % of traffic in each actual band

It gets the band wrong about one time in six. That is not a caveat in the footnotes, it is the reason the ladder exists.

Under-routing
Scored a rung too low. The ladder catches these — they are the main source of escalation.
Over-routing
Scored a rung too high. Nothing catches these: it is money we spent for nothing, roughly 11% of the bill.

Tighten the thresholds and over-routing shrinks while escalation — and the latency tail with it — grows. There is no setting where both go down. The knob is yours, per endpoint.

Step 02 — Route budget

A BUDGET IS A CONSTRAINT, NOT A REPORT.

Each endpoint declares a ceiling in dollars per thousand calls. It is applied while the route is being chosen, not reconciled at the end of the month — when headroom runs low the whole ladder shifts down a rung for the rest of the day rather than overspending and emailing you about it.

That is the whole difference between a budget and a dashboard. A dashboard tells you what happened. This changes what happens.

Budget and actual, per endpoint

Budget and actual, per endpoint
EndpointQuality floorBudgetActualHeadroom
/chatjudge p10 ≥ 74$4.20$2.8133%
/docqajudge p10 ≥ 78$5.50$4.2223%
/extractschema pass ≥ 99.0%$4.80$3.6823%
/reviewjudge p10 ≥ 84$9.60$8.6510%

Budgets are hard caps in this illustrative deployment; every endpoint is running under its own.

The decision treeshare of all traffic, percentage pointsFIG-04ILLUSTRATIVE
first checksemantic cache hit?cos ≥ 0.94 && same tenant && same prompt_versionD142 pproutineD234 ppstandardD319 ppcomplexD45 ppC-08S-130M-24S-18M-222L-34M-25L-313L-3R1L-32L-3R30102030405060708090100share of all traffic, percentage pointsΣ 100 ppPathRungppD1cache hitC-08D1D1 → budget headroomS-130D1D1 → factual sensitivity K ≥ 70M-24D2D2 → format strictness F < 80S-18D2D2 → defaultM-222D2D2 → K ≥ 85 or F ≥ 95L-34PathRungppD3D3 → under 20% budget headroomM-25D3D3 → defaultL-313D3D3 → R ≥ 85L-3R1D4D4 → under 20% budget headroomL-32D4D4 → defaultL-3R3
Illustrative model · FIG-04 · leaf shares in points of total traffic; leaves sum to 100

The router is a tree, not a model. Every request carries the path it took and the condition on each edge it crossed, which means a routing decision can be argued with — by you, in a ticket, six weeks later.

Cache hits are counted as a leaf, because they are a routing decision too.

Four side rules

These override the tree. They exist because some failures are not worth being clever about.

  • Format strictness at or above 80 forces a rung with schema validation, and a validation failure escalates.
  • Factual sensitivity at or above 70 disables the small general tier entirely.
  • Below 20% budget headroom the whole ladder shifts down one rung until the end of the day.
  • Refusal risk at or above 70 races two rungs in parallel and takes the first answer that clears the floor.

Step 03 — Fallback ladder

FAIL SIDEWAYS, NOT DOWN.

A ladder is an ordered chain of three to five rungs. Each rung is a model tier plus its parameters plus a prompt version, with a trigger condition and a maximum wait. The same ladder runs in both directions: up when the first pick fell short, down when the budget or the upstream says it has to.

Four things move a request to the next rung: a timeout, an upstream error, a schema validation failure, or a judge score under the endpoint’s floor. Nothing else. There is no mood in it.

What moves a request up

Timeout
The rung’s max wait elapsed. The in-flight call is abandoned, not waited out.
Upstream error
A 5xx or a 429 from the provider, after the retry the rung is allowed.
Schema failure
Structured output did not validate against the endpoint’s schema.
Under the floor
The sampled judge score came in below the floor this endpoint declared.
Ten thousand requests, followed to the endper 10,000 arrivalsFIG-05ILLUSTRATIVE
per 10,000 arrivalsanswered, one way or another99.99%arrive10,000served from cache800routed9,200first pick passes8,455ladder triggered7458.1%02,0004,0006,0008,00010,000of the calls that triggered the ladderhatched = counted as a failureresolved at +1 rung589resolved at +2 rungs127resolved at top rung25brownout response3hard failure1ladder triggeredΣ 745first-pick pass rate91.9%escalation rate8.1%Gained on escalation, judge points+12.0
Illustrative model · FIG-05 · per 10,000 arrivals; rungs resolved, brownout responses and hard failures sum to the escalated count

First picks hold 91.9% of routed calls. The 8.1% that escalate are mostly under-routed requests being corrected, and the average escalated answer gains 12 judge points by moving up — which is the whole argument for having a ladder instead of a bigger default.

What the ladder costs

Median latency drops 58%, because most requests never needed a frontier model in the first place. P99 gets 1% worse, because an escalated request makes two hops. We print both. If an endpoint cannot absorb that tail, race two rungs in parallel and pay for the second one.

  • −58%Percentile P502,265ms → 955ms
  • +1%Percentile P9913,332ms → 13,511ms
Latency, both distributionsend-to-end latency, ms — log-spaced bucketsFIG-06ILLUSTRATIVE
RoutedBaselineshare of calls, %0102030400–250250–500500–1k1k–2k2k–4k4k–8k8k–16kend-to-end latency, ms — log-spaced bucketsPercentileBaselineP502,265msP753,532msP905,911msP957,545msP9913,332msRoutedP50955msP751,927msP904,223msP957,257msP9913,511msP99 +1% · hatched = worse than baselineRouter overheadfingerprint / decision & budget, p95 9 / 4 msP506msP9511msP9919ms
Illustrative model · FIG-06 · end-to-end latency, log-spaced buckets, both distributions on the same axis

The ladder is a text file

This is the whole configuration for one endpoint. Export it and it runs somewhere else; there is no state hidden in our console that you would have to re-derive.

Thresholds, waits and the brownout trigger are all in one file, versioned with the prompt versions it names.

chat.ladder.yamlILLUSTRATIVE
endpoint: /chat
quality_floor:
  judge_p10: 74
route_budget:
  usd_per_1k_calls: 4.20
  hard_cap: true
ladder:
  - tier: C-0                 # semantic cache
    when: { cache_cos: ">=0.94", same_prompt_version: true }
  - tier: S-1
    when: { D: "<25", K: "<70" }
    max_wait_ms: 1200
  - tier: M-2
    when: { D: "25..55", or_escalated_from: S-1 }
    max_wait_ms: 2500
  - tier: L-3
    when: { D: ">=55", or_escalated_from: M-2 }
    max_wait_ms: 6000
brownout:
  trigger: { upstream_5xx_or_429: ">8% over 60s" }
  action: { cap_output_tokens: 512, drop_top_tier: true, keep_serving: true }
  cooldown_min: 10

Step 04 — Brownout

DIM THE LIGHTS, DON’T CUT THE CITY.

When a provider throttles or falls over, the endpoint does not start returning 5xx. It drops its top rung, caps output length and keeps answering — worse answers, on purpose, out loud.

The name is borrowed from the grid, where dimming a city is the designed response to losing supply and blacking it out is the failure.

Four states

Four states
StateEntered whenWhat it does
normalUpstream errors under the trigger on a rolling 60-second window.Full ladder, declared budgets, top tier available.
watchThe error rate crosses the trigger but the window has not closed yet.Nothing user-visible changes. The router starts counting and the clock on the detection target starts.
brownoutThe window closes above the trigger — 5xx and 429 over 8% across 60 seconds.Top rung dropped, output capped at 512 tokens, the reasoning tier disabled. The endpoint keeps serving.
recoverUpstream back under the trigger.The endpoint holds the degraded ladder for a ten-minute cooldown, then restores the top rung on the same 5% → 25% → 100% ramp used for a swap.

Cooldown exists because a provider that just came back is the most likely thing to go away again. Flapping between states costs more than staying dim for ten minutes.

Step 05 — Swap window

NEW TIERS LIVE IN THE SHADOW FIRST.

Three percent of live traffic is mirrored to candidate tiers. Those answers are never returned to anyone; they exist only to move a quality score and narrow its confidence interval.

A candidate becomes a default only after it clears three gates. Then it ramps — it does not switch.

Three gates

5,000 shadow samples
On the same endpoint. Not 5,000 across the deployment — endpoints do not generalise to each other.
Seven consecutive days
Enough to contain a weekend and a Monday, which look nothing alike.
CI lower bound within 0.5 points
The 95% interval’s lower bound may not sit more than half a point below the incumbent.

The 5,000 is not a round number picked for a slide. At a judge-score spread of about 22 points, that is where the 95% interval narrows to roughly ±0.6 — which is what makes a −0.5 rule mean anything at all.

Then it ramps

Promotion goes 5% → 25% → 100%, one endpoint at a time, with a rollback switch at every step. Last quarter, in this illustrative deployment: three promoted, one rolled back, two retired.

Swap windowinterval half-width, judge pointsFIG-09ILLUSTRATIVE
interval half-width, judge pointsexpected half-width at this sample countσ ≈ 22±0±1±2±3±0.6swap threshold±3.1200±1.9500±1.41,000±1.02,0005,000±0.410,000±0.320,000shadow samples on one endpoint — log scaleinterval half-width, judge pointsPromotion rampswap threshold · 5,000 · 7 d15%225%3100%
Illustrative model · FIG-09 · 95% CI half-width against shadow sample count, judge score σ ≈ 22

Next

What this method actually produced.

Read the full numbers