use super::AgentError;
use crate::metrics::AgentMetrics;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Default)]
pub struct BudgetConfig {
pub max_tool_calls: Option<usize>,
pub max_tokens: Option<usize>,
pub max_duration: Option<Duration>,
pub max_iterations: Option<usize>,
}
#[derive(Debug, Clone)]
pub enum BudgetExceeded {
ToolCalls {
limit: usize,
actual: usize,
},
Tokens {
limit: usize,
actual: usize,
},
Duration {
limit: Duration,
elapsed: Duration,
},
Iterations {
limit: usize,
},
}
pub(crate) fn budget_iteration_gate(
budget: Option<&BudgetConfig>,
max_iterations: usize,
iteration: usize,
loop_start: Instant,
) -> Option<AgentError> {
let budget = budget?;
if let Some(limit) = budget.max_iterations {
let effective = limit.min(max_iterations);
if iteration >= effective {
return Some(AgentError::BudgetExceeded(BudgetExceeded::Iterations {
limit: effective,
}));
}
}
if let Some(limit) = budget.max_duration {
let elapsed = loop_start.elapsed();
if elapsed >= limit {
return Some(AgentError::BudgetExceeded(BudgetExceeded::Duration {
limit,
elapsed,
}));
}
}
None
}
pub(crate) fn budget_token_gate(
budget: Option<&BudgetConfig>,
metrics: &AgentMetrics,
) -> Option<AgentError> {
let budget = budget?;
let limit = budget.max_tokens?;
let actual = metrics.total_tokens.unwrap_or(0);
if actual >= limit {
return Some(AgentError::BudgetExceeded(BudgetExceeded::Tokens {
limit,
actual,
}));
}
None
}
pub(crate) fn budget_tool_gate(
budget: Option<&BudgetConfig>,
metrics: &AgentMetrics,
loop_start: Instant,
) -> Option<AgentError> {
let budget = budget?;
if let Some(limit) = budget.max_tool_calls {
if metrics.tool_calls > limit {
return Some(AgentError::BudgetExceeded(BudgetExceeded::ToolCalls {
limit,
actual: metrics.tool_calls,
}));
}
}
if let Some(limit) = budget.max_duration {
let elapsed = loop_start.elapsed();
if elapsed >= limit {
return Some(AgentError::BudgetExceeded(BudgetExceeded::Duration {
limit,
elapsed,
}));
}
}
None
}