How Routing Works

Back to home

V1 Honesty: OffRouter routes delegated work. It does not replace your harness's primary model call.

Every routing decision runs the same deterministic pipeline, in the order below. Same inputs always produce the same decision, and every stage is visible in the output of offrouter route explain.

1. Policy Gates

Policy runs first. Every candidate provider/model pair is checked, and anything that fails is removed with a structured denial: a machine-readable code, a target, and a message. Denied candidates are never resurrected by later stages.

Profile and workspace gates

Per-candidate hard filters

Paid-spend gates (API-key candidates only)

Outbound secret scan

Before any provider call, outbound prompt text is scanned once for credential shapes. The outbound_secret_scan policy key picks the mode: "warn" (the default) proceeds and audits the finding, "block" fails the request before any network call (outbound_secret_blocked), and "off" skips the scan. Findings name the credential pattern only; matched values are never logged.

2. User-Defined Routing Rules

After policy gates, user-defined [[routing.rules]] are evaluated in order. The first rule whose match keys all hold selects a preference among the allowed candidates:

Rules match on caller-supplied role (exact equality) and/or the classifier's family signal. Proxy traffic has no role surface in V1, so proxy requests can only match family rules. If no rule matches, the pipeline continues in auto mode.

3. Subscription-First Tier Order

Surviving candidates are ranked, and the primary sort key is always the auth tier:

  1. Healthy subscription (active)
  2. Healthy subscription near its limit
  3. Local runtime
  4. Degraded subscription
  5. API key

This order is a hard invariant: nothing below can cross it. A sick subscription still beats a healthy API key, and no health score or classifier signal ever promotes an API-key candidate over a subscription candidate. Policy reinforces the same rule separately: while a healthy subscription can satisfy the task, API-key candidates are denied outright (api_key_blocked_by_subscription_first_policy), and with allow_api_key_fallback = false they stay denied even without one.

When the winning subscription is near its limit and no healthier subscription alternative exists, the decision carries a near-limit warning telling you which account is affected.

4. Health Re-Ordering Within a Tier

Inside a single tier, recent outcomes reshuffle candidates before cost is considered. OffRouter keeps a small per-(provider, model) record of successes, failures, and an exponentially weighted latency average:

Health tracking is automatic: there are no config keys for it, and it never crosses tier boundaries.

5. Classifier Signal Within a Tier

A deterministic task classifier inspects the prompt preview and its length — no network, no LLM, no config keys — and recommends a logical model family: fast-coder or frontier-coder. The rules are simple heuristics. Prompts over 2,000 characters, multiple code fences, and multi-step verbs like refactor or migrate push toward frontier; short prompts (280 characters or less) with mechanical verbs like rename or format push toward fast. Ambiguous input defaults to frontier-coder at low confidence, so complex work is never quietly under-served.

The recommendation is a signal, not authority. Only a high-confidence classification nudges matching-family candidates up within their tier, after health re-ordering. Medium and low confidence change nothing and are recorded for explain output only. The classifier never crosses tiers, never resurrects a policy-denied candidate, and policy always has the final say.

6. Cost and Id Tiebreaks

If candidates are still tied after tier, health, and classifier ordering, cheaper wins: lower estimated cost first (an unknown cost is treated as $0.05), then provider id, then model id, both alphabetical. The result is fully deterministic — the same inputs always produce the same decision.

Inspecting Decisions

offrouter route explain "summarize this file" --profile claude-personal
offrouter status --costs
offrouter doctor