guard() reference
Every parameter of guard() — the one call that wraps your LangGraph app with a budget.
Signature
from breakerbox import guard
app = guard(
compiled_app, # your compiled LangGraph app
budget_usd=5.00, # required
max_hops=100,
ttl_seconds=None,
velocity_usd_per_min=None,
on_trip="pause", # "pause" | "kill"
sub_budgets=None, # {"node_id": 2.00, ...}
topup_policy="deny", # "deny" | "auto" | callable
unknown_model="fail", # "fail" | "default_rate"
report_dir="./breakerbox_reports",
report_to=None, # cloud ingest URL
detect_loops=False, # True | {window, threshold, repeats}
live=False, # True prints a live $-spent line; or pass a callback
max_depth=None, # cap sub-agent nesting depth (anti-evasion)
alerts=False, # True | {thresholds, on_alert} — warn before the kill
tags=None, # {"team": "growth", "env": "prod"} — cost attribution
)guard() returns a drop-in wrapper — call app.invoke(...) / app.stream(...) exactly as before.
Parameters
| Parameter | Type | Default | What it does |
|---|---|---|---|
app | compiled LangGraph app | — | The app to guard. |
budget_usd | float | required | Hard dollar cap for the whole run — hierarchical across sub-agents. |
max_hops | int | 100 | Trips if the run exceeds this many hops (runaway-loop guard). |
ttl_seconds | int | None | None | Wall-clock limit; trips if the run runs longer. |
velocity_usd_per_min | float | None | None | Spend-rate limit; trips if $/min exceeds it. |
on_trip | "pause" | "kill" | "pause" | pause = checkpoint so you can resume with more budget; kill = stop and finalize the receipt. |
sub_budgets | dict[str, float] | None | None | Per-node dollar caps ({node_id: usd}). A node draws from its own cap and the parent's remaining budget. |
topup_policy | "deny" | "auto" | callable | "deny" | On a pause, whether to add budget: deny (stay paused), auto, or a callable that decides how much. |
unknown_model | "fail" | "default_rate" | "fail" | A model not in the price table: fail (raise) or default_rate (meter at a conservative rate). |
report_dir | str | Path | "./breakerbox_reports" | Where the receipt (terminal summary + single-file HTML + JSON) is written. |
report_to | str | None | None | A cloud ingest URL to stream events to (best-effort, non-blocking). |
otel | bool | False | Emit an OpenTelemetry span per run + per hop (GenAI semantic conventions). Needs pip install 'breakerbox[otel]'. |
detect_loops | bool | dict | False | Semantic loop detection: trip (reason loop) when a node keeps re-firing with near-identical input — before the budget does, cheaper and faster. No LLM (character-shingle Jaccard). True uses defaults; pass a dict to tune {window, threshold, repeats}. Opt-in. |
live | bool | Callable | False | Real-time spend counter. True prints a live $spent / $budget line to stderr as each hop reconciles (in-place on a TTY). Pass a callback fn({"spent_usd", "budget_usd", "hops"}) to render it yourself. Local only, no server. Opt-in. |
max_depth | int | None | None | Cap sub-agent nesting depth (trip reason depth). A run that spawns sub-agents/subgraphs deeper than this trips — checked in the guard-injected callback, outside agent-controllable code, so an adversarial agent can't route around it. Spend is already shared across all nesting levels; this adds a structural cap. Top-level calls are depth 1. |
alerts | bool | dict | Callable | False | Graduated warn-before-kill rail. As spend crosses budget thresholds it fires once each — before the hard trip. True uses [0.5, 0.8, 0.95] and logs to stderr; pass a callback fn({"threshold", "pct", "spent_usd", "budget_usd"}), or a dict {"thresholds": [...], "on_alert": fn} to tune. Local-first, opt-in. |
tags | dict | None | None | Cost-attribution tags ({"team": "growth", "customer": "acme", "env": "prod"}) threaded into the run's start event, the receipt (terminal + HTML), and the cloud stream — so cost is attributable, not just a per-run total. Keys and values are coerced to strings. |
When a budget trips
Breakerbox trips at hop boundaries, never mid-call, so no request is interrupted:
on_trip="pause"— checkpoints the run; resume later with more budget (seetopup_policy).on_trip="kill"— raisesBudgetKilled; the receipt lists the trip reason and any side-effecting tools that fired before the stop.
from breakerbox import BudgetKilled
try:
app.invoke({...})
except BudgetKilled as e:
print(e) # spent, reason, side effectsEnvironment variables
| Variable | Purpose |
|---|---|
BREAKERBOX_INGEST_KEY | Auth for report_to — your personal key from the dashboard (Settings → Your ingest key), or a shared key. |
BREAKERBOX_CONTROL_URL | Override the live pause/kill control endpoint (otherwise derived from report_to). |
Metering
Tokens are self-metered with tiktoken, then reconciled against the provider's reported usage, so
the receipt reflects what actually happened rather than an estimate. Prices come from a built-in table;
use unknown_model="default_rate" for models it doesn't recognize.
OpenTelemetry export
Set otel=True to land receipts in the observability stack you already run (Datadog, Grafana,
Langfuse, …). Breakerbox emits one span per run (breakerbox.run) with a child span per
model hop (chat <model>) and tool hop (execute_tool <name>), tagged with the OTel GenAI
semantic conventions (gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens,
gen_ai.usage.output_tokens) plus a breakerbox.cost_usd attribute. You configure the exporter
the normal OTel way; breakerbox just emits the spans.
pip install 'breakerbox[otel]'from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) # → your collector
trace.set_tracer_provider(provider)
from breakerbox import guard
app = guard(my_app, budget_usd=5.00, otel=True) # spans now flow to your collectorNext: receipts · security model · FAQ.