Skip to main content

ToolLoopConfig

Struct ToolLoopConfig 

Source
pub struct ToolLoopConfig {
    pub max_iterations: u32,
    pub parallel_tool_execution: bool,
    pub on_tool_call: Option<ToolApprovalFn>,
    pub stop_when: Option<StopConditionFn>,
    pub loop_detection: Option<LoopDetectionConfig>,
    pub timeout: Option<Duration>,
    pub max_depth: Option<u32>,
}
Expand description

Configuration for tool_loop and tool_loop_stream.

Fields§

§max_iterations: u32

Maximum number of generate-execute iterations. Default: 10.

§parallel_tool_execution: bool

Whether to execute multiple tool calls in parallel. Default: true.

§on_tool_call: Option<ToolApprovalFn>

Optional callback to approve, deny, or modify each tool call before execution.

Called once per tool call in the LLM response, after the response is assembled but before any tool is executed. Receives the ToolCall as parsed from the LLM output. Modified arguments are re-validated against the tool’s schema.

Panics in the callback propagate and terminate the loop.

§stop_when: Option<StopConditionFn>

Optional stop condition checked after each LLM response.

Called after the LLM response is received but before tools are executed. If the callback returns StopDecision::Stop or StopDecision::StopWithReason, the loop terminates immediately without executing the requested tool calls.

Receives a StopContext with information about the current iteration and returns a StopDecision. Use this to implement:

  • final_answer tool patterns (stop when a specific tool is called)
  • Token budget enforcement
  • Total tool call limits
  • Content pattern matching

§Example

use llm_stack::tool::{ToolLoopConfig, StopDecision};
use std::sync::Arc;

let config = ToolLoopConfig {
    stop_when: Some(Arc::new(|ctx| {
        // Stop if we've executed 5 or more tool calls
        if ctx.tool_calls_executed >= 5 {
            StopDecision::StopWithReason("Tool call limit reached".into())
        } else {
            StopDecision::Continue
        }
    })),
    ..Default::default()
};
§loop_detection: Option<LoopDetectionConfig>

Optional loop detection to catch stuck agents.

When enabled, tracks consecutive identical tool calls (same name and arguments) and takes action when the threshold is reached.

§Example

use llm_stack::tool::{ToolLoopConfig, LoopDetectionConfig, LoopAction};

let config = ToolLoopConfig {
    loop_detection: Some(LoopDetectionConfig {
        threshold: 3,
        action: LoopAction::InjectWarning,
    }),
    ..Default::default()
};
§timeout: Option<Duration>

Maximum wall-clock time for the entire tool loop.

If exceeded, returns with TerminationReason::Timeout. This is useful for enforcing time budgets in production systems.

§Example

use llm_stack::tool::ToolLoopConfig;
use std::time::Duration;

let config = ToolLoopConfig {
    timeout: Some(Duration::from_secs(30)),
    ..Default::default()
};
§max_depth: Option<u32>

Maximum allowed nesting depth for recursive tool loops.

When a tool calls tool_loop internally (e.g., spawning a sub-agent), the depth is tracked via the context’s LoopDepth implementation. If ctx.loop_depth() >= max_depth at entry, returns Err(LlmError::MaxDepthExceeded).

  • Some(n): Error if depth >= n
  • None: No limit (dangerous, use with caution)

Default: Some(3) (allows master → worker → one more level)

§Example

use llm_stack::tool::ToolLoopConfig;

// Master/Worker pattern: master=0, worker=1, no grandchildren
let config = ToolLoopConfig {
    max_depth: Some(2),
    ..Default::default()
};

Trait Implementations§

Source§

impl Clone for ToolLoopConfig

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ToolLoopConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ToolLoopConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more