Token Budget Allocator

A direct-manipulation visualiser for how production AI systems allocate a fixed token budget across competing context sections using priority-based truncation. The learner drags a single budget slider and flips per-section priority chips — MUST, HIGH, MEDIUM, LOW — and a stacked SVG bar chart redistributes tokens in real time.

The deterministic allocator runs four passes: MUST first (full request), then HIGH, MEDIUM, LOW, with proportional fill within each tier when budget runs out. Sections that don't fit show a striped truncation overlay. When MUST overflows, the summary turns red and the narration calls out that the prompt builder would fail.

Phase-keyed narration moves through observeadjustinginsight as the learner explores.

budget 32KSystem Prompt3K / 3KRetrieved Docs20K / 20KHistory8.5K / 25KtruncatedFew-Shot0 / 1.5KUser Message500 / 500
System Prompt
3K / 3K
Retrieved Docs
20K / 20K
History
8.5K / 25K
Few-Shot
0 / 1.5K
User Message
500 / 500

3,500 MUST20,000 HIGH8,500 MEDIUM0 LOW

Budget: 32,000 tokens. 2 of 5 sections truncated. Total allocated: 32,000.

5 sections want 50,000 tokens, but only 32,000 are available. The allocator fills MUST first, then HIGH, MEDIUM, LOW. Something has to give.

Customize
Budget
32K
8K
128K

Installation

npx shadcn@latest add https://craftbits.dev/r/token-budget-allocator.json

Usage

import { TokenBudgetAllocator } from "@craft-bits/viz/token-budget-allocator";
 
<TokenBudgetAllocator />

Custom starting budget and slider bounds:

<TokenBudgetAllocator
  defaultBudget={64_000}
  minBudget={16_000}
  maxBudget={200_000}
/>

Subscribe to allocations from outside:

<TokenBudgetAllocator
  onAllocate={(rows) => {
    const truncated = rows.filter((r) => r.truncated).map((r) => r.name);
    console.log("truncated", truncated);
  }}
/>

Understanding the component

  1. Coordinate system. The plot is a 560 × dynamic SVG with origin top-left. The left 100px holds section names; the rest is the budget bar track, with 60px on the right reserved for the per-row count label. The bar height grows with the section count.
  2. Allocation algorithm. A pure function runs four passes: fill all MUST first, then walk HIGHMEDIUMLOW. Inside a tier, if there's enough budget, everyone gets their full request; otherwise the remaining budget is distributed proportional to each section's request and every section in that tier is marked truncated.
  3. Budget-line scaling. The dashed accent line marks the budget edge. The bar's pixel scale is BAR_W / max(totalRequested, budget), so dragging the budget past the total requested doesn't compress everything — it just shrinks the budget line's position relative to the rightmost bar end.
  4. Truncation overlay. When allocated < requested, a striped overlay (45° rotated pattern) fills the missing portion. The pattern colour matches the section's priority.
  5. Imperative animation. Both per-row bar widths and the dashed budget line are animated via motion's animate() so the SVG never re-renders per frame. Under prefers-reduced-motion: reduce, every animation collapses to an instant attribute set.
  6. Phase machine. observe is the initial state, adjusting triggers once the budget slider moves, insight triggers once any priority chip is flipped. Phase drives the tint of the narration paragraph; a MUST overflow short-circuits to the error tint.

Props

PropTypeDefaultDescription
sectionsreadonly TokenBudgetAllocatorSection[]five-section LLM prompt anatomySections to allocate across.
defaultBudgetnumber32_000Initial budget in tokens.
minBudgetnumber8_000Minimum budget the slider can reach.
maxBudgetnumber128_000Maximum budget the slider can reach.
budgetStepnumber1_000Slider step size in tokens.
onBudgetChange(budget) => voidFires when the budget slider moves.
onAllocate(allocated) => voidFires when allocation is recomputed.
transitionTransitionSPRINGS.snapOverride the bar-width spring.
budgetLineTransitionTransitionSPRINGS.smoothOverride the budget-line spring.
classNamestringMerged onto the root via cn().

Accessibility

  • The viz SVG is role="img" with an aria-label summarising budget, total requested, and truncated section count.
  • The budget slider is a native <input type="range"> with full keyboard support (arrow keys advance one budgetStep).
  • Each priority chip is a <button> with aria-pressed and a descriptive aria-label, plus a visible focus-visible ring.
  • A visually hidden sr-only status region with aria-live="polite" and aria-atomic announces the budget, truncated count, and total on every change.
  • The narration paragraph below the SVG is aria-live="polite" and announces phase transitions in prose.
  • Colour is never the only signal — the summary line names each priority tier in plain text and per-row labels read allocated / requested numerically.
  • Motion respects prefers-reduced-motion: reduce — every animation collapses to an instant attribute set.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/systems/TokenBudgetAllocator.tsx). The source was a lesson primitive wrapped in the project's SvgLabel text helper and ChallengeBtn chrome, consumed lesson-track palette tokens (--color-fail-400, --color-warn-400, --color-accent-400, --color-ink-*), and used the project's ca-narration paragraph. The viz extract drops the lesson chrome, swaps the palette to var(--cb-*) semantic tokens, replaces SvgLabel with bare <text> carrying the canonical cb-* font, exposes sections / defaultBudget / minBudget / maxBudget / budgetStep / onBudgetChange / onAllocate, and pins the imperative bar-width animation to SPRINGS.snap and the dashed-line animation to SPRINGS.smooth with overrides.