agent_chain_core/messages/
human.rs1use crate::utils::uuid7;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[cfg(feature = "specta")]
11use specta::Type;
12
13use super::base::merge_content;
14use super::content::{ContentPart, ImageSource, MessageContent};
15
16#[cfg_attr(feature = "specta", derive(Type))]
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
25pub struct HumanMessage {
26 content: MessageContent,
28 id: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 name: Option<String>,
33 #[serde(default)]
35 additional_kwargs: HashMap<String, serde_json::Value>,
36}
37
38impl HumanMessage {
39 pub fn new(content: impl Into<String>) -> Self {
41 Self {
42 content: MessageContent::Text(content.into()),
43 id: Some(uuid7(None).to_string()),
44 name: None,
45 additional_kwargs: HashMap::new(),
46 }
47 }
48
49 pub fn with_id(id: impl Into<String>, content: impl Into<String>) -> Self {
53 Self {
54 content: MessageContent::Text(content.into()),
55 id: Some(id.into()),
56 name: None,
57 additional_kwargs: HashMap::new(),
58 }
59 }
60
61 pub fn with_content(parts: Vec<ContentPart>) -> Self {
79 Self {
80 content: MessageContent::Parts(parts),
81 id: Some(uuid7(None).to_string()),
82 name: None,
83 additional_kwargs: HashMap::new(),
84 }
85 }
86
87 pub fn with_id_and_content(id: impl Into<String>, parts: Vec<ContentPart>) -> Self {
91 Self {
92 content: MessageContent::Parts(parts),
93 id: Some(id.into()),
94 name: None,
95 additional_kwargs: HashMap::new(),
96 }
97 }
98
99 pub fn with_image_url(text: impl Into<String>, url: impl Into<String>) -> Self {
101 Self::with_content(vec![
102 ContentPart::Text { text: text.into() },
103 ContentPart::Image {
104 source: ImageSource::Url { url: url.into() },
105 detail: None,
106 },
107 ])
108 }
109
110 pub fn with_image_base64(
112 text: impl Into<String>,
113 media_type: impl Into<String>,
114 data: impl Into<String>,
115 ) -> Self {
116 Self::with_content(vec![
117 ContentPart::Text { text: text.into() },
118 ContentPart::Image {
119 source: ImageSource::Base64 {
120 media_type: media_type.into(),
121 data: data.into(),
122 },
123 detail: None,
124 },
125 ])
126 }
127
128 pub fn with_name(mut self, name: impl Into<String>) -> Self {
130 self.name = Some(name.into());
131 self
132 }
133
134 pub fn content(&self) -> &str {
139 match &self.content {
140 MessageContent::Text(s) => s,
141 MessageContent::Parts(_) => "",
142 }
143 }
144
145 pub fn message_content(&self) -> &MessageContent {
147 &self.content
148 }
149
150 pub fn has_images(&self) -> bool {
152 self.content.has_images()
153 }
154
155 pub fn id(&self) -> Option<&str> {
157 self.id.as_deref()
158 }
159
160 pub fn name(&self) -> Option<&str> {
162 self.name.as_deref()
163 }
164
165 pub fn additional_kwargs(&self) -> &HashMap<String, serde_json::Value> {
167 &self.additional_kwargs
168 }
169}
170
171#[cfg_attr(feature = "specta", derive(Type))]
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
176pub struct HumanMessageChunk {
177 content: MessageContent,
179 id: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 name: Option<String>,
184 #[serde(default)]
186 additional_kwargs: HashMap<String, serde_json::Value>,
187 #[serde(default)]
189 response_metadata: HashMap<String, serde_json::Value>,
190}
191
192impl HumanMessageChunk {
193 pub fn new(content: impl Into<String>) -> Self {
195 Self {
196 content: MessageContent::Text(content.into()),
197 id: None,
198 name: None,
199 additional_kwargs: HashMap::new(),
200 response_metadata: HashMap::new(),
201 }
202 }
203
204 pub fn with_id(id: impl Into<String>, content: impl Into<String>) -> Self {
206 Self {
207 content: MessageContent::Text(content.into()),
208 id: Some(id.into()),
209 name: None,
210 additional_kwargs: HashMap::new(),
211 response_metadata: HashMap::new(),
212 }
213 }
214
215 pub fn content(&self) -> &str {
217 match &self.content {
218 MessageContent::Text(s) => s,
219 MessageContent::Parts(_) => "",
220 }
221 }
222
223 pub fn message_content(&self) -> &MessageContent {
225 &self.content
226 }
227
228 pub fn id(&self) -> Option<&str> {
230 self.id.as_deref()
231 }
232
233 pub fn name(&self) -> Option<&str> {
235 self.name.as_deref()
236 }
237
238 pub fn additional_kwargs(&self) -> &HashMap<String, serde_json::Value> {
240 &self.additional_kwargs
241 }
242
243 pub fn response_metadata(&self) -> &HashMap<String, serde_json::Value> {
245 &self.response_metadata
246 }
247
248 pub fn concat(&self, other: &HumanMessageChunk) -> HumanMessageChunk {
250 let content = match (&self.content, &other.content) {
251 (MessageContent::Text(a), MessageContent::Text(b)) => {
252 MessageContent::Text(merge_content(a, b))
253 }
254 (MessageContent::Parts(a), MessageContent::Parts(b)) => {
255 let mut parts = a.clone();
256 parts.extend(b.clone());
257 MessageContent::Parts(parts)
258 }
259 (MessageContent::Text(a), MessageContent::Parts(b)) => {
260 let mut parts = vec![ContentPart::Text { text: a.clone() }];
261 parts.extend(b.clone());
262 MessageContent::Parts(parts)
263 }
264 (MessageContent::Parts(a), MessageContent::Text(b)) => {
265 let mut parts = a.clone();
266 parts.push(ContentPart::Text { text: b.clone() });
267 MessageContent::Parts(parts)
268 }
269 };
270
271 let mut additional_kwargs = self.additional_kwargs.clone();
273 for (k, v) in &other.additional_kwargs {
274 additional_kwargs.insert(k.clone(), v.clone());
275 }
276
277 let mut response_metadata = self.response_metadata.clone();
279 for (k, v) in &other.response_metadata {
280 response_metadata.insert(k.clone(), v.clone());
281 }
282
283 HumanMessageChunk {
284 content,
285 id: self.id.clone().or_else(|| other.id.clone()),
286 name: self.name.clone().or_else(|| other.name.clone()),
287 additional_kwargs,
288 response_metadata,
289 }
290 }
291
292 pub fn to_message(&self) -> HumanMessage {
294 HumanMessage {
295 content: self.content.clone(),
296 id: self.id.clone(),
297 name: self.name.clone(),
298 additional_kwargs: self.additional_kwargs.clone(),
299 }
300 }
301}
302
303impl std::ops::Add for HumanMessageChunk {
304 type Output = HumanMessageChunk;
305
306 fn add(self, other: HumanMessageChunk) -> HumanMessageChunk {
307 self.concat(&other)
308 }
309}