use crate::callable::{Callable, DynCallable};
use crate::graph::CheckpointStore;
use crate::policy::LongRunningExecutionPolicy;
use crate::runner::Runner;
use crate::streaming::StreamEvent;
use std::time::{Duration, Instant};
#[allow(dead_code)]
pub struct AgenticLoop;
#[allow(dead_code)]
impl AgenticLoop {
pub async fn run<S: CheckpointStore>(
runner: &mut Runner<S>,
callable: DynCallable,
input: String,
policy: LongRunningExecutionPolicy,
) -> anyhow::Result<String> {
let start_time = Instant::now();
let mut steps_executed = 0;
let mut _tokens_used = 0;
let mut history: Vec<String> = Vec::new();
history.push(format!("User: {}", input));
let current_input = input.clone();
let current_callable = callable;
loop {
if let Some(max_steps) = policy.max_discovered_steps {
if steps_executed > max_steps {
runner.emitter().emit(StreamEvent::execution_failed(
runner.execution_id(),
crate::kernel::ExecutionError::quota_exceeded("Max steps exceeded"),
));
anyhow::bail!("Max steps exceeded");
}
}
if let Some(timeout) = policy.idle_timeout_seconds {
if start_time.elapsed() > Duration::from_secs(timeout) {
runner.emitter().emit(StreamEvent::execution_failed(
runner.execution_id(),
crate::kernel::ExecutionError::timeout(format!(
"Idle timeout after {}s",
timeout
)),
));
anyhow::bail!("Idle timeout");
}
}
let result = runner
.run_callable(current_callable.as_ref() as &dyn Callable, ¤t_input)
.await;
match result {
Ok(output) => {
history.push(format!("Assistant: {}", output));
steps_executed += 1;
if policy.checkpointing.on_discovery
|| policy
.checkpointing
.interval_steps
.map_or(false, |i| steps_executed % i == 0)
{
let state = crate::graph::NodeState::from_str(&output);
if let Err(e) = runner
.save_checkpoint(state, Some(current_callable.name()))
.await
{
eprintln!("Failed to save checkpoint: {}", e);
}
}
return Ok(output);
}
Err(e) => return Err(e),
}
}
}
}