Budget Guard

Spend and token caps against what a run has consumed. A soft limit warns and a hard limit stops — collapsing them into one bar is how a team ends up believing they have a spend cap when what they have is a notification.

src/components/ui/budget-guard.tsx

About

From the component's own source, where it was written.

Spend and token caps, against what a run has actually consumed.

The distinction the component is built around: a **soft** limit warns, a **hard** limit stops. Collapsing them into one bar is how a team ends up believing they have a spend cap when what they have is a notification.

So a hard limit is drawn as a wall on the track — a solid marker with a stop glyph — and a soft one as a dashed threshold. Once a hard limit is passed the whole row is destructive and says stopped, because at that point the run is not running and every other reading on the screen is historical.

Bars are scaled to the hard limit when there is one, so "how much room is left before it stops" is the length being read. Scaling to the soft limit makes a run look finished when it has half its budget left.

Install

npx astralyx-ui add budget-guard

Usage

import { BudgetGuard } from '@/components/ui/budget-guard'

<BudgetGuard budgets={[{ id: 'spend', label: 'Spend', used: 18_40, soft: 20_00, hard: 25_00 }]} />

Composer

Change the props and the source below follows.

Budgets

  • Spend£18.40 / £25.00
    warn at £20.00hard limit £25.00
  • Tokens184,000 / 500,000
    warn at 400,000hard limit 500,000
  • Tool calls12 / 40
    hard limit 40per run
Props
tsx
<BudgetGuard
  budgets={[
    { id: 'spend', label: 'Spend', used: 1840, soft: 2000, hard: 2500 },
  ]}
/>

Examples

One budget past its warning threshold

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

BudgetGuard

Spend and token caps, against what a run has actually consumed.

Extends Omit<ComponentProps<'div'>, 'children'>.

PropTypeDefaultDescription
budgetsBudget[]requiredAn empty list renders as “this run is uncapped”, not as an empty box.
stoppedLabelstring'stopped'—
warningLabelstring'over the warning threshold'—
hardLabelstring'hard limit'—
softLabelstring'warn at'—
emptyLabelstring'No budgets configured — this run is uncapped.'—
labelstring'Budgets'—
scaleagainst the hard limit—So the length being read is “room left before it stops”. Scaling to the soft limit makes a run look finished with half its budget left.

Types

Budget

FieldTypeDescription
idstring—
labelReactNode—
usednumberConsumed so far, in whatever unit format renders.
soft?numberSoft warns, hard stops. A hard limit is a wall on the track and turns the row destructive when passed; a soft one is a dashed threshold.
hard?numberSoft warns, hard stops. A hard limit is a wall on the track and turns the row destructive when passed; a soft one is a dashed threshold.
format?(value: number) => string—
note?ReactNode—