Skip to main content

tiny_loop/tool/executor/
sequential.rs

1use std::collections::HashMap;
2
3use crate::{
4    tool::{Tool, executor::ToolExecutor},
5    types::ToolCall,
6};
7use async_trait::async_trait;
8
9/// Executes tools sequentially one by one by using [`Tool::call`]
10///
11/// # How it works
12///
13/// 1. Iterates through tool calls in order
14/// 2. Executes each call one at a time using [`Tool::call`]
15/// 3. Waits for each call to complete before starting the next
16///
17/// # Example
18///
19/// Given tool calls:
20/// ```text
21/// [
22///   ToolCall { id: "1", function: { name: "weather", ... } },
23///   ToolCall { id: "2", function: { name: "search", ... } },
24///   ToolCall { id: "3", function: { name: "weather", ... } },
25/// ]
26/// ```
27///
28/// The executor will:
29/// 1. Execute `weather_tool.call(call1)` and wait for completion
30/// 2. Execute `search_tool.call(call2)` and wait for completion
31/// 3. Execute `weather_tool.call(call3)` and wait for completion
32/// 4. Return results in order: `[result1, result2, result3]`
33pub struct SequentialExecutor {
34    tools: HashMap<String, Box<dyn Tool + Sync>>,
35}
36
37impl SequentialExecutor {
38    /// Create a new sequential executor
39    pub fn new() -> Self {
40        Self {
41            tools: HashMap::new(),
42        }
43    }
44}
45
46#[async_trait]
47impl ToolExecutor for SequentialExecutor {
48    fn add(&mut self, name: String, tool: Box<dyn Tool + Sync>) -> Option<Box<dyn Tool + Sync>> {
49        tracing::trace!("Registering tool: {}", name);
50        self.tools.insert(name, tool)
51    }
52
53    async fn execute(&self, calls: Vec<ToolCall>) -> Vec<crate::types::ToolResult> {
54        tracing::debug!("Executing {} tool calls sequentially", calls.len());
55        let mut results = Vec::new();
56        for call in calls {
57            tracing::debug!("Executing tool '{}'", call.function.name);
58            if let Some(tool) = self.tools.get(&call.function.name) {
59                results.push(tool.call_timed(call).await);
60            } else {
61                tracing::debug!("Tool '{}' not found", call.function.name);
62                results.push(super::tool_not_found_result(call.id, &call.function.name));
63            }
64        }
65        tracing::debug!("Sequential execution completed");
66        results
67    }
68}