use std::sync::Arc;
use super::budget::RolloutBudget;
use super::config::ControlConfig;
use super::limiter::AgentExecutionLimiter;
pub struct AgentControl {
budget: Arc<RolloutBudget>,
limiter: Arc<AgentExecutionLimiter>,
}
impl std::fmt::Debug for AgentControl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AgentControl")
.field("budget", &self.budget)
.field("limiter", &self.limiter)
.finish()
}
}
impl AgentControl {
pub fn new(config: &ControlConfig) -> Self {
Self {
budget: Arc::new(RolloutBudget::new(
config.child_max_tokens,
config.max_spawns,
)),
limiter: Arc::new(match config.max_concurrency {
Some(max) => AgentExecutionLimiter::new(max),
None => AgentExecutionLimiter::unlimited(),
}),
}
}
pub fn budget(&self) -> &Arc<RolloutBudget> {
&self.budget
}
pub fn limiter(&self) -> &Arc<AgentExecutionLimiter> {
&self.limiter
}
pub fn status(&self) -> ControlStatus {
ControlStatus {
used_tokens: self.budget.used_tokens(),
child_max_tokens: self.budget.child_max_tokens(),
spawn_count: self.budget.spawn_count(),
max_spawns: self.budget.max_spawns(),
live_children: self.limiter.current(),
max_concurrency: self.limiter.max_concurrency(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ControlStatus {
pub used_tokens: u64,
pub child_max_tokens: u64,
pub spawn_count: usize,
pub max_spawns: usize,
pub live_children: usize,
pub max_concurrency: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_none_config_yields_observable_unlimited_gates() {
let c = AgentControl::new(&ControlConfig::default());
let slot = c.limiter().try_acquire().expect("unlimited");
let ticket = c.budget().try_reserve_spawn().expect("unlimited");
assert_eq!(
c.status(),
ControlStatus {
used_tokens: 0,
child_max_tokens: u64::MAX,
spawn_count: 1,
max_spawns: usize::MAX,
live_children: 1,
max_concurrency: usize::MAX,
}
);
ticket.commit();
drop(slot);
assert_eq!(c.status().live_children, 0);
}
#[test]
fn config_knobs_reach_the_gates() {
let cfg = ControlConfig {
child_max_tokens: Some(500),
max_spawns: Some(3),
max_concurrency: Some(2),
..Default::default()
};
let c = AgentControl::new(&cfg);
let s = c.status();
assert_eq!(
(s.child_max_tokens, s.max_spawns, s.max_concurrency),
(500, 3, 2)
);
}
}