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.tsxAbout
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.00warn at £20.00hard limit £25.00
- Tokens184,000 / 500,000warn at 400,000hard limit 500,000
- Tool calls12 / 40hard limit 40per run
<BudgetGuard
budgets={[
{ id: 'spend', label: 'Spend', used: 1840, soft: 2000, hard: 2500 },
]}
/>Examples
One budget past its warning threshold
<BudgetGuard budgets={budgets} />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'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| budgets | Budget[] | required | An empty list renders as “this run is uncapped”, not as an empty box. |
| stoppedLabel | string | 'stopped' | — |
| warningLabel | string | 'over the warning threshold' | — |
| hardLabel | string | 'hard limit' | — |
| softLabel | string | 'warn at' | — |
| emptyLabel | string | 'No budgets configured — this run is uncapped.' | — |
| label | string | 'Budgets' | — |
| scale | against 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
| Field | Type | Description |
|---|---|---|
| id | string | — |
| label | ReactNode | — |
| used | number | Consumed so far, in whatever unit format renders. |
| soft? | number | Soft 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? | number | Soft 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 | — |