Skip to main content

Per-task budget, loop detection, and kill-switch middleware for agent LLM calls. v0, deterministic and dependency-free.

Project description

BudgetGuard

Per-task budget, loop detection, and kill-switch middleware for agent LLM calls. Deterministic, dependency-free, fail-closed. v0.

An agent with a payment credential and a vague instruction is a budget incident waiting to happen. BudgetGuard sits between your agent and its model calls and refuses the next call the moment it would cross a limit you set, before the spend happens, not after.

It is one of three small primitives from Major Labs:

MandateKit says what an agent may do. BudgetGuard caps what it spends. WitnessKit proves what it did.


What it does

Three controls, all enforced before the call runs:

  • Budgets — cap a task by USD, total tokens, input/output tokens, or call count. The next call is refused if it would cross the cap.
  • Loop detection — catch runaway agents that repeat the same call. If one signature repeats past a threshold within a sliding window, the call is denied.
  • Kill switch — halt a single task, or everything, immediately.

BudgetGuard never makes the model call itself. You ask it whether the next call is allowed (check), make the call, then tell it what actually happened (record).


Install

pip install budget-guard-agents   # Python 3.8+
npm install budget-guard-agents   # Node 22.6+

Token and call budgets work with zero configuration. USD budgets need a pricing table (see below).


Quickstart (Python)

from budgetguard import BudgetGuard, BudgetPolicy, Pricing

guard = BudgetGuard(pricing=Pricing())  # pricing only needed for USD budgets

with guard.task("research-job", BudgetPolicy(max_usd=0.50, max_calls=20, max_repeats=3)):
    sig = "search(query='...')"
    # BEFORE the model call: raises BudgetExceeded / LoopDetected / KillSwitched
    guard.check("research-job", model="claude-sonnet-4-6",
                est_input_tokens=1200, est_output_tokens=600, signature=sig)

    response = call_your_model(...)   # you make the call

    # AFTER: record the real usage
    guard.record("research-job", model="claude-sonnet-4-6",
                 input_tokens=response.usage.input_tokens,
                 output_tokens=response.usage.output_tokens, signature=sig)

Prefer not to use exceptions? guard.check(..., enforce=False) returns a Decision(allowed=False, reason=...) instead of raising.

Quickstart (TypeScript)

import { BudgetGuard, Pricing } from "budget-guard";

const guard = new BudgetGuard(new Pricing());
guard.open("research-job", { maxUsd: 0.5, maxCalls: 20, maxRepeats: 3 });

guard.check("research-job", { model: "claude-sonnet-4-6", estInputTokens: 1200, estOutputTokens: 600, signature: sig });
const res = await callYourModel();
guard.record("research-job", { model: "claude-sonnet-4-6", inputTokens: res.usage.input, outputTokens: res.usage.output, signature: sig });
guard.close("research-job");

Run the demo: python3 demo.py (Python) or npm run demo (TypeScript).


Pricing

USD budgets need to convert tokens to dollars. The built-in price table is illustrative and will drift — do not trust it for billing. Supply your own verified prices (per 1,000 tokens):

from budgetguard import Pricing, ModelPrice
pricing = Pricing({"my-model": ModelPrice(input_per_1k=0.003, output_per_1k=0.015)})

If you only use token or call budgets, you do not need pricing at all.


Honest limitations (v0)

  • Concurrency is check-then-act. check and record are individually safe, but the model call happens between them. Two calls running concurrently under the same task can both pass check before either records, and overshoot the cap. For now, run one guarded call per task at a time, or treat the cap as a soft ceiling under concurrency. A reserve/commit API is planned.
  • USD enforcement carries float drift. Costs are floating point; the cap may be honored to within a fraction of a cent, not exactly.
  • Loop detection is signature-based. It only catches loops you give it a stable signature for (e.g. a hash of the prompt and tool arguments). It does not infer loops on its own.
  • In-memory only. State lives in the process. A kill switch or ledger does not survive a restart and is not shared across machines. A pluggable store is planned.

License

MIT. Built by Major Labs · github.com/major-matters

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

budget_guard_agents-0.0.2.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

budget_guard_agents-0.0.2-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file budget_guard_agents-0.0.2.tar.gz.

File metadata

  • Download URL: budget_guard_agents-0.0.2.tar.gz
  • Upload date:
  • Size: 11.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for budget_guard_agents-0.0.2.tar.gz
Algorithm Hash digest
SHA256 aee45f746298922c72d3726235ad699beb6aff830499a226714f0b22de9af1e5
MD5 f49e73e8c993611fcc7a1983fec27164
BLAKE2b-256 1615dae43efbde62e0a14c3aa1276afcbab2c0920b5834a61fda3f0565780ecd

See more details on using hashes here.

File details

Details for the file budget_guard_agents-0.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for budget_guard_agents-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8ac43b3cf1b9edbaee642bfb93f24d555d33fa056a6bd934c4d19788456f9fe0
MD5 79b7798ba8af18b3bc87925be218b513
BLAKE2b-256 8b106f1f0adf0f46ad2ad6d52c99a8fb2a828ddaad1a2c5c013f33043d295613

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page