agent_chain_core/messages/
function.rs

1//! Function message type.
2//!
3//! This module contains the `FunctionMessage` and `FunctionMessageChunk` types which represent
4//! messages for passing the result of executing a function back to a model.
5//! Mirrors `langchain_core.messages.function`.
6//!
7//! Note: FunctionMessage is an older version of ToolMessage and doesn't contain
8//! the `tool_call_id` field. Consider using ToolMessage for new code.
9
10use crate::utils::uuid7;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[cfg(feature = "specta")]
15use specta::Type;
16
17use super::base::merge_content;
18
19/// A function message in the conversation.
20///
21/// `FunctionMessage` objects are an older version of the `ToolMessage` schema, and
22/// do not contain the `tool_call_id` field.
23///
24/// The `tool_call_id` field is used to associate the tool call request with the
25/// tool call response. Useful in situations where a chat model is able
26/// to request multiple tool calls in parallel.
27///
28/// This corresponds to `FunctionMessage` in LangChain Python.
29#[cfg_attr(feature = "specta", derive(Type))]
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31pub struct FunctionMessage {
32    /// The message content (result of the function)
33    content: String,
34    /// The name of the function that was executed
35    name: String,
36    /// Optional unique identifier
37    id: Option<String>,
38    /// Additional metadata
39    #[serde(default)]
40    additional_kwargs: HashMap<String, serde_json::Value>,
41    /// Response metadata
42    #[serde(default)]
43    response_metadata: HashMap<String, serde_json::Value>,
44}
45
46impl FunctionMessage {
47    /// Create a new function message.
48    ///
49    /// # Arguments
50    ///
51    /// * `name` - The name of the function that was executed.
52    /// * `content` - The result of the function execution.
53    pub fn new(name: impl Into<String>, content: impl Into<String>) -> Self {
54        Self {
55            content: content.into(),
56            name: name.into(),
57            id: Some(uuid7(None).to_string()),
58            additional_kwargs: HashMap::new(),
59            response_metadata: HashMap::new(),
60        }
61    }
62
63    /// Create a new function message with an explicit ID.
64    ///
65    /// Use this when deserializing or reconstructing messages where the ID must be preserved.
66    pub fn with_id(
67        id: impl Into<String>,
68        name: impl Into<String>,
69        content: impl Into<String>,
70    ) -> Self {
71        Self {
72            content: content.into(),
73            name: name.into(),
74            id: Some(id.into()),
75            additional_kwargs: HashMap::new(),
76            response_metadata: HashMap::new(),
77        }
78    }
79
80    /// Get the message content.
81    pub fn content(&self) -> &str {
82        &self.content
83    }
84
85    /// Get the function name.
86    pub fn name(&self) -> &str {
87        &self.name
88    }
89
90    /// Get the message ID.
91    pub fn id(&self) -> Option<&str> {
92        self.id.as_deref()
93    }
94
95    /// Get additional kwargs.
96    pub fn additional_kwargs(&self) -> &HashMap<String, serde_json::Value> {
97        &self.additional_kwargs
98    }
99
100    /// Get response metadata.
101    pub fn response_metadata(&self) -> &HashMap<String, serde_json::Value> {
102        &self.response_metadata
103    }
104
105    /// Set response metadata.
106    pub fn with_response_metadata(
107        mut self,
108        response_metadata: HashMap<String, serde_json::Value>,
109    ) -> Self {
110        self.response_metadata = response_metadata;
111        self
112    }
113}
114
115/// Function message chunk (yielded when streaming).
116///
117/// This corresponds to `FunctionMessageChunk` in LangChain Python.
118#[cfg_attr(feature = "specta", derive(Type))]
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120pub struct FunctionMessageChunk {
121    /// The message content (may be partial during streaming)
122    content: String,
123    /// The name of the function that was executed
124    name: String,
125    /// Optional unique identifier
126    id: Option<String>,
127    /// Additional metadata
128    #[serde(default)]
129    additional_kwargs: HashMap<String, serde_json::Value>,
130    /// Response metadata
131    #[serde(default)]
132    response_metadata: HashMap<String, serde_json::Value>,
133}
134
135impl FunctionMessageChunk {
136    /// Create a new function message chunk.
137    pub fn new(name: impl Into<String>, content: impl Into<String>) -> Self {
138        Self {
139            content: content.into(),
140            name: name.into(),
141            id: None,
142            additional_kwargs: HashMap::new(),
143            response_metadata: HashMap::new(),
144        }
145    }
146
147    /// Create a new function message chunk with an ID.
148    pub fn with_id(
149        id: impl Into<String>,
150        name: impl Into<String>,
151        content: impl Into<String>,
152    ) -> Self {
153        Self {
154            content: content.into(),
155            name: name.into(),
156            id: Some(id.into()),
157            additional_kwargs: HashMap::new(),
158            response_metadata: HashMap::new(),
159        }
160    }
161
162    /// Get the message content.
163    pub fn content(&self) -> &str {
164        &self.content
165    }
166
167    /// Get the function name.
168    pub fn name(&self) -> &str {
169        &self.name
170    }
171
172    /// Get the message ID.
173    pub fn id(&self) -> Option<&str> {
174        self.id.as_deref()
175    }
176
177    /// Get additional kwargs.
178    pub fn additional_kwargs(&self) -> &HashMap<String, serde_json::Value> {
179        &self.additional_kwargs
180    }
181
182    /// Get response metadata.
183    pub fn response_metadata(&self) -> &HashMap<String, serde_json::Value> {
184        &self.response_metadata
185    }
186
187    /// Concatenate this chunk with another chunk.
188    ///
189    /// # Panics
190    ///
191    /// Panics if the function names are different.
192    pub fn concat(&self, other: &FunctionMessageChunk) -> FunctionMessageChunk {
193        if self.name != other.name {
194            panic!("Cannot concatenate FunctionMessageChunks with different names");
195        }
196
197        let content = merge_content(&self.content, &other.content);
198
199        // Merge additional_kwargs
200        let mut additional_kwargs = self.additional_kwargs.clone();
201        for (k, v) in &other.additional_kwargs {
202            additional_kwargs.insert(k.clone(), v.clone());
203        }
204
205        // Merge response_metadata
206        let mut response_metadata = self.response_metadata.clone();
207        for (k, v) in &other.response_metadata {
208            response_metadata.insert(k.clone(), v.clone());
209        }
210
211        FunctionMessageChunk {
212            content,
213            name: self.name.clone(),
214            id: self.id.clone().or_else(|| other.id.clone()),
215            additional_kwargs,
216            response_metadata,
217        }
218    }
219
220    /// Convert this chunk to a complete FunctionMessage.
221    pub fn to_message(&self) -> FunctionMessage {
222        FunctionMessage {
223            content: self.content.clone(),
224            name: self.name.clone(),
225            id: self.id.clone(),
226            additional_kwargs: self.additional_kwargs.clone(),
227            response_metadata: self.response_metadata.clone(),
228        }
229    }
230}
231
232impl std::ops::Add for FunctionMessageChunk {
233    type Output = FunctionMessageChunk;
234
235    fn add(self, other: FunctionMessageChunk) -> FunctionMessageChunk {
236        self.concat(&other)
237    }
238}