1use crate::command::NodeOutput;
8use crate::config::GraphConfig;
9use crate::error::AgentGraphError;
10use crate::node::Node;
11use crate::state::AgentState;
12use serde_json::Value;
13
14pub type MergeFn = Box<dyn Fn(Vec<(String, Value)>) -> crate::Result<Value> + Send + Sync>;
16
17pub struct JoinNode {
23 name: Option<String>,
24 input_keys: Vec<String>,
26 output_key: String,
28 merge_fn: MergeFn,
30}
31
32impl JoinNode {
33 pub fn new(
39 input_keys: Vec<String>,
40 output_key: impl Into<String>,
41 merge_fn: impl Fn(Vec<(String, Value)>) -> crate::Result<Value> + Send + Sync + 'static,
42 ) -> Self {
43 Self {
44 name: None,
45 input_keys,
46 output_key: output_key.into(),
47 merge_fn: Box::new(merge_fn),
48 }
49 }
50
51 pub fn with_name(mut self, name: impl Into<String>) -> Self {
53 self.name = Some(name.into());
54 self
55 }
56
57 pub fn collect_array(input_keys: Vec<String>, output_key: impl Into<String>) -> Self {
59 Self::new(input_keys, output_key, |values| {
60 let arr: Vec<Value> = values.into_iter().map(|(_, v)| v).collect();
61 Ok(Value::Array(arr))
62 })
63 }
64
65 pub fn merge_objects(input_keys: Vec<String>, output_key: impl Into<String>) -> Self {
67 Self::new(input_keys, output_key, |values| {
68 let mut result = serde_json::Map::new();
69 for (key, value) in values {
70 if let Value::Object(map) = value {
71 for (k, v) in map {
72 result.insert(k, v);
73 }
74 } else {
75 result.insert(key, value);
76 }
77 }
78 Ok(Value::Object(result))
79 })
80 }
81}
82
83#[async_trait::async_trait]
84impl Node for JoinNode {
85 async fn execute(
86 &self,
87 state: &AgentState,
88 _config: &GraphConfig,
89 ) -> crate::Result<NodeOutput> {
90 let mut inputs = Vec::new();
92 for key in &self.input_keys {
93 let value: Value = state.get_opt::<Value>(key).await?.unwrap_or(Value::Null);
94 inputs.push((key.clone(), value));
95 }
96
97 let has_data = inputs.iter().any(|(_, v)| !v.is_null());
99 if !has_data {
100 return Err(AgentGraphError::ExecutionError(format!(
101 "JoinNode: no data found for input keys {:?}",
102 self.input_keys
103 )));
104 }
105
106 let merged = (self.merge_fn)(inputs)?;
108
109 state.set_raw(&self.output_key, merged).await?;
111
112 Ok(NodeOutput::Done)
113 }
114
115 fn name(&self) -> Option<&str> {
116 self.name.as_deref()
117 }
118}
119
120impl std::fmt::Debug for JoinNode {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.debug_struct("JoinNode")
123 .field("name", &self.name)
124 .field("input_keys", &self.input_keys)
125 .field("output_key", &self.output_key)
126 .finish()
127 }
128}