Skip to main content

Budget

Struct Budget 

Source
pub struct Budget { /* private fields */ }
Expand description

§Budget Management System

This module provides a thread-safe monetary budget management system designed for controlling Anthropic API usage costs. The system operates on precise micro-cent accounting to handle fractional pricing models accurately while avoiding floating-point arithmetic issues.

§Core Concepts

§Micro-cents Monetary System

All monetary values are represented in micro-cents - one-millionth of a cent (1/1,000,000 of a cent). This allows precise tracking of API costs that may be fractions of a cent:

  • 1 dollar = 100,000,000 micro-cents
  • 1 cent = 1,000,000 micro-cents
  • 0.001 cents = 1,000 micro-cents

This precision is essential for accurately tracking modern API pricing models where individual tokens may cost fractions of a cent.

§Allocation vs Consumption Model

The budget system uses a two-phase approach:

  1. Allocation: Reserve budget for the maximum expected cost before making an API call
  2. Consumption: Deduct the actual cost after receiving the API response

This prevents race conditions and ensures budget limits are never exceeded, even in concurrent scenarios where multiple API calls might be in flight simultaneously.

§Thread Safety

The budget system is designed for concurrent use:

  • Budget uses atomic operations for lock-free budget tracking
  • Multiple allocations can be created concurrently from the same budget
  • Unused allocated budget is automatically returned when [BudgetAllocation] is dropped
  • All operations are atomic and consistent across threads

§Example Usage

use std::sync::Arc;
use claudius::{Budget, Usage};

// Create a $5.00 budget with realistic Anthropic API rates
let budget = Arc::new(Budget::from_dollars_with_rates(
    5.0,   // $5.00 total budget
    300,   // ~$0.0003 per input token
    1500,  // ~$0.0015 per output token
    150,   // ~$0.00015 per cache creation token
    75,    // ~$0.000075 per cache read token
));

// Allocate budget for an API call expecting up to 1000 tokens
if let Some(mut allocation) = budget.allocate(1000) {
    println!("Allocated budget for up to {} tokens", allocation.remaining_tokens());

    // After making the API call, consume the actual usage
    let actual_usage = Usage::new(150, 75); // 150 input, 75 output tokens
    if allocation.consume_usage(&actual_usage) {
        println!("Successfully consumed budget for actual usage");
    }
    // Unused budget is automatically returned when allocation is dropped
} else {
    println!("Insufficient budget for this operation");
}

println!("Remaining budget: ${:.6}",
         budget.remaining_micro_cents() as f64 / 100_000_000.0);

§Concurrent Usage Example

use std::sync::Arc;
use std::thread;
use claudius::Budget;

let budget = Arc::new(Budget::from_dollars_flat_rate(1.0, 100));
let mut handles = vec![];

// Spawn multiple threads that try to allocate budget concurrently
for i in 0..10 {
    let budget_clone = Arc::clone(&budget);
    handles.push(thread::spawn(move || {
        if let Some(_allocation) = budget_clone.allocate(50) {
            println!("Thread {} successfully allocated budget", i);
            // allocation automatically returns unused budget when dropped
        } else {
            println!("Thread {} failed to allocate budget", i);
        }
    }));
}

for handle in handles {
    handle.join().unwrap();
}

§Error Handling and Recovery Example

use claudius::{Budget, Usage};

let budget = Budget::from_dollars_with_rates(2.0, 300, 1500, 150, 75);

// Function to handle API operations with proper error handling
fn make_api_call(
    budget: &Budget,
    expected_tokens: u32,
) -> Result<(), &'static str> {
    // Try to allocate budget
    let mut allocation = budget.allocate(expected_tokens)
        .ok_or("Insufficient budget for operation")?;

    // Simulate API call - in reality, you'd make the actual API request here
    let prompt_tokens = i32::try_from(expected_tokens / 2)
        .map_err(|_| "Token count too large for i32")?;
    let completion_tokens = i32::try_from(expected_tokens / 4)
        .map_err(|_| "Token count too large for i32")?;
    let actual_usage = Usage::new(prompt_tokens, completion_tokens);

    // Consume actual usage
    if allocation.consume_usage(&actual_usage) {
        println!("API call completed successfully");
        Ok(())
    } else {
        // This shouldn't happen if allocation was calculated correctly,
        // but defensive programming is good practice
        Err("Usage exceeded allocation - this indicates a bug")
    }
}

// Multiple API calls with error handling
for i in 1..=5 {
    match make_api_call(&budget, 100) {
        Ok(()) => println!("API call {} succeeded", i),
        Err(e) => {
            println!("API call {} failed: {}", i, e);
            break; // Stop making calls if budget is exhausted
        }
    }
}

println!("Final budget: ${:.6}",
         budget.remaining_micro_cents() as f64 / 100_000_000.0);

§Real-world Agent Example

use std::sync::Arc;
use claudius::{Budget, Usage};

// Simulate an AI agent that processes multiple tasks
struct AIAgent {
    budget: Arc<Budget>,
    name: String,
}

impl AIAgent {
    fn new(name: String, daily_budget_dollars: f64) -> Self {
        // Create budget with realistic Anthropic API rates
        let budget = Arc::new(Budget::from_dollars_with_rates(
            daily_budget_dollars,
            300,  // ~$0.0003 per input token
            1500, // ~$0.0015 per output token
            375,  // ~$0.000375 per cache creation token
            30,   // ~$0.00003 per cache read token
        ));

        Self { budget, name }
    }

    fn process_task(&self, task_complexity: u32) -> Result<String, String> {
        // Estimate tokens based on task complexity
        let estimated_tokens = task_complexity * 10;

        let mut allocation = self.budget.allocate(estimated_tokens)
            .ok_or_else(|| format!(
                "Agent {} insufficient budget for task (need {} tokens)",
                self.name, estimated_tokens
            ))?;

        // Simulate API call with actual usage
        let input_tokens = (estimated_tokens * 6) / 10;
        let output_tokens = (estimated_tokens * 3) / 10;
        let cache_read_tokens = estimated_tokens / 10;

        let input_i32 = i32::try_from(input_tokens)
            .map_err(|_| "Input token count too large for i32".to_string())?;
        let output_i32 = i32::try_from(output_tokens)
            .map_err(|_| "Output token count too large for i32".to_string())?;
        let cache_i32 = i32::try_from(cache_read_tokens)
            .map_err(|_| "Cache read token count too large for i32".to_string())?;

        let usage = Usage::new(input_i32, output_i32)
            .with_cache_read_input_tokens(cache_i32);

        if allocation.consume_usage(&usage) {
            Ok(format!("Task completed by {} using {} total tokens",
                       self.name, input_tokens + output_tokens + cache_read_tokens))
        } else {
            Err("Usage calculation error".to_string())
        }
    }

    fn remaining_budget_dollars(&self) -> f64 {
        self.budget.remaining_micro_cents() as f64 / 100_000_000.0
    }
}

// Usage example
let agent = AIAgent::new("DataAnalyzer".to_string(), 50.0); // $50 daily budget

let tasks = vec![5, 10, 15, 8, 12]; // Task complexity scores
for (i, &complexity) in tasks.iter().enumerate() {
    match agent.process_task(complexity) {
        Ok(result) => {
            println!("Task {}: {}", i + 1, result);
            println!("  Remaining budget: ${:.2}", agent.remaining_budget_dollars());
        }
        Err(error) => {
            println!("Task {}: Failed - {}", i + 1, error);
            break;
        }
    }
}

Monetary budget manager for controlling API usage costs.

The Budget struct provides thread-safe, atomic budget allocation and tracking for Anthropic API operations. It uses a micro-cent precision monetary system to accurately track costs without floating-point arithmetic issues.

§Micro-cents Precision

All monetary amounts are stored as micro-cents (1/1,000,000 of a cent) to provide precise cost tracking for API operations where individual tokens may cost fractions of a cent. This eliminates floating-point rounding errors that could accumulate over many API calls.

§Token Rate Model

The budget supports different rates for different types of tokens:

  • Input tokens: The base cost for processing input text
  • Output tokens: The cost for generating response text
  • Cache creation tokens: The cost for creating prompt caches
  • Cache read tokens: The reduced cost for reading from prompt caches

§Thread Safety

Budget is designed for concurrent access across multiple threads or async tasks. All budget operations use atomic operations to ensure consistency without locks. Multiple [BudgetAllocation]s can be created concurrently, and unused budget is automatically returned when allocations are dropped.

§Example

use claudius::{Budget, Usage};

// Create a budget with $10 and realistic token rates
let budget = Budget::from_dollars_with_rates(
    10.0, // $10 budget
    300,  // 300 micro-cents per input token
    1500, // 1500 micro-cents per output token
    150,  // 150 micro-cents per cache creation token
    75,   // 75 micro-cents per cache read token
);

// Allocate budget for an operation expecting up to 500 tokens
if let Some(mut allocation) = budget.allocate(500) {
    // Simulate API usage
    let usage = Usage::new(100, 50); // 100 input, 50 output tokens

    if allocation.consume_usage(&usage) {
        println!("Operation completed within budget");
    }
}

Implementations§

Source§

impl Budget

Source

pub fn new_with_rates( budget_micro_cents: u64, input_token_rate_micro_cents: u64, output_token_rate_micro_cents: u64, cache_creation_token_rate_micro_cents: u64, cache_read_token_rate_micro_cents: u64, ) -> Self

Creates a new budget with the specified monetary amount in micro-cents and token rates.

§Arguments
  • budget_micro_cents - Total budget in micro-cents (1/1,000,000 of a cent)
  • input_token_rate_micro_cents - Cost per input token in micro-cents
  • output_token_rate_micro_cents - Cost per output token in micro-cents
  • cache_creation_token_rate_micro_cents - Cost per cache creation token in micro-cents
  • cache_read_token_rate_micro_cents - Cost per cache read token in micro-cents
Source

pub fn new_flat_rate( budget_micro_cents: u64, token_rate_micro_cents: u64, ) -> Self

Creates a new budget with a simplified flat rate per token.

§Arguments
  • budget_micro_cents - Total budget in micro-cents
  • token_rate_micro_cents - Cost per token (applies to all token types)
Source

pub fn from_dollars_with_rates( budget_dollars: f64, input_token_rate_micro_cents: u64, output_token_rate_micro_cents: u64, cache_creation_token_rate_micro_cents: u64, cache_read_token_rate_micro_cents: u64, ) -> Self

Creates a budget from dollars with specified rates per token in micro-cents.

Source

pub fn from_dollars_flat_rate( budget_dollars: f64, token_rate_micro_cents: u64, ) -> Self

Creates a budget from dollars with a flat rate per token.

§Example
// Create a $10 budget where each token costs 500 micro-cents
let budget = Budget::from_dollars_flat_rate(10.0, 500);

// This budget can handle up to 20,000,000 tokens (10 * 100,000,000 / 500)
assert!(budget.allocate(1000).is_some());
Source

pub fn new(tokens: u32) -> Self

👎Deprecated:

Use new_with_rates or new_flat_rate instead

Legacy constructor for backward compatibility - creates a token-based budget. This converts tokens to micro-cents using a default rate.

Source

pub fn calculate_cost(&self, usage: &Usage) -> u64

Calculates the total cost in micro-cents for a specific token usage pattern.

This method computes the precise cost by applying the budget’s token rates to each type of token usage. The calculation includes:

  • Input tokens × input token rate
  • Output tokens × output token rate
  • Cache creation tokens × cache creation rate
  • Cache read tokens × cache read rate
§Arguments
  • usage - The token usage to calculate costs for
§Returns

Total cost in micro-cents as a u64

§Example
use claudius::{Budget, Usage};

let budget = Budget::new_with_rates(
    1_000_000, // 1M micro-cents budget
    300,       // 300 micro-cents per input token
    1500,      // 1500 micro-cents per output token
    375,       // 375 micro-cents per cache creation token
    30,        // 30 micro-cents per cache read token
);

let usage = Usage::new(100, 50) // 100 input, 50 output tokens
    .with_cache_creation_input_tokens(20)
    .with_cache_read_input_tokens(10);

let cost = budget.calculate_cost(&usage);
// Cost = (100 × 300) + (50 × 1500) + (20 × 375) + (10 × 30)
//      = 30,000 + 75,000 + 7,500 + 300 = 112,800 micro-cents
assert_eq!(cost, 112_800);
§Overflow Safety

This method performs arithmetic that could theoretically overflow with extreme values, but overflow would require unrealistic combinations such as billions of tokens with extremely high rates. All practical API usage scenarios are well within safe bounds.

Source

pub fn calculate_input_cost(&self, usage: &Usage) -> u64

Calculates the cost in micro-cents for only input tokens in a usage record.

This includes input tokens, cache creation tokens, and cache read tokens. Output tokens are excluded.

§Returns

Input cost in micro-cents as a u64

Source

pub fn calculate_output_cost(&self, usage: &Usage) -> u64

Calculates the cost in micro-cents for only output tokens in a usage record.

§Returns

Output cost in micro-cents as a u64

Source

pub fn allocate(&self, max_tokens: u32) -> Option<BudgetAllocation<'_>>

Attempts to allocate cost for the expected maximum tokens from the budget.

Returns Some(BudgetAllocation) if sufficient budget is available, or None if the budget is insufficient.

§Example
let budget = Budget::from_dollars_flat_rate(1.0, 100);  // $1 budget, 100 micro-cents per token

if let Some(allocation) = budget.allocate(50) {
    println!("Successfully allocated budget for up to 50 tokens");
    // Use allocation for API call...
} else {
    println!("Insufficient budget for 50 tokens");
}
Source

pub fn remaining_micro_cents(&self) -> u64

Returns the current remaining budget in micro-cents.

This method provides a snapshot of the budget’s current state. Note that in concurrent scenarios, the value may change between the time you read it and when you use it for decisions.

§Returns

Current remaining budget as u64 micro-cents

§Example
use claudius::Budget;

let budget = Budget::from_dollars_flat_rate(5.0, 1000);
assert_eq!(budget.remaining_micro_cents(), 500_000_000); // $5.00

// After some allocations, the remaining amount decreases
let _allocation = budget.allocate(100); // Reserves 100 * 1000 = 100,000 micro-cents
assert_eq!(budget.remaining_micro_cents(), 499_900_000);
§Thread Safety

This method is thread-safe and uses atomic loads. The returned value represents a consistent point-in-time snapshot of the budget state.

Source

pub fn total_micro_cents(&self) -> u64

Returns the total micro-cents allocated to this budget.

Source

pub fn consume_token(&self, kind: TokenKind, tokens: u64) -> bool

Consumes a token count for a specific token category.

Returns true if the budget was sufficient and the tokens were consumed.

Source

pub fn consume_usage(&self, usage: &Usage) -> bool

Consumes budget based on an API usage record.

Source

pub fn consume_usage_saturating(&self, usage: &Usage) -> u64

Consumes budget based on an API usage record, saturating at zero.

Returns the amount consumed in micro-cents.

Source

pub fn remaining(&self) -> Arc<AtomicU64>

👎Deprecated:

Use remaining_micro_cents() instead

Legacy field access for backward compatibility.

Trait Implementations§

Source§

impl Clone for Budget

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Budget

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more