pub struct ToolLoopHandle<'a, Ctx: LoopDepth + Send + Sync + 'static> { /* private fields */ }Expand description
Caller-driven resumable tool loop.
Unlike tool_loop which runs autonomously, this struct
gives the caller control between each tool execution round. Call
next_turn() to advance the loop, inspect the result,
then consume the Yielded handle to control what happens next.
§No spawning required
This is a direct state machine — no background tasks, no channels. The
caller drives it by calling next_turn() which performs one iteration
(LLM call + tool execution) and returns.
§Lifecycle
- Create with
new() - Call
next_turn()to get the first result - If
Yielded, consume viaresume()/continue_loop()/ etc., then callnext_turn()again - Repeat until
CompletedorError - Optionally call
into_result()for aToolLoopResult
Implementations§
Source§impl<'a, Ctx: LoopDepth + Send + Sync + 'static> ToolLoopHandle<'a, Ctx>
impl<'a, Ctx: LoopDepth + Send + Sync + 'static> ToolLoopHandle<'a, Ctx>
Sourcepub fn new(
provider: &'a dyn DynProvider,
registry: &'a ToolRegistry<Ctx>,
params: ChatParams,
config: ToolLoopConfig,
ctx: &Ctx,
) -> Self
pub fn new( provider: &'a dyn DynProvider, registry: &'a ToolRegistry<Ctx>, params: ChatParams, config: ToolLoopConfig, ctx: &Ctx, ) -> Self
Create a new resumable tool loop.
Does not start execution — call next_turn() to
begin the first iteration.
§Depth Tracking
Same as tool_loop — if Ctx implements LoopDepth,
nested calls are tracked and max_depth is enforced. If the depth limit
is already exceeded, the first call to next_turn() returns Error.
Sourcepub async fn next_turn(&mut self) -> TurnResult<'a, '_, Ctx>
pub async fn next_turn(&mut self) -> TurnResult<'a, '_, Ctx>
Advance the loop and return the result of this turn.
Each call performs one iteration: LLM generation, tool execution (if applicable), and returns the result.
Returns a TurnResult that must be matched:
TurnResult::Yielded— tools ran, consume viaresume()/continue_loop()/inject_and_continue()/stop()to continueTurnResult::Completed— loop is done, read.responseTurnResult::Error— loop failed, read.error
After Completed or Error, all subsequent calls return the same
terminal result.
Sourcepub fn resume(&mut self, command: LoopCommand)
pub fn resume(&mut self, command: LoopCommand)
Tell the loop how to proceed before the next next_turn() call.
When using TurnResult::Yielded, prefer the convenience methods on
Yielded (continue_loop(), inject_and_continue(), stop()),
which consume the yielded handle and call this internally.
This method is useful when you need to set a command on the handle
directly — for example, when driving the handle from an external
event loop that receives the command asynchronously after the
Yielded has already been consumed.
Has no effect after Completed or Error.
Sourcepub fn messages(&self) -> &[ChatMessage]
pub fn messages(&self) -> &[ChatMessage]
Get a snapshot of the current conversation messages.
Useful for context window management or debugging.
Sourcepub fn messages_mut(&mut self) -> &mut Vec<ChatMessage>
pub fn messages_mut(&mut self) -> &mut Vec<ChatMessage>
Get a mutable reference to the conversation messages.
Allows direct manipulation of the message history between iterations (e.g., for context compaction/summarization).
Sourcepub fn total_usage(&self) -> &Usage
pub fn total_usage(&self) -> &Usage
Get the accumulated usage across all iterations so far.
Sourcepub fn iterations(&self) -> u32
pub fn iterations(&self) -> u32
Get the current iteration count.
Sourcepub fn is_finished(&self) -> bool
pub fn is_finished(&self) -> bool
Whether the loop has finished (returned Completed or Error).
Sourcepub fn drain_events(&mut self) -> Vec<LoopEvent>
pub fn drain_events(&mut self) -> Vec<LoopEvent>
Drain any remaining buffered LoopEvents.
Most callers should use the events field on Yielded, Completed,
or TurnError instead — those are pre-populated by next_turn().
This method exists for edge cases where events may accumulate between
turns (e.g., after calling resume() directly from an
external event loop).
Sourcepub fn into_result(self) -> ToolLoopResult
pub fn into_result(self) -> ToolLoopResult
Consume the handle and return a ToolLoopResult.
If the loop hasn’t completed yet, returns a result with current
iteration count and TerminationReason::Complete.
Sourcepub fn into_owned(
self,
provider: Arc<dyn DynProvider>,
registry: Arc<ToolRegistry<Ctx>>,
) -> OwnedToolLoopHandle<Ctx>
pub fn into_owned( self, provider: Arc<dyn DynProvider>, registry: Arc<ToolRegistry<Ctx>>, ) -> OwnedToolLoopHandle<Ctx>
Convert this borrowed handle into an owned handle.
The provider and registry must be provided as Arc since this
handle only holds references. The loop state (iterations, messages,
usage, etc.) is transferred as-is.