The ToolExecutor is responsible for taking tool calls from the LLM and managing their execution lifecycle. It handles input validation, parallel execution, and ensures that tools respect system-wide constraints like timeouts and cancellations.
The ToolExecutor
The ToolExecutor acts as the engine for tool execution. It doesn’t hold the tools themselves (that’s the ToolRegistry’s job), but rather provides the logic to run them.
pub struct ToolExecutor {
registry: Arc<ToolRegistry>,
interaction_registry: Arc<UserInteractionRegistry>,
permission_registry: Arc<PermissionRegistry>,
}
Batch Execution
One of the key features of Agent Air is the ability to execute multiple tools in parallel. If an LLM provider (like Anthropic or OpenAI) returns multiple tool calls in a single response, the ToolExecutor can run them concurrently.
The execute_batch Method
The execute_batch method takes a collection of tool calls and spawns them as separate Tokio tasks.
pub async fn execute_batch(
&self,
session_id: &str,
calls: Vec<ToolCall>,
cancellation_token: CancellationToken,
) -> ToolBatchResult {
let mut futures = Vec::new();
for call in calls {
// Find the tool in the registry
// Prepare ToolContext
// Spawn the task
futures.push(tokio::spawn(async move {
tool.execute(context, call.input).await
}));
}
// Wait for all tools to complete or be cancelled
let results = join_all(futures).await;
// Process and return results
}
Cancellation and Timeouts
Tools are expected to be “good citizens” and respect the CancellationToken provided in their ToolContext.
- User-Initiated Cancellation: If the user interrupts the agent (e.g., via a keyboard shortcut in the TUI), the
CancellationTokenis triggered. - Timeouts: The controller can enforce a global timeout for tool execution.
- Internal Cancellation: If one tool in a batch fails critically, the system may choose to cancel the remaining tools in that batch.
Implementation in Tools
Tools should check the token during long-running operations:
async fn execute(&self, context: ToolContext, input: Value) -> ToolResult {
for item in items {
if context.cancellation_token.is_cancelled() {
return ToolResult::error("Cancelled by user");
}
// ... perform work ...
}
}
Error Handling
The execution flow distinguishes between two types of errors:
- Tool-Level Errors: The tool ran but failed to achieve its goal (e.g., “File not found”). This is returned to the LLM as a result.
- System-Level Errors: The tool could not be found, the input failed schema validation, or the task panicked. These are handled by the
ToolExecutorand reported as execution failures.
