Routing
Choose, retry, split, escalate, and fail over between models with one receipt.
Your primary provider starts returning 429s the morning you launch. Your best model is too expensive for easy support tickets. You want to send 5% of traffic to a new model before trusting it with everything. Routing covers all three with five composable primitives.
Routing primitives are model values. Pass them as the model option to generate() or stream(), and Crux resolves them to one concrete provider model before the adapter call.
npm install @use-crux/core
@use-crux/aiPrimitives
| Need | Use |
|---|---|
| Choose a route before generation | router() |
| Send a stable percentage of traffic to each route | split() |
| Retry the same model before giving up | retry() |
| Try another model after failure or unusable output | fallback() |
| Generate, inspect the result, then escalate if needed | cascade() |
All five compose recursively. router, split, retry, and fallback work with generate() and stream(). cascade is generate-only because it must inspect complete results.
import {
fallback,
retry,
router,
split,
type RouteArgs,
} from "@use-crux/core/routing";
const canary = split({
id: "gpt-5-mini-canary",
seed: ({ context }: RouteArgs<{ sessionId: string }>) => context.sessionId,
routes: {
stable: { model: gpt4o, weight: 95 },
canary: { model: gpt5mini, weight: 5 },
},
});
export const supportModel = router({
id: "support-model",
classify: ({
input,
context,
}: RouteArgs<{ tier: "free" | "enterprise" }, { question: string }>) =>
context.tier === "enterprise" || input.question.length > 2000
? "deep"
: "fast",
routes: {
fast: fallback([
retry(gpt5nano, { id: "nano-retry", attempts: 2 }),
canary,
]),
deep: sonnet,
default: sonnet,
},
});Use call-site options for request-specific context and overrides:
const result = await generate(supportPrompt, {
model: supportModel,
input: { question },
routing: { tier: account.tier, sessionId },
route: "deep",
});Receipts
Every routed result exposes one append-only receipt:
result.routing;
// {
// model: 'claude-sonnet-4-5',
// cost: 0.018,
// trace: [
// { kind: 'router', id: 'support-model', route: 'deep', forced: true },
// { kind: 'fallback', attempts: [...] },
// ],
// }The same receipt is available on stream completions and routing errors such as FallbackExhaustedError and CascadeExhaustedError.
Project Index
The Project Index records every authored routing definition with its folded child rows, and stable id values join those definitions to runtime spans, receipts, and decision reports: see Routing definitions and catalog facts.
Router
Classify input and select a route before generation.
Split
Run deterministic weighted traffic splits.
Retry
Retry a model with bounded attempts.
Fallback
Recover from failures and invalid responses.
Cascade
Escalate through result-aware quality tiers.
Streaming
Understand first-token and mid-stream failure rules.