pub trait Loop: Send + Sync {
// Required methods
fn run<'a>(
&'a mut self,
input: &'a str,
run_config: &'a RunConfig,
) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>;
fn should_continue(&self) -> bool;
fn finalize<'a>(
&'a mut self,
error: Option<&'a LoopError>,
) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>;
fn state(&self) -> MachineState;
fn cancel(&self);
// Provided method
fn stop_reason(&self) -> Option<LoopError> { ... }
}Expand description
The core agent lifecycle trait.
Implement this trait to create a new type of agent. The framework provides shared infrastructure for context management, tool execution, reflection, and observability, so implementations only need to define the core processing logic.
§Example
use loopctl::engine::core::Loop;
use loopctl::engine::RunConfig;
use loopctl::engine::core::{MachineState, Run};
use loopctl::error::LoopError;
struct MyAgent {
state: MyState,
}
impl Loop for MyAgent {
fn run<'a>(
&'a mut self,
input: &'a str,
run_config: &'a RunConfig,
) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>> {
Box::pin(async { Ok(loopctl::engine::core::Run::new(input, &Default::default())) })
}
fn should_continue(&self) -> bool {
!self.state.is_complete
}
fn finalize<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>> {
Box::pin(async { Ok(loopctl::engine::core::Run::new("", &Default::default())) })
}
fn state(&self) -> MachineState {
MachineState::Start
}
fn cancel(&self) {}
}Required Methods§
Sourcefn run<'a>(
&'a mut self,
input: &'a str,
run_config: &'a RunConfig,
) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>
fn run<'a>( &'a mut self, input: &'a str, run_config: &'a RunConfig, ) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>
Drive one run of the agent loop for the given user prompt.
This is the main entry point for running an agent. Session-scoped state
(identity, start time, system prompt) is established once at
construction and stays stable across calls; each run() receives a
fresh RunConfig for the per-run budget and policy, mints a new
Run, and drives the turn loop until
the model finishes or the budget is exhausted.
input is the prompt for this run. It is passed to the first turn;
continuation turns receive an empty string (tool results are already in
the conversation history).
§Errors
LoopError::Cancelled— if the session was cancelled.LoopError::MaxTurnsExceeded— if the turn limit was reached.- Any error returned by the turn loop or
finalize.
Sourcefn should_continue(&self) -> bool
fn should_continue(&self) -> bool
Check whether the agent should continue processing turns.
Called after each turn. Return false to end the session.
Sourcefn finalize<'a>(
&'a mut self,
error: Option<&'a LoopError>,
) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>
fn finalize<'a>( &'a mut self, error: Option<&'a LoopError>, ) -> Pin<Box<dyn Future<Output = RunResult> + Send + 'a>>
Finalize the agent run and produce its result.
Called once after the last turn. Use this to clean up resources
and produce a final Run.
Sourcefn state(&self) -> MachineState
fn state(&self) -> MachineState
Get the current state of the agent.
Used by the framework to drive the state machine and by observers to report status.
Sourcefn cancel(&self)
fn cancel(&self)
Cancel the agent’s current operation.
Implementations must use thread-safe interior mutability (e.g.
AtomicBool, Mutex<bool>) to
store the cancellation flag, since this method takes &self. The
flag should be set in a non-blocking fashion so that
run and should_continue
can observe it and return promptly across threads.
Provided Methods§
Sourcefn stop_reason(&self) -> Option<LoopError>
fn stop_reason(&self) -> Option<LoopError>
Explain why should_continue returned false.
Return None for normal completion (the model finished) or Some(err)
when the session was forced to stop (Cancelled, MaxTurnsExceeded,
etc.). The default implementation returns None (normal completion).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".