pub struct ToolLoopConfig {
pub max_iterations: u32,
pub parallel_tool_execution: bool,
pub on_tool_call: Option<ToolApprovalFn>,
pub on_event: Option<ToolLoopEventFn>,
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: u32Maximum number of generate-execute iterations. Default: 10.
parallel_tool_execution: boolWhether 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.
on_event: Option<ToolLoopEventFn>Optional callback invoked during loop execution for observability.
Receives ToolLoopEvents at key points: iteration start,
tool execution start/end, and LLM response received.
stop_when: Option<StopConditionFn>Optional stop condition checked after each LLM response.
Receives a StopContext with information about the current
iteration and returns a StopDecision. Use this to implement:
final_answertool 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 >= nNone: 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()
};