Model Router Viz

A walkthrough of model routing — sending easy requests to cheap models and hard requests to expensive ones. Five user requests sit in a queue on the left; a central router classifies each and lanes it into one of three tier rails on the right (Haiku / Sonnet / Opus by default). Once all five are routed, a cost summary appears with a toggle for the brute-force baseline — "send every request to the top tier" — so the savings are unambiguous.

A second mode, cascade, replaces the classifier with confidence-gated escalation: every request starts at Tier 1, escalates if the confidence is too low, and pays the latency tax of each hop. The cascade trace below the lanes shows the per-step confidence bar, the escalation arrows, and the cumulative latency.

Routing mode
C to toggle
Request Queue
What are your hours?
~600 tokens
Compare Q3 vs Q4 revenue and suggest strategy
~2800 tokens
Translate 'hello' to Spanish
~250 tokens
Debug why this async pipeline deadlocks
~4000 tokens
What's 2+2?
~120 tokens
CLASSIFIER
Tier 1 — Haiku
$0.25/M in · $1.25/M out
No requests yet
Tier 2 — Sonnet
$3/M in · $15/M out
No requests yet
Tier 3 — Opus
$15/M in · $75/M out
No requests yet
Space to route
Model router visualization ready. Press Space to route the first request.

Five user requests are waiting. A lightweight classifier will examine each one and route it to the cheapest model that can handle it well. Press Space or the Route button to send the next request through the router.

Customize
Mode
classifier

Installation

npx shadcn@latest add https://craftbits.dev/r/model-router-viz.json

Usage

import { ModelRouterViz } from "@craft-bits/viz/model-router-viz";
 
<ModelRouterViz />

Drive the mode from outside (controlled):

const [mode, setMode] = useState<ModelRouterVizMode>("classifier");
 
<ModelRouterViz mode={mode} onModeChange={setMode} />;

Bring your own corpus and tiers:

<ModelRouterViz
  requests={myRequests}
  tiers={myTiers}
  cascade={myCascade}
  onPhaseChange={(phase) => {
    if (phase === "comparing") trackBruteForceShown();
  }}
/>

Understanding the component

  1. Queue → router → lanes. The 3-column layout reads left-to-right: the request queue, the diamond router with a pulse animation while routing is in progress, and the three tier lanes that fill one request at a time. Each routed request crossfades from the queue into a lane chip while a counter in the centre announces the in-flight request.
  2. Cost math, made visible. Each lane shows its per-1M-token prices in the header, each chip prints its own cost, and the lane prints a running subtotal once it has any requests. The bottom-of-card summary sums every routed request and contrasts it with the brute-force total when the "Route ALL to top tier" toggle is on.
  3. Phase derivation. Selection drives a derived ModelRouterVizPhaseidle before any routing, routing while requests are streaming, routed once every request is placed, and comparing while the all-top toggle is active. The narration paragraph and its background tint both follow the phase.
  4. Cascade trace. In cascade mode, every routed request renders one trace row with one confidence bar per attempt, an arrow between escalations, and a latency badge that turns red if the request needed more than one hop. The bottom of the trace prints the cascade total cost and total latency.
  5. Reduced motion. Under prefers-reduced-motion: reduce, the router's pulse, the chip pop-ins, every entrance, the brute-force scale flourish, and the cascade-bar grow all collapse to instant.

Props

PropTypeDefaultDescription
requestsreadonly ModelRouterVizRequest[]curated 5-request datasetRequests to route.
tiersreadonly ModelRouterVizTierInfo[]Haiku / Sonnet / OpusThree-tier model definitions. Order is preserved for lane rendering.
cascadeRecord<number, ModelRouterVizCascadeStep[]>default Haiku→Sonnet→Opus traceCascade trace keyed by request.id. Only consulted in "cascade" mode.
modeModelRouterVizModeControlled routing mode. Pair with onModeChange.
defaultModeModelRouterVizMode"classifier"Uncontrolled initial mode.
onModeChange(mode) => voidFires when the mode changes.
onPhaseChange(phase) => voidFires when the narration phase transitions.
transitionTransitionSPRINGS.smoothOverride the entrance spring for stats / summary / cascade rows.
classNamestringMerged onto the root via cn().

Accessibility

  • The root is role="figure" with an aria-label describing the dataset.
  • The mode strip is a role="group" with aria-pressed on each mode button, so screen-reader users can announce and switch modes via keyboard.
  • The Route, Reset, and "Route ALL" toggle all expose explicit aria-labels and an aria-pressed state where applicable, and all reach ≥ 36×36px hit area.
  • A polite live region announces the current selection (with cost + tier) or, when complete, the smart routing total and the brute-force savings.
  • Space routes the next request, A toggles the brute-force baseline, C toggles the routing mode.
  • Color is never the only signal — every difficulty chip carries its name, every lane prints its label and price, and the cascade trace prints the model name above each confidence bar.
  • Motion respects prefers-reduced-motion: reduce — every entrance, pop, pulse, and width-grow collapses to instant.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/systems/ModelRouterViz.tsx). The source bound colours to literal oklch(…) swatches, depended on the project's SPRINGS.snappy / SPRINGS.gentle / SPRINGS.bouncy re-exports plus a STAGGER.tight object key, and read raw --color-ink-* / --color-accent-400 / --color-surface-raised theme tokens. The viz extract remaps the palette to var(--cb-success) / var(--cb-info) / var(--cb-error) / var(--cb-warning) / var(--cb-fg-*) / var(--cb-bg-*) semantic tokens, re-keys every spring to the canonical SPRINGS.snap / SPRINGS.smooth / SPRINGS.bouncy / SPRINGS.damped and STAGGER constant from @craft-bits/core/motion, wraps the component in forwardRef, accepts className via cn(), spreads ...props on the root, hoists the 5-request corpus, 3-tier table, and cascade trace to requests / tiers / cascade props (with the originals shipped as MODEL_ROUTER_VIZ_DEFAULT_REQUESTS / MODEL_ROUTER_VIZ_DEFAULT_TIERS / MODEL_ROUTER_VIZ_DEFAULT_CASCADE), exposes the routing mode via the controlled / uncontrolled Radix pattern with an onModeChange event, and adds an onPhaseChange event so consumers can wire the viz to external scoreboards or tracking.