agent_chain_core/utils/
merge.rs

1//! Dictionary and list merging utilities.
2//!
3//! Adapted from langchain_core/utils/_merge.py
4
5use serde_json::{Map, Value};
6
7/// Merge multiple JSON objects.
8///
9/// Handles specific scenarios where a key exists in both
10/// dictionaries but has a value of null in 'left'. In such cases, the method uses the
11/// value from 'right' for that key in the merged dictionary.
12///
13/// # Arguments
14///
15/// * `left` - The first dictionary to merge
16/// * `others` - The other dictionaries to merge
17///
18/// # Returns
19///
20/// The merged dictionary.
21///
22/// # Errors
23///
24/// Returns an error if:
25/// - The key exists in both dictionaries but has a different type.
26/// - The value has an unsupported type.
27///
28/// # Example
29///
30/// ```
31/// use serde_json::json;
32/// use agent_chain_core::utils::merge::merge_dicts;
33///
34/// let left = json!({"function_call": {"arguments": null}});
35/// let right = json!({"function_call": {"arguments": "{\n"}});
36/// let merged = merge_dicts(left, vec![right]).unwrap();
37/// // merged = {"function_call": {"arguments": "{\n"}}
38/// ```
39pub fn merge_dicts(left: Value, others: Vec<Value>) -> Result<Value, MergeError> {
40    let mut merged = match left {
41        Value::Object(map) => map,
42        _ => return Err(MergeError::NotAnObject),
43    };
44
45    for right in others {
46        let right_map = match right {
47            Value::Object(map) => map,
48            Value::Null => continue,
49            _ => return Err(MergeError::NotAnObject),
50        };
51
52        for (right_k, right_v) in right_map {
53            if !merged.contains_key(&right_k)
54                || (right_v != Value::Null && merged.get(&right_k) == Some(&Value::Null))
55            {
56                merged.insert(right_k, right_v);
57            } else if right_v == Value::Null {
58                continue;
59            } else {
60                let left_v = merged.get(&right_k).unwrap();
61
62                if !values_same_type(left_v, &right_v) {
63                    return Err(MergeError::TypeMismatch {
64                        key: right_k.clone(),
65                        left_type: type_name(left_v),
66                        right_type: type_name(&right_v),
67                    });
68                }
69
70                match (left_v.clone(), right_v.clone()) {
71                    (Value::String(left_str), Value::String(right_str)) => {
72                        if (right_k == "index" && left_str.starts_with("lc_"))
73                            || ((right_k == "id"
74                                || right_k == "output_version"
75                                || right_k == "model_provider")
76                                && left_str == right_str)
77                        {
78                            continue;
79                        }
80                        merged.insert(right_k, Value::String(left_str + &right_str));
81                    }
82                    (Value::Object(_), Value::Object(_)) => {
83                        let merged_nested =
84                            merge_dicts(merged.remove(&right_k).unwrap(), vec![right_v])?;
85                        merged.insert(right_k, merged_nested);
86                    }
87                    (Value::Array(left_arr), Value::Array(right_arr)) => {
88                        let merged_list = merge_lists(Some(left_arr), vec![Some(right_arr)])?;
89                        merged.insert(right_k, Value::Array(merged_list.unwrap_or_default()));
90                    }
91                    (Value::Number(left_num), Value::Number(right_num)) => {
92                        if left_num == right_num {
93                            continue;
94                        }
95                        if let (Some(left_i), Some(right_i)) =
96                            (left_num.as_i64(), right_num.as_i64())
97                        {
98                            merged.insert(
99                                right_k,
100                                Value::Number(serde_json::Number::from(left_i + right_i)),
101                            );
102                        } else if let (Some(left_f), Some(right_f)) =
103                            (left_num.as_f64(), right_num.as_f64())
104                            && let Some(num) = serde_json::Number::from_f64(left_f + right_f)
105                        {
106                            merged.insert(right_k, Value::Number(num));
107                        }
108                    }
109                    (left_v, right_v) if left_v == right_v => {
110                        continue;
111                    }
112                    (left_v, _) => {
113                        return Err(MergeError::UnsupportedType {
114                            key: right_k,
115                            value_type: type_name(&left_v),
116                        });
117                    }
118                }
119            }
120        }
121    }
122
123    Ok(Value::Object(merged))
124}
125
126/// Merge multiple lists, handling None.
127///
128/// # Arguments
129///
130/// * `left` - The first list to merge
131/// * `others` - The other lists to merge
132///
133/// # Returns
134///
135/// The merged list.
136pub fn merge_lists(
137    left: Option<Vec<Value>>,
138    others: Vec<Option<Vec<Value>>>,
139) -> Result<Option<Vec<Value>>, MergeError> {
140    let mut merged = left.map(|v| v.to_vec());
141
142    for other in others {
143        let Some(other_vec) = other else {
144            continue;
145        };
146
147        if let Some(ref mut merged_vec) = merged {
148            for e in other_vec {
149                if let Value::Object(ref e_map) = e
150                    && let Some(index) = e_map.get("index")
151                {
152                    let should_merge = match index {
153                        Value::Number(n) => n.as_i64().is_some(),
154                        Value::String(s) => s.starts_with("lc_"),
155                        _ => false,
156                    };
157
158                    if should_merge {
159                        let to_merge: Vec<usize> = merged_vec
160                            .iter()
161                            .enumerate()
162                            .filter_map(|(i, e_left)| {
163                                if let Value::Object(left_map) = e_left
164                                    && left_map.get("index") == Some(index)
165                                {
166                                    return Some(i);
167                                }
168                                None
169                            })
170                            .collect();
171
172                        if !to_merge.is_empty() {
173                            let merge_idx = to_merge[0];
174                            let left_elem = &merged_vec[merge_idx];
175
176                            let left_type = left_elem
177                                .as_object()
178                                .and_then(|m| m.get("type"))
179                                .and_then(|t| t.as_str());
180
181                            let new_e: Value = if left_type.is_some() {
182                                let e_type = e_map.get("type").and_then(|t| t.as_str());
183
184                                if e_type == Some("non_standard") && e_map.contains_key("value") {
185                                    if left_type != Some("non_standard") {
186                                        let mut extras = Map::new();
187                                        if let Some(Value::Object(value_map)) = e_map.get("value") {
188                                            for (k, v) in value_map {
189                                                if k != "type" {
190                                                    extras.insert(k.clone(), v.clone());
191                                                }
192                                            }
193                                        }
194                                        Value::Object(
195                                            [("extras".to_string(), Value::Object(extras))]
196                                                .into_iter()
197                                                .collect(),
198                                        )
199                                    } else {
200                                        let mut new_map = Map::new();
201                                        let mut value_map = Map::new();
202                                        if let Some(Value::Object(orig_value)) = e_map.get("value")
203                                        {
204                                            for (k, v) in orig_value {
205                                                if k != "type" {
206                                                    value_map.insert(k.clone(), v.clone());
207                                                }
208                                            }
209                                        }
210                                        new_map
211                                            .insert("value".to_string(), Value::Object(value_map));
212                                        if let Some(idx) = e_map.get("index") {
213                                            new_map.insert("index".to_string(), idx.clone());
214                                        }
215                                        Value::Object(new_map)
216                                    }
217                                } else {
218                                    let mut new_map = Map::new();
219                                    for (k, v) in e_map {
220                                        if k != "type" {
221                                            new_map.insert(k.clone(), v.clone());
222                                        }
223                                    }
224                                    Value::Object(new_map)
225                                }
226                            } else {
227                                e.clone()
228                            };
229
230                            let left_val = merged_vec.remove(merge_idx);
231                            let merged_val = merge_dicts(left_val, vec![new_e])?;
232                            merged_vec.insert(merge_idx, merged_val);
233                        } else {
234                            merged_vec.push(e);
235                        }
236                        continue;
237                    }
238                }
239                merged_vec.push(e);
240            }
241        } else {
242            merged = Some(other_vec);
243        }
244    }
245
246    Ok(merged)
247}
248
249/// Merge two objects.
250///
251/// It handles specific scenarios where a key exists in both
252/// dictionaries but has a value of null in 'left'. In such cases, the method uses the
253/// value from 'right' for that key in the merged dictionary.
254///
255/// # Arguments
256///
257/// * `left` - The first object to merge
258/// * `right` - The other object to merge
259///
260/// # Returns
261///
262/// The merged object.
263///
264/// # Errors
265///
266/// Returns an error if:
267/// - The key exists in both dictionaries but has a different type.
268/// - The two objects cannot be merged.
269pub fn merge_obj(left: Value, right: Value) -> Result<Value, MergeError> {
270    if left == Value::Null || right == Value::Null {
271        return Ok(if left != Value::Null { left } else { right });
272    }
273
274    if !values_same_type(&left, &right) {
275        return Err(MergeError::TypeMismatch {
276            key: String::new(),
277            left_type: type_name(&left),
278            right_type: type_name(&right),
279        });
280    }
281
282    match (&left, &right) {
283        (Value::String(l), Value::String(r)) => Ok(Value::String(l.clone() + r)),
284        (Value::Object(_), Value::Object(_)) => merge_dicts(left, vec![right]),
285        (Value::Array(l), Value::Array(r)) => {
286            let merged = merge_lists(Some(l.clone()), vec![Some(r.clone())])?;
287            Ok(Value::Array(merged.unwrap_or_default()))
288        }
289        (l, r) if l == r => Ok(left),
290        _ => Err(MergeError::UnableToMerge {
291            left_type: type_name(&left),
292            right_type: type_name(&right),
293        }),
294    }
295}
296
297/// Error types for merge operations.
298#[derive(Debug, Clone, PartialEq)]
299pub enum MergeError {
300    /// The value is not a JSON object.
301    NotAnObject,
302    /// Type mismatch between left and right values.
303    TypeMismatch {
304        key: String,
305        left_type: String,
306        right_type: String,
307    },
308    /// Unsupported value type.
309    UnsupportedType { key: String, value_type: String },
310    /// Unable to merge the two values.
311    UnableToMerge {
312        left_type: String,
313        right_type: String,
314    },
315}
316
317impl std::fmt::Display for MergeError {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319        match self {
320            MergeError::NotAnObject => write!(f, "Value is not a JSON object"),
321            MergeError::TypeMismatch {
322                key,
323                left_type,
324                right_type,
325            } => {
326                write!(
327                    f,
328                    "additional_kwargs[\"{}\"] already exists in this message, but with a different type. Left type: {}, Right type: {}",
329                    key, left_type, right_type
330                )
331            }
332            MergeError::UnsupportedType { key, value_type } => {
333                write!(
334                    f,
335                    "Additional kwargs key {} already exists in left dict and value has unsupported type {}",
336                    key, value_type
337                )
338            }
339            MergeError::UnableToMerge {
340                left_type,
341                right_type,
342            } => {
343                write!(
344                    f,
345                    "Unable to merge {} and {}. Both must be of type str, dict, or list, or else be two equal objects",
346                    left_type, right_type
347                )
348            }
349        }
350    }
351}
352
353impl std::error::Error for MergeError {}
354
355fn values_same_type(left: &Value, right: &Value) -> bool {
356    matches!(
357        (left, right),
358        (Value::Null, Value::Null)
359            | (Value::Bool(_), Value::Bool(_))
360            | (Value::Number(_), Value::Number(_))
361            | (Value::String(_), Value::String(_))
362            | (Value::Array(_), Value::Array(_))
363            | (Value::Object(_), Value::Object(_))
364    )
365}
366
367fn type_name(value: &Value) -> String {
368    match value {
369        Value::Null => "null",
370        Value::Bool(_) => "bool",
371        Value::Number(_) => "number",
372        Value::String(_) => "string",
373        Value::Array(_) => "array",
374        Value::Object(_) => "object",
375    }
376    .to_string()
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382    use serde_json::json;
383
384    #[test]
385    fn test_merge_dicts_basic() {
386        let left = json!({"a": 1, "b": 2});
387        let right = json!({"c": 3});
388        let result = merge_dicts(left, vec![right]).unwrap();
389        assert_eq!(result, json!({"a": 1, "b": 2, "c": 3}));
390    }
391
392    #[test]
393    fn test_merge_dicts_null_handling() {
394        let left = json!({"a": null});
395        let right = json!({"a": "value"});
396        let result = merge_dicts(left, vec![right]).unwrap();
397        assert_eq!(result, json!({"a": "value"}));
398    }
399
400    #[test]
401    fn test_merge_dicts_string_concatenation() {
402        let left = json!({"text": "hello "});
403        let right = json!({"text": "world"});
404        let result = merge_dicts(left, vec![right]).unwrap();
405        assert_eq!(result, json!({"text": "hello world"}));
406    }
407
408    #[test]
409    fn test_merge_dicts_nested() {
410        let left = json!({"outer": {"inner": "a"}});
411        let right = json!({"outer": {"inner": "b"}});
412        let result = merge_dicts(left, vec![right]).unwrap();
413        assert_eq!(result, json!({"outer": {"inner": "ab"}}));
414    }
415
416    #[test]
417    fn test_merge_lists_basic() {
418        let left = Some(vec![json!(1), json!(2)]);
419        let right = Some(vec![json!(3), json!(4)]);
420        let result = merge_lists(left, vec![right]).unwrap();
421        assert_eq!(result, Some(vec![json!(1), json!(2), json!(3), json!(4)]));
422    }
423
424    #[test]
425    fn test_merge_lists_with_index() {
426        let left = Some(vec![json!({"index": 0, "value": "a"})]);
427        let right = Some(vec![json!({"index": 0, "value": "b"})]);
428        let result = merge_lists(left, vec![right]).unwrap();
429        assert_eq!(result, Some(vec![json!({"index": 0, "value": "ab"})]));
430    }
431
432    #[test]
433    fn test_merge_obj_strings() {
434        let left = json!("hello ");
435        let right = json!("world");
436        let result = merge_obj(left, right).unwrap();
437        assert_eq!(result, json!("hello world"));
438    }
439
440    #[test]
441    fn test_merge_obj_with_null() {
442        let left = Value::Null;
443        let right = json!("value");
444        let result = merge_obj(left, right).unwrap();
445        assert_eq!(result, json!("value"));
446    }
447}