1use super::any::{into_runnable_any, RunnableAny};
11use super::config::RunnableConfig;
12use super::error::LcelError;
13use super::runnable_trait::Runnable;
14use async_trait::async_trait;
15use futures_util::{Stream, StreamExt};
16use std::any::Any;
17use std::marker::PhantomData;
18use std::pin::Pin;
19
20pub struct RunnableSequence<I: Send + Sync + 'static, O: Send + Sync + 'static> {
38 steps: Vec<Box<dyn RunnableAny>>,
39 _marker: PhantomData<(I, O)>,
40}
41
42impl<I: Send + Sync + 'static, O: Send + Sync + 'static> std::fmt::Debug for RunnableSequence<I, O> {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("RunnableSequence")
45 .field("steps", &self.steps.len())
46 .field("input", &std::any::type_name::<I>())
47 .field("output", &std::any::type_name::<O>())
48 .finish()
49 }
50}
51
52impl<I: Send + Sync + 'static, O: Send + Sync + 'static> RunnableSequence<I, O> {
53 pub fn from_single<R>(runnable: R) -> Self
55 where
56 R: Runnable<I, O> + 'static,
57 R::Error: Into<LcelError>,
58 {
59 Self {
60 steps: vec![into_runnable_any(runnable)],
61 _marker: PhantomData,
62 }
63 }
64
65 pub fn from_pair<R1, R2, M>(first: R1, second: R2) -> RunnableSequence<I, O>
69 where
70 M: Send + Sync + 'static,
71 R1: Runnable<I, M> + 'static,
72 R1::Error: Into<LcelError>,
73 R2: Runnable<M, O> + 'static,
74 R2::Error: Into<LcelError>,
75 {
76 Self {
77 steps: vec![into_runnable_any(first), into_runnable_any(second)],
78 _marker: PhantomData,
79 }
80 }
81
82 pub fn pipe<O2, R>(self, other: R) -> RunnableSequence<I, O2>
85 where
86 O2: Send + Sync + 'static,
87 R: Runnable<O, O2> + Send + Sync + 'static,
88 R::Error: Into<LcelError>,
89 {
90 let mut steps = self.steps;
91 steps.push(into_runnable_any(other));
92 RunnableSequence {
93 steps,
94 _marker: PhantomData,
95 }
96 }
97
98 pub fn len(&self) -> usize {
100 self.steps.len()
101 }
102
103 pub fn is_empty(&self) -> bool {
105 self.steps.is_empty()
106 }
107
108 pub fn steps(&self) -> &[Box<dyn RunnableAny>] {
110 &self.steps
111 }
112}
113
114#[async_trait]
115impl<I: Send + Sync + 'static, O: Send + Sync + 'static> Runnable<I, O> for RunnableSequence<I, O> {
116 type Error = LcelError;
117
118 async fn invoke(
120 &self,
121 input: I,
122 config: Option<RunnableConfig>,
123 ) -> Result<O, LcelError> {
124 let mut current: Box<dyn Any + Send> = Box::new(input);
125 for step in &self.steps {
126 current = step.invoke_any(current, config.clone()).await?;
127 }
128 current
129 .downcast::<O>()
130 .map(|b| *b)
131 .map_err(|_| LcelError::TypeMismatch(format!(
132 "final downcast failed: expected {}",
133 std::any::type_name::<O>()
134 )))
135 }
136
137 async fn batch(
141 &self,
142 inputs: Vec<I>,
143 config: Option<RunnableConfig>,
144 ) -> Result<Vec<O>, LcelError> {
145 let mut current: Vec<Box<dyn Any + Send>> =
146 inputs.into_iter().map(|i| Box::new(i) as Box<dyn Any + Send>).collect();
147
148 for step in &self.steps {
149 current = step.batch_any(current, config.clone()).await?;
150 }
151
152 current
153 .into_iter()
154 .map(|boxed| {
155 boxed
156 .downcast::<O>()
157 .map(|b| *b)
158 .map_err(|_| LcelError::TypeMismatch(format!(
159 "batch final downcast: expected {}",
160 std::any::type_name::<O>()
161 )))
162 })
163 .collect()
164 }
165
166 async fn stream(
169 &self,
170 input: I,
171 config: Option<RunnableConfig>,
172 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send>>, LcelError> {
173 let input_stream: Pin<Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>> =
175 Box::pin(futures_util::stream::once(async {
176 Ok(Box::new(input) as Box<dyn Any + Send>)
177 }));
178
179 let mut current_stream = input_stream;
180
181 for step in &self.steps {
183 current_stream = step.transform_any(current_stream, config.clone()).await?;
184 }
185
186 let output_stream = current_stream.map(|result| {
188 result.and_then(|boxed| {
189 boxed
190 .downcast::<O>()
191 .map(|b| *b)
192 .map_err(|_| LcelError::TypeMismatch(format!(
193 "stream final downcast: expected {}",
194 std::any::type_name::<O>()
195 )))
196 })
197 });
198
199 Ok(Box::pin(output_stream))
200 }
201
202 async fn transform(
205 &self,
206 input: Pin<Box<dyn Stream<Item = Result<I, LcelError>> + Send>>,
207 config: Option<RunnableConfig>,
208 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send>>, LcelError> {
209 let mut current_stream: Pin<Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>> =
211 Box::pin(input.map(|result| {
212 result.map(|item| Box::new(item) as Box<dyn Any + Send>)
213 }));
214
215 for step in &self.steps {
217 current_stream = step.transform_any(current_stream, config.clone()).await?;
218 }
219
220 let output_stream = current_stream.map(|result| {
222 result.and_then(|boxed| {
223 boxed
224 .downcast::<O>()
225 .map(|b| *b)
226 .map_err(|_| LcelError::TypeMismatch(format!(
227 "transform final downcast: expected {}",
228 std::any::type_name::<O>()
229 )))
230 })
231 });
232
233 Ok(Box::pin(output_stream))
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use futures_util::StreamExt;
241
242 struct Double;
243
244 #[async_trait]
245 impl Runnable<i32, i32> for Double {
246 type Error = std::convert::Infallible;
247
248 async fn invoke(&self, input: i32, _config: Option<RunnableConfig>) -> Result<i32, Self::Error> {
249 Ok(input * 2)
250 }
251 }
252
253 struct AddOne;
254
255 #[async_trait]
256 impl Runnable<i32, i32> for AddOne {
257 type Error = std::convert::Infallible;
258
259 async fn invoke(&self, input: i32, _config: Option<RunnableConfig>) -> Result<i32, Self::Error> {
260 Ok(input + 1)
261 }
262 }
263
264 struct I32ToString;
265
266 #[async_trait]
267 impl Runnable<i32, String> for I32ToString {
268 type Error = std::convert::Infallible;
269
270 async fn invoke(&self, input: i32, _config: Option<RunnableConfig>) -> Result<String, Self::Error> {
271 Ok(format!("value={}", input))
272 }
273 }
274
275 #[tokio::test]
276 async fn invoke_two_steps() {
277 let seq = RunnableSequence::from_pair(Double, AddOne);
279 let result = seq.invoke(5, None).await.unwrap();
280 assert_eq!(result, 11);
281 }
282
283 #[tokio::test]
284 async fn invoke_three_steps() {
285 let seq = RunnableSequence::from_pair(Double, AddOne).pipe(I32ToString);
287 let result = seq.invoke(3, None).await.unwrap();
288 assert_eq!(result, "value=7");
289 }
290
291 #[tokio::test]
292 async fn batch_works() {
293 let seq = RunnableSequence::from_pair(Double, AddOne);
294 let results = seq.batch(vec![1, 2, 3], None).await.unwrap();
295 assert_eq!(results, vec![3, 5, 7]);
296 }
297
298 #[tokio::test]
299 async fn stream_works() {
300 let seq = RunnableSequence::from_pair(Double, AddOne);
301 let mut stream = seq.stream(10, None).await.unwrap();
302 let result = stream.next().await.unwrap().unwrap();
303 assert_eq!(result, 21);
304 }
305
306 #[tokio::test]
307 async fn transform_works() {
308 let seq = RunnableSequence::from_pair(Double, AddOne);
309 let input = Box::pin(futures_util::stream::iter(vec![
310 Ok(1i32),
311 Ok(2i32),
312 Ok(3i32),
313 ])) as Pin<Box<dyn Stream<Item = Result<i32, LcelError>> + Send>>;
314
315 let mut output = seq.transform(input, None).await.unwrap();
316 let result = output.next().await.unwrap().unwrap();
318 assert_eq!(result, 7);
319 }
320
321 #[tokio::test]
322 async fn from_single_works() {
323 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(Double);
324 let result = seq.invoke(4, None).await.unwrap();
325 assert_eq!(result, 8);
326 }
327
328 #[tokio::test]
329 async fn pipe_on_sequence_works() {
330 let seq = RunnableSequence::from_single(Double).pipe(AddOne).pipe(I32ToString);
331 let result = seq.invoke(5, None).await.unwrap();
332 assert_eq!(result, "value=11"); }
334
335 #[tokio::test]
336 async fn len_and_empty() {
337 let seq = RunnableSequence::from_pair(Double, AddOne);
338 assert_eq!(seq.len(), 2);
339 assert!(!seq.is_empty());
340 }
341}