Skip to main content

synaptic_graph/
tool_node.rs

1use async_trait::async_trait;
2use synaptic_core::{Message, SynapseError};
3use synaptic_tools::SerialToolExecutor;
4
5use crate::node::Node;
6use crate::state::MessageState;
7
8/// Prebuilt node that executes tool calls from the last AI message in state.
9pub struct ToolNode {
10    executor: SerialToolExecutor,
11}
12
13impl ToolNode {
14    pub fn new(executor: SerialToolExecutor) -> Self {
15        Self { executor }
16    }
17}
18
19#[async_trait]
20impl Node<MessageState> for ToolNode {
21    async fn process(&self, mut state: MessageState) -> Result<MessageState, SynapseError> {
22        let last = state
23            .last_message()
24            .ok_or_else(|| SynapseError::Graph("no messages in state".to_string()))?;
25
26        let tool_calls = last.tool_calls().to_vec();
27        if tool_calls.is_empty() {
28            return Ok(state);
29        }
30
31        for call in &tool_calls {
32            let result = self
33                .executor
34                .execute(&call.name, call.arguments.clone())
35                .await?;
36            state
37                .messages
38                .push(Message::tool(result.to_string(), &call.id));
39        }
40
41        Ok(state)
42    }
43}