Skip to main content

AgentLoop

Struct AgentLoop 

Source
pub struct AgentLoop<P: Provider, C: ContextStrategy> { /* private fields */ }
Expand description

The agentic while loop: drives provider + tool + context interactions.

Generic over P: Provider (the LLM backend) and C: ContextStrategy (the compaction strategy). Hooks and durability are optional.

Implementations§

Source§

impl<P: Provider, C: ContextStrategy> AgentLoop<P, C>

Source

pub fn new( provider: P, tools: ToolRegistry, context: C, config: LoopConfig, ) -> Self

Create a new AgentLoop with the given provider, tools, context strategy, and configuration.

Source

pub fn add_hook<H: ObservabilityHook + 'static>(&mut self, hook: H) -> &mut Self

Add an observability hook to the loop.

Hooks are called in order of registration at each event point.

Source

pub fn set_durability<D: DurableContext + 'static>( &mut self, durable: D, ) -> &mut Self

Set the durable context for crash-recoverable execution.

When set, LLM calls and tool executions go through the durable context so they can be journaled, replayed, and recovered by engines like Temporal, Restate, or Inngest.

Source

pub fn config(&self) -> &LoopConfig

Returns a reference to the current configuration.

Source

pub fn messages(&self) -> &[Message]

Returns a reference to the current messages.

Source

pub fn tools_mut(&mut self) -> &mut ToolRegistry

Returns a mutable reference to the tool registry.

Source

pub async fn run( &mut self, user_message: Message, tool_ctx: &ToolContext, ) -> Result<AgentResult, LoopError>

Run the agentic loop to completion.

Appends the user message, then loops: call provider, execute tools if needed, append results, repeat until the model returns a text-only response or the turn limit is reached.

When durability is set, LLM calls go through DurableContext::execute_llm_call and tool calls go through DurableContext::execute_tool.

Fires HookEvent at each step. If a hook returns HookAction::Terminate, the loop stops with LoopError::HookTerminated.

§Errors

Returns LoopError::MaxTurns if the turn limit is exceeded, LoopError::Provider on provider failures, LoopError::Tool on tool execution failures, or LoopError::HookTerminated if a hook requests termination.

Source

pub async fn run_text( &mut self, text: &str, tool_ctx: &ToolContext, ) -> Result<AgentResult, LoopError>

Convenience method to run the loop with a plain text message.

Wraps text into a Message { role: User, content: [Text(text)] } and calls run.

Source

pub fn builder(provider: P, context: C) -> AgentLoopBuilder<P, C>

Create a builder with the required provider and context strategy.

All other options have sensible defaults:

  • Empty tool registry
  • Default loop config (no turn limit, empty system prompt)
  • No hooks or durability
Source§

impl<P: Provider, C: ContextStrategy> AgentLoop<P, C>

Source

pub fn run_step<'a>( &'a mut self, user_message: Message, tool_ctx: &'a ToolContext, ) -> StepIterator<'a, P, C>

Create a step-by-step iterator over the loop.

Unlike run which drives to completion, this lets you advance one turn at a time, inspect state, inject messages, and modify tools between turns.

The user message is appended immediately. Call StepIterator::next to advance.

Source

pub async fn run_stream( &mut self, user_message: Message, tool_ctx: &ToolContext, ) -> Receiver<StreamEvent>

Run the loop with streaming, forwarding StreamEvents through a channel.

Uses provider.complete_stream() instead of provider.complete() for each LLM turn. When durability is set, falls back to DurableContext::execute_llm_call (full response) and synthesizes stream events from the result.

Tool execution is handled identically to run. Fires the same hook events as run(): LoopIteration, PreLlmCall, PostLlmCall, PreToolExecution, PostToolExecution, and ContextCompaction.

Returns a receiver that yields StreamEvents. The final StreamEvent::MessageComplete on the last turn signals the loop has finished.

§Errors

Errors are sent as StreamEvent::Error on the channel.

Auto Trait Implementations§

§

impl<P, C> Freeze for AgentLoop<P, C>
where P: Freeze, C: Freeze,

§

impl<P, C> !RefUnwindSafe for AgentLoop<P, C>

§

impl<P, C> Send for AgentLoop<P, C>

§

impl<P, C> Sync for AgentLoop<P, C>

§

impl<P, C> Unpin for AgentLoop<P, C>
where P: Unpin, C: Unpin,

§

impl<P, C> UnsafeUnpin for AgentLoop<P, C>
where P: UnsafeUnpin, C: UnsafeUnpin,

§

impl<P, C> !UnwindSafe for AgentLoop<P, C>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WasmCompatSend for T
where T: Send,

Source§

impl<T> WasmCompatSync for T
where T: Sync,