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.
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.
Installation
npx shadcn@latest add https://craftbits.dev/r/model-router-viz.jsonUsage
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
- 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.
- 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.
- Phase derivation. Selection drives a derived
ModelRouterVizPhase—idlebefore any routing,routingwhile requests are streaming,routedonce every request is placed, andcomparingwhile the all-top toggle is active. The narration paragraph and its background tint both follow the phase. - 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.
- 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
| Prop | Type | Default | Description |
|---|---|---|---|
requests | readonly ModelRouterVizRequest[] | curated 5-request dataset | Requests to route. |
tiers | readonly ModelRouterVizTierInfo[] | Haiku / Sonnet / Opus | Three-tier model definitions. Order is preserved for lane rendering. |
cascade | Record<number, ModelRouterVizCascadeStep[]> | default Haiku→Sonnet→Opus trace | Cascade trace keyed by request.id. Only consulted in "cascade" mode. |
mode | ModelRouterVizMode | — | Controlled routing mode. Pair with onModeChange. |
defaultMode | ModelRouterVizMode | "classifier" | Uncontrolled initial mode. |
onModeChange | (mode) => void | — | Fires when the mode changes. |
onPhaseChange | (phase) => void | — | Fires when the narration phase transitions. |
transition | Transition | SPRINGS.smooth | Override the entrance spring for stats / summary / cascade rows. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root is
role="figure"with anaria-labeldescribing the dataset. - The mode strip is a
role="group"witharia-pressedon 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 anaria-pressedstate 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.
Spaceroutes the next request,Atoggles the brute-force baseline,Ctoggles 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 literaloklch(…)swatches, depended on the project'sSPRINGS.snappy/SPRINGS.gentle/SPRINGS.bouncyre-exports plus aSTAGGER.tightobject key, and read raw--color-ink-*/--color-accent-400/--color-surface-raisedtheme tokens. The viz extract remaps the palette tovar(--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 canonicalSPRINGS.snap/SPRINGS.smooth/SPRINGS.bouncy/SPRINGS.dampedandSTAGGERconstant from@craft-bits/core/motion, wraps the component inforwardRef, acceptsclassNameviacn(), spreads...propson the root, hoists the 5-request corpus, 3-tier table, and cascade trace torequests/tiers/cascadeprops (with the originals shipped asMODEL_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 anonModeChangeevent, and adds anonPhaseChangeevent so consumers can wire the viz to external scoreboards or tracking.