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::ToolMessage> {
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            let message = if let Some(tool) = self.tools.get(&call.function.name) {
59                crate::types::ToolMessage {
60                    tool_call_id: call.id.clone(),
61                    content: tool.call(call.function.arguments).await,
62                }
63            } else {
64                tracing::debug!("Tool '{}' not found", call.function.name);
65                crate::types::ToolMessage {
66                    tool_call_id: call.id,
67                    content: format!("Tool '{}' not found", call.function.name),
68                }
69            };
70            results.push(message);
71        }
72        tracing::debug!("Sequential execution completed");
73        results
74    }
75}