1use async_trait::async_trait;
7use serde_json::Value;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11use crate::base::{BaseChain, ChainError, ChainResult, ChainStream};
12
13pub struct SequentialChain {
18 chains: Vec<ChainStep>,
20
21 name: String,
23}
24
25struct ChainStep {
27 chain: Arc<dyn BaseChain>,
29
30 input_mapping: HashMap<String, String>,
32
33 output_mapping: HashMap<String, String>,
35}
36
37impl SequentialChain {
38 pub fn new() -> Self {
40 Self {
41 chains: Vec::new(),
42 name: "sequential_chain".to_string(),
43 }
44 }
45
46 pub fn with_name(mut self, name: impl Into<String>) -> Self {
48 self.name = name.into();
49 self
50 }
51
52 pub fn add_chain(
59 mut self,
60 chain: Arc<dyn BaseChain>,
61 input_keys: Vec<&str>,
62 output_keys: Vec<&str>,
63 ) -> Self {
64 let input_mapping = input_keys
65 .into_iter()
66 .map(|k| (k.to_string(), k.to_string()))
67 .collect();
68
69 let output_mapping = output_keys
70 .into_iter()
71 .map(|k| (k.to_string(), k.to_string()))
72 .collect();
73
74 self.chains.push(ChainStep {
75 chain,
76 input_mapping,
77 output_mapping,
78 });
79
80 self
81 }
82
83 pub fn add_chain_with_mapping(
90 mut self,
91 chain: Arc<dyn BaseChain>,
92 input_mapping: HashMap<String, String>,
93 output_mapping: HashMap<String, String>,
94 ) -> Self {
95 self.chains.push(ChainStep {
96 chain,
97 input_mapping,
98 output_mapping,
99 });
100
101 self
102 }
103}
104
105impl Default for SequentialChain {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[async_trait]
112impl BaseChain for SequentialChain {
113 fn input_keys(&self) -> Vec<&str> {
114 if let Some(first) = self.chains.first() {
115 first.input_mapping.values().map(|s| s.as_str()).collect()
116 } else {
117 vec![]
118 }
119 }
120
121 fn output_keys(&self) -> Vec<&str> {
122 if let Some(last) = self.chains.last() {
123 last.output_mapping.values().map(|s| s.as_str()).collect()
124 } else {
125 vec![]
126 }
127 }
128
129 async fn invoke(&self, inputs: HashMap<String, Value>) -> Result<ChainResult, ChainError> {
130 let mut current_state = inputs.clone();
131 let mut final_output = HashMap::new();
132
133 for (step_index, step) in self.chains.iter().enumerate() {
134 let mut chain_inputs = HashMap::new();
135 for (chain_key, global_key) in &step.input_mapping {
136 if let Some(value) = current_state.get(global_key) {
137 chain_inputs.insert(chain_key.clone(), value.clone());
138 } else {
139 return Err(ChainError::MissingInput(format!(
140 "Step {}: missing input '{}' (mapped from '{}')",
141 step_index, chain_key, global_key
142 )));
143 }
144 }
145
146 let chain_output = step.chain.invoke(chain_inputs).await.map_err(|e| {
147 ChainError::ExecutionError(format!(
148 "Step {} ({}) execution failed: {}",
149 step_index,
150 step.chain.name(),
151 e
152 ))
153 })?;
154
155 for (chain_key, global_key) in &step.output_mapping {
156 if let Some(value) = chain_output.get(chain_key) {
157 current_state.insert(global_key.clone(), value.clone());
158 final_output.insert(global_key.clone(), value.clone());
159 } else {
160 return Err(ChainError::OutputError(format!(
161 "Step {} ({}) did not produce expected output key '{}' (mapped to '{}')",
162 step_index,
163 step.chain.name(),
164 chain_key,
165 global_key
166 )));
167 }
168 }
169 }
170
171 Ok(final_output)
172 }
173
174 async fn stream(&self, inputs: HashMap<String, Value>) -> Result<ChainStream, ChainError> {
180 if self.chains.is_empty() {
181 return Err(ChainError::ExecutionError(
182 "SequentialChain has no chains".to_string(),
183 ));
184 }
185
186 let mut current_state = inputs.clone();
187
188 let last_idx = self.chains.len() - 1;
190 for (step_index, step) in self.chains[..last_idx].iter().enumerate() {
191 let mut chain_inputs = HashMap::new();
192 for (chain_key, global_key) in &step.input_mapping {
193 if let Some(value) = current_state.get(global_key) {
194 chain_inputs.insert(chain_key.clone(), value.clone());
195 } else {
196 return Err(ChainError::MissingInput(format!(
197 "Step {}: missing input '{}' (mapped from '{}')",
198 step_index, chain_key, global_key
199 )));
200 }
201 }
202
203 let chain_output = step.chain.invoke(chain_inputs).await.map_err(|e| {
204 ChainError::ExecutionError(format!(
205 "Step {} ({}) execution failed: {}",
206 step_index,
207 step.chain.name(),
208 e
209 ))
210 })?;
211
212 for (chain_key, global_key) in &step.output_mapping {
213 if let Some(value) = chain_output.get(chain_key) {
214 current_state.insert(global_key.clone(), value.clone());
215 } else {
216 return Err(ChainError::OutputError(format!(
217 "Step {} ({}) did not produce expected output key '{}'",
218 step_index,
219 step.chain.name(),
220 chain_key,
221 )));
222 }
223 }
224 }
225
226 let last_step = &self.chains[last_idx];
228 let mut chain_inputs = HashMap::new();
229 for (chain_key, global_key) in &last_step.input_mapping {
230 if let Some(value) = current_state.get(global_key) {
231 chain_inputs.insert(chain_key.clone(), value.clone());
232 } else {
233 return Err(ChainError::MissingInput(format!(
234 "Last step: missing input '{}' (mapped from '{}')",
235 chain_key, global_key
236 )));
237 }
238 }
239
240 last_step.chain.stream(chain_inputs).await
241 }
242
243 fn name(&self) -> &str {
244 &self.name
245 }
246}
247
248impl std::fmt::Debug for SequentialChain {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 f.debug_struct("SequentialChain")
251 .field("steps", &self.chains.len())
252 .field("name", &self.name)
253 .finish()
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use async_trait::async_trait;
261 use serde_json::json;
262
263 struct EchoChain {
265 input_key: String,
266 output_key: String,
267 }
268
269 impl EchoChain {
270 fn new(input_key: &str, output_key: &str) -> Self {
271 Self {
272 input_key: input_key.to_string(),
273 output_key: output_key.to_string(),
274 }
275 }
276 }
277
278 #[async_trait]
279 impl BaseChain for EchoChain {
280 fn input_keys(&self) -> Vec<&str> {
281 vec![&self.input_key]
282 }
283 fn output_keys(&self) -> Vec<&str> {
284 vec![&self.output_key]
285 }
286 async fn invoke(&self, inputs: HashMap<String, Value>) -> Result<ChainResult, ChainError> {
287 let mut result = HashMap::new();
288 if let Some(v) = inputs.get(&self.input_key) {
289 result.insert(self.output_key.clone(), v.clone());
290 }
291 Ok(result)
292 }
293 }
294
295 struct UppercaseChain {
297 input_key: String,
298 output_key: String,
299 }
300
301 #[async_trait]
302 impl BaseChain for UppercaseChain {
303 fn input_keys(&self) -> Vec<&str> { vec![&self.input_key] }
304 fn output_keys(&self) -> Vec<&str> { vec![&self.output_key] }
305 async fn invoke(&self, inputs: HashMap<String, Value>) -> Result<ChainResult, ChainError> {
306 let mut result = HashMap::new();
307 if let Some(Value::String(s)) = inputs.get(&self.input_key) {
308 result.insert(self.output_key.clone(), Value::String(s.to_uppercase()));
309 }
310 Ok(result)
311 }
312 }
313
314 #[tokio::test]
315 async fn test_sequential_chain_single_step() {
316 let chain = SequentialChain::new()
317 .add_chain(Arc::new(EchoChain::new("text", "result")), vec!["text"], vec!["result"]);
318
319 let mut inputs = HashMap::new();
320 inputs.insert("text".to_string(), json!("hello"));
321
322 let result = chain.invoke(inputs).await.unwrap();
323 assert_eq!(result.get("result").unwrap(), &json!("hello"));
324 }
325
326 #[tokio::test]
327 async fn test_sequential_chain_two_steps() {
328 let chain = SequentialChain::new()
329 .add_chain(Arc::new(EchoChain::new("text", "intermediate")), vec!["text"], vec!["intermediate"])
330 .add_chain(Arc::new(UppercaseChain { input_key: "intermediate".to_string(), output_key: "result".to_string() }), vec!["intermediate"], vec!["result"]);
331
332 let mut inputs = HashMap::new();
333 inputs.insert("text".to_string(), json!("hello"));
334
335 let result = chain.invoke(inputs).await.unwrap();
336 assert_eq!(result.get("result").unwrap(), &json!("HELLO"));
337 }
338
339 #[tokio::test]
340 async fn test_sequential_chain_missing_input() {
341 let chain = SequentialChain::new()
342 .add_chain(Arc::new(EchoChain::new("text", "result")), vec!["text"], vec!["result"]);
343
344 let inputs = HashMap::new();
345 let result = chain.invoke(inputs).await;
346 assert!(result.is_err());
347 }
348
349 #[tokio::test]
350 async fn test_sequential_chain_with_name() {
351 let chain = SequentialChain::new().with_name("my_chain");
352 assert_eq!(chain.name(), "my_chain");
353 }
354
355 #[tokio::test]
356 async fn test_sequential_chain_default() {
357 let chain = SequentialChain::default();
358 assert_eq!(chain.name(), "sequential_chain");
359 }
360
361 #[tokio::test]
362 async fn test_sequential_chain_debug() {
363 let chain = SequentialChain::new().with_name("test_chain");
364 let debug_str = format!("{:?}", chain);
365 assert!(debug_str.contains("test_chain"));
366 assert!(debug_str.contains("0")); }
368
369 #[tokio::test]
370 async fn test_sequential_chain_input_keys() {
371 let chain = SequentialChain::new()
372 .add_chain(Arc::new(EchoChain::new("query", "result")), vec!["query"], vec!["result"]);
373 assert_eq!(chain.input_keys(), vec!["query"]);
374 }
375
376 #[tokio::test]
377 async fn test_sequential_chain_output_keys() {
378 let chain = SequentialChain::new()
379 .add_chain(Arc::new(EchoChain::new("query", "answer")), vec!["query"], vec!["answer"]);
380 assert_eq!(chain.output_keys(), vec!["answer"]);
381 }
382}