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:
- Allocation: Reserve budget for the maximum expected cost before making an API call
- 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:
Budgetuses 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
impl Budget
Sourcepub 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
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-centsoutput_token_rate_micro_cents- Cost per output token in micro-centscache_creation_token_rate_micro_cents- Cost per cache creation token in micro-centscache_read_token_rate_micro_cents- Cost per cache read token in micro-cents
Sourcepub fn new_flat_rate(
budget_micro_cents: u64,
token_rate_micro_cents: u64,
) -> Self
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-centstoken_rate_micro_cents- Cost per token (applies to all token types)
Sourcepub 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
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.
Sourcepub fn from_dollars_flat_rate(
budget_dollars: f64,
token_rate_micro_cents: u64,
) -> Self
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());Sourcepub fn new(tokens: u32) -> Self
👎Deprecated: Use new_with_rates or new_flat_rate instead
pub fn new(tokens: u32) -> Self
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.
Sourcepub fn calculate_cost(&self, usage: &Usage) -> u64
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.
Sourcepub fn calculate_input_cost(&self, usage: &Usage) -> u64
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
Sourcepub fn calculate_output_cost(&self, usage: &Usage) -> u64
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
Sourcepub fn allocate(&self, max_tokens: u32) -> Option<BudgetAllocation<'_>>
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");
}Sourcepub fn remaining_micro_cents(&self) -> u64
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.
Sourcepub fn total_micro_cents(&self) -> u64
pub fn total_micro_cents(&self) -> u64
Returns the total micro-cents allocated to this budget.
Sourcepub fn consume_token(&self, kind: TokenKind, tokens: u64) -> bool
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.
Sourcepub fn consume_usage(&self, usage: &Usage) -> bool
pub fn consume_usage(&self, usage: &Usage) -> bool
Consumes budget based on an API usage record.
Sourcepub fn consume_usage_saturating(&self, usage: &Usage) -> u64
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.