mod types;
pub use types::*;
use std::time::{Duration, Instant};
use crate::error::{Result, TinyAgentsError};
impl RunLimits {
pub fn with_max_model_calls(mut self, n: usize) -> Self {
self.max_model_calls = n;
self
}
pub fn with_max_tool_calls(mut self, n: usize) -> Self {
self.max_tool_calls = n;
self
}
pub fn with_max_wall_clock_ms(mut self, ms: Option<u64>) -> Self {
self.max_wall_clock_ms = ms;
self
}
pub fn with_max_retries_per_call(mut self, n: usize) -> Self {
self.max_retries_per_call = n;
self
}
pub fn with_max_concurrency(mut self, n: Option<usize>) -> Self {
self.max_concurrency = n;
self
}
pub fn with_max_depth(mut self, n: usize) -> Self {
self.max_depth = n;
self
}
}
pub struct LimitTracker {
limits: RunLimits,
model_calls: usize,
tool_calls: usize,
started_at: Instant,
}
impl LimitTracker {
pub fn new(limits: RunLimits) -> Self {
Self {
limits,
model_calls: 0,
tool_calls: 0,
started_at: Instant::now(),
}
}
pub fn record_model_call(&mut self) -> Result<()> {
self.model_calls += 1;
if self.model_calls > self.limits.max_model_calls {
return Err(TinyAgentsError::Validation(format!(
"max model calls ({}) exceeded",
self.limits.max_model_calls
)));
}
Ok(())
}
pub fn record_tool_call(&mut self) -> Result<()> {
self.tool_calls += 1;
if self.tool_calls > self.limits.max_tool_calls {
return Err(TinyAgentsError::Validation(format!(
"max tool calls ({}) exceeded",
self.limits.max_tool_calls
)));
}
Ok(())
}
pub fn check_wall_clock(&self) -> Result<()> {
if let Some(max_ms) = self.limits.max_wall_clock_ms {
let elapsed_ms = self.started_at.elapsed().as_millis() as u64;
if elapsed_ms > max_ms {
return Err(TinyAgentsError::Validation(format!(
"wall-clock limit ({max_ms} ms) exceeded after {elapsed_ms} ms"
)));
}
}
Ok(())
}
pub fn elapsed(&self) -> Duration {
self.started_at.elapsed()
}
pub fn remaining_wall_clock(&self) -> Option<Duration> {
self.limits.max_wall_clock_ms.map(|max_ms| {
let max = Duration::from_millis(max_ms);
max.checked_sub(self.started_at.elapsed())
.unwrap_or(Duration::ZERO)
})
}
pub fn model_calls(&self) -> usize {
self.model_calls
}
pub fn tool_calls(&self) -> usize {
self.tool_calls
}
pub fn remaining_model_calls(&self) -> usize {
self.limits.max_model_calls.saturating_sub(self.model_calls)
}
pub fn limits(&self) -> &RunLimits {
&self.limits
}
}
#[cfg(test)]
mod test;