Skip to main content

Budgets

A budget caps what an agent may spend — dollars, tokens, model calls, tool calls, sandbox seconds — for one run, a session, the agent, or the whole application. By the end of this page you will have a run that stops before a model call it cannot afford, waits for a person to top it up, and carries on.
Every output on this page is what the code printed when it was run. Prices and the model’s wording will differ on your run.

A run that waits for money

The first call was priced at 0.001628—itsinput,plus300outputtokensat‘maxtokens‘—whichdidnotfitin0.001628 — its input, plus 300 output tokens at `max_tokens` — which did not fit in 0.0005. Nothing was spent and no call was made: not even the session’s call counter moved. Alice granted 0.01,therunresumed,andthecallreallycost0.01, the run resumed, and the call really cost 0.001321.

How it works

1

Every scope that covers the run is charged

A run charges its own request budget, its session’s, its agent’s, and the application’s. Any one of them running out stops the work, and the run says which.
2

A model call is held before it is made

The runtime counts the input it is about to send and prices the most the call could return: model_config["max_tokens"], or 4,096 tokens when none is set (sent to the provider as the call’s ceiling, so an answer that reaches it ends with termination_reason length). That amount is held, together with one model call. If it does not fit, the call is not made.
3

The real cost replaces the hold

When the call returns, the hold is settled at what it really cost and its tokens are counted. The trace records a budget_warning when a meter reaches warn_at (80%) of its limit, and budget_exhausted when it runs out.
4

At the wall: pause or end

With on_exhausted: "pause" (the default) the run returns status: "awaiting_budget" with a budget_request, keeping the work done so far. With "terminate" it ends with status: "error" and termination_reason: "budget_exhausted".
Counters live in the agent’s memory store, next to its run records, and are changed with compare-and-swap, so workers sharing a Redis, SQL or MongoDB store share a budget and two of them cannot both spend the last dollar. With the default in-memory store, a budget lasts as long as the process. A finished run’s own request counter is removed and its spend kept on the run’s record; session, agent and application counters stay.

Scopes, meters and windows

A window resets a counter: total (never), day (00:00 UTC) or month (the 1st, 00:00 UTC).
A call’s output is known only when it returns, so a call can cross a token limit: what it used is still counted in full, and the next call is stopped before it is made (its input would not fit). Leave a call’s worth of headroom if the limit must never be passed. (In 0.4.1 the call that crossed the limit was not counted, and resuming made it again.)
Every field, default and meter is in the budgets reference. Budgets can also live in a policy file (budgets: in the policy); set them in one place or the other, not both.

When a budget runs out

Pause, then grant or deny

budget_request says what stopped the run: Then, from this process or any other sharing the memory store:
  • await agent.grant_budget(run_id, approver="alice", amount=..., note=...) adds to that one budget. Without amount, the grant is exactly the shortfall: enough for the call that stopped, so the run may stop again at its next call. The policy is not changed; the grant is recorded with the approver’s name.
  • await agent.deny_budget(run_id, approver="alice", note=...) refuses it.
  • await agent.resume(run_id) then continues a granted run, or ends a denied one cleanly.
  • await agent.budget_status(run_id) lists every budget covering the run: limit, granted, spent, reserved and remaining.

End instead of waiting

Use terminate where no person is watching: batch jobs, evaluations, a background worker. A budget a person has already denied for this run is not asked about again: the run ends.

A model with no published price

A model behind base_url (vLLM, LM Studio, a gateway) usually has no published price. Its calls are made and recorded, but a model_cost_usd budget cannot govern them: it would never trip. Budget its calls and tokens instead. Here against a local OpenAI-compatible server (a small stand-in that gives the same answer to every question):
The third run stopped before its call. cost_complete: False says the 0.0 is not a real price, and the trace has a budget_cost_incomplete event for each such call.

Over HTTP

OmniServe has the same flow. This server (on port 8123, beside the stand-in model on 8000) allows one model call per session:
The first question in session demo was answered; the second returned "status": "awaiting_budget" with its run_id and budget_request:
GET /runs/{run_id}/budget returns budgets (as budget_status) and requests (each time the run ran out). Its budget entry and the request’s status:
A person grants (or sends "decision": "deny"), and the run is resumed:
The last line is the resumed run’s status and response. Leave out amount to grant the shortfall.

Budgets and the other limits

Budgets are not the only thing that stops a run; they are the only limit in dollars, and the only one across runs. More in which limit to use.

When things go wrong

A meter name that does not exist:
The window and on_exhausted are checked the same way:
An application budget needs to know which application it belongs to:
Add "application_id": "support-desk" beside the scopes.
grant_budget or deny_budget on a run that is not awaiting_budget — already granted, or never stopped — raises LookupError; over HTTP it is a 404:
The model has no published price, so model_cost_usd has nothing to count; cost_complete is False in the run’s totals. Budget model_calls and model_tokens instead (above).
A grant without amount covers only the call that stopped. Grant more headroom: amount=0.50.

Next

Budgets reference

Every meter, window, scope and field.

Approvals

Pausing for a person, for tools as well as money.

Policies

Budgets can live in a policy file, with its rules.

Sandboxes and execution

Where sandbox seconds are spent.

Durable runs

Resume a waiting run from another process, days later.

Security model

What is enforced, and where.