Complexity Routing
What you'll accomplish: Configure the routing gateway's three cost/capability tiers, understand how PII policy and explicit provider intent interact with size-based classification, and read the Local/Cloud pool composition the dashboard uses.
There is no [routing] section with simple_model/medium_model/complex_model fields. The real mechanism is the routing gateway (crates/platform/hoziron-core/src/routing/gateway.rs + tier.rs), configured under [routing_gateway] (ADR-053/054).
The three tiers
A request classifies into exactly one of three tiers, purely by size — this classification never dispatches by itself, it only selects which of the gateway's three ranked preference lists resolves the request:
| Tier | Purpose | Boot requirement |
|---|---|---|
local | Trivial enough to need nothing beyond a Local model — hard-walled, Local-sovereignty-only | preference required (non-empty) whenever the inventory has a non-empty Local pool |
economy | Needs Cloud/general capability, but a cheap/fast model suffices — disabled by default | Not boot-required; opt-in |
standard | The catch-all: everything else | preference required (non-empty) whenever the inventory has any provider at all |
[routing_gateway.local]
max_chars = 500 # default; size-based `local` classification
max_tools = 0 # default: any tools present → too complex for size-based local
preference = [{ provider = "ollama.west", model = "llama3.2" }]
[routing_gateway.economy]
max_chars = 2000 # default 0 = disabled; a request never lands in `economy`
max_tools = 2 # unless you widen these thresholds
preference = [{ provider = "ollama.west", model = "llama3.2" }, { provider = "big.licence", model = "claude-sonnet-4-5-20260929" }]
[routing_gateway.standard]
preference = [{ provider = "big.licence", model = "claude-sonnet-4-5-20260929" }]
localandeconomymay freely mix Local- and Cloud-sovereignty providers in theirpreferencelist exceptlocal, whose list is hard-walled to Local-sovereignty providers only (a Cloud entry there is a boot error) — becauselocal's preference list is shared infrastructure consulted by every path that routes to Local (size classification, a PII-policy force, or an explicitForceLocalintent), not just the size tier.economy/standardmay mix Local and Cloud (Pool::Mixed) — a request reaching either tier has already cleared PII policy as Cloud-eligible, so ranking a self-hosted model there is never a compliance regression.- Widening
economy's thresholds without also setting itspreferencesilently opts into declaration-order-over-the-combined-pool — setpreferenceexplicitly once you enable a tier.
Classification rules
// routing/tier.rs
- A workflow type in
always_standard_workflows, or the presence of attachments, always forcesStandardregardless of size. - Otherwise: within
local's thresholds →Local; else withineconomy's thresholds →Economy; elseStandard. - Classification is pure size/shape — it has no notion of PII policy or explicit provider intent by itself. Two independent gates evaluate on top of it inside
gateway.rs::route():
- A
local_onlyPII type detected in the payload forces local routing immediately — checked first, before size classification is even consulted for that decision. See PII Engine § LLM dimension. This is the single source of the force-local verdict — there is no separate provider-pattern rule engine layered on top. - An explicit
RequestedProvider::ForceLocalintent (an agent hard-requiring the Local tier) also routes through the same hard-walled Local pool. - Both bypass size classification for routing purposes — the fact that a message happened to be short or long never overrides a
local_onlyPII hit or an explicit force-local request.
Live availability (circuit breakers)
Ranked preference resolution filters out providers whose circuit is currently open (Issue #448) — the same CircuitBreakerRegistry the platform records dispatch success/failure into. Without this wired (rare — only in isolated test setups), every pool member is treated as available.
Gateway pools (dashboard / operator visibility)
curl http://localhost:4200/gateway/pools -H "Authorization: Bearer hzk_..."
Returns the Local/Cloud pool composition sourced from the same ADR-053 inventory the gateway itself dispatches against — each entry's declared driver disposition, resolved sovereignty reason (native-local / attested-local / defaulted-cloud / cloud), live auth status, live circuit-breaker reachability, and resolvable models:
{
"local": [
{
"id": "ollama.west",
"driver": "ollama",
"disposition": "AlwaysLocal",
"sovereignty_reason": "native-local",
"auth_status": "NotRequired",
"reachable": true,
"models": ["llama3.2", "qwen2.5"]
}
],
"cloud": [
{
"id": "big.licence",
"driver": "anthropic",
"disposition": "AlwaysCloud",
"sovereignty_reason": "cloud",
"auth_status": "Configured",
"reachable": true,
"models": ["claude-sonnet-4-5-20260929"]
}
]
}
GET /gateway/pools requires config:read — the same role bar (Admin/Operator/Developer/Viewer/Auditor) as reading the rest of the routing config.
Next steps
Related: