Breakerbox

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

ParameterTypeDefaultWhat it does
appcompiled LangGraph appThe app to guard.
budget_usdfloatrequiredHard dollar cap for the whole run — hierarchical across sub-agents.
max_hopsint100Trips if the run exceeds this many hops (runaway-loop guard).
ttl_secondsint | NoneNoneWall-clock limit; trips if the run runs longer.
velocity_usd_per_minfloat | NoneNoneSpend-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_budgetsdict[str, float] | NoneNonePer-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_dirstr | Path"./breakerbox_reports"Where the receipt (terminal summary + single-file HTML + JSON) is written.
report_tostr | NoneNoneA cloud ingest URL to stream events to (best-effort, non-blocking).
otelboolFalseEmit an OpenTelemetry span per run + per hop (GenAI semantic conventions). Needs pip install 'breakerbox[otel]'.
detect_loopsbool | dictFalseSemantic 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.
livebool | CallableFalseReal-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_depthint | NoneNoneCap 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.
alertsbool | dict | CallableFalseGraduated 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.
tagsdict | NoneNoneCost-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 (see topup_policy).
  • on_trip="kill" — raises BudgetKilled; 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 effects

Environment variables

VariablePurpose
BREAKERBOX_INGEST_KEYAuth for report_to — your personal key from the dashboard (Settings → Your ingest key), or a shared key.
BREAKERBOX_CONTROL_URLOverride 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 collector

Next: receipts · security model · FAQ.

On this page