Skip to main content

Module budget

Module budget 

Source
Expand description

The resource budget of the sandboxed autonomous mode (spec §11.1).

OrchestrationMode::SandboxedAutonomous is the one mode where the model drives writes nobody reviewed, and the ResourceBudget it carries is what keeps that from being open-ended. This module is the enforcement: it measures what a turn has spent and answers one question — may the turn keep going? — with the name of the bound that ran out when the answer is no.

§Three bounds, one rule

BoundMeasured asExhausted when
max_model_callsone ProviderAttempt per call the turn made, retries and fallbacks includedspent >= max
max_prompt_tokensthe prompt tokens the provider reported for every successful attemptspent >= max
max_wall_clockthe runtime clock, from the moment the turn was acceptedelapsed >= max

The comparison is >=, not >, and that is the fail-closed reading: a budget of eight calls means a turn may make eight, so a turn that has made eight has nothing left to spend and stops before asking for a ninth. An attempt that failed still counts — it cost the same call.

§Where it is checked, and what happens

Orchestrator::handle_turn checks the budget after interpretation and again before anything executes. Both are before any effect, so an exhausted budget fails the turn outright: BudgetLimit::into_error names the bound in a typed PolicyError::BudgetExhausted, nothing was journaled and nothing can have happened.

After the commit the answer is different, and deliberately so. The effects are real by then, and §23.1 says a turn that committed keeps its effects and regenerates its wording; failing it to save a token budget would throw away the only truthful account of what just happened. So a budget that runs out post-commit stops the model calls — narration and answers — and the turn is delivered with its receipts, its notices and an explicit budget_exhausted notice.

use std::time::Duration;
use turnframe_runtime::budget::{BudgetLimit, BudgetSpend, TurnBudget};
use turnframe_runtime::config::ResourceBudget;

let budget = ResourceBudget::conservative().with_max_model_calls(2);
let started = chrono::DateTime::UNIX_EPOCH;
let turn = TurnBudget::new(budget, started);

let spent = BudgetSpend::none().with_model_calls(2);
assert_eq!(turn.exhausted(spent, started), Some(BudgetLimit::ModelCalls));
assert_eq!(turn.exhausted(BudgetSpend::none(), started), None);

Structs§

BudgetSpend
What a turn has spent so far.
TurnBudget
One turn’s budget, measured against the clock it started on.

Enums§

BudgetLimit
Which bound of a ResourceBudget ran out.