Crux
GuidesRouting & Fallback

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/ai

Primitives

NeedUse
Choose a route before generationrouter()
Send a stable percentage of traffic to each routesplit()
Retry the same model before giving upretry()
Try another model after failure or unusable outputfallback()
Generate, inspect the result, then escalate if neededcascade()

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.

On this page