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
43 for RunnableSequence<I, O>
44{
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("RunnableSequence")
47 .field("steps", &self.steps.len())
48 .field("input", &std::any::type_name::<I>())
49 .field("output", &std::any::type_name::<O>())
50 .finish()
51 }
52}
53
54impl<I: Send + Sync + 'static, O: Send + Sync + 'static> RunnableSequence<I, O> {
55 pub fn from_single<R>(runnable: R) -> Self
57 where
58 R: Runnable<I, O> + 'static,
59 R::Error: Into<LcelError>,
60 {
61 Self {
62 steps: vec![into_runnable_any(runnable)],
63 _marker: PhantomData,
64 }
65 }
66
67 pub fn from_pair<R1, R2, M>(first: R1, second: R2) -> RunnableSequence<I, O>
71 where
72 M: Send + Sync + 'static,
73 R1: Runnable<I, M> + 'static,
74 R1::Error: Into<LcelError>,
75 R2: Runnable<M, O> + 'static,
76 R2::Error: Into<LcelError>,
77 {
78 Self {
79 steps: vec![into_runnable_any(first), into_runnable_any(second)],
80 _marker: PhantomData,
81 }
82 }
83
84 pub fn pipe<O2, R>(self, other: R) -> RunnableSequence<I, O2>
87 where
88 O2: Send + Sync + 'static,
89 R: Runnable<O, O2> + Send + Sync + 'static,
90 R::Error: Into<LcelError>,
91 {
92 let mut steps = self.steps;
93 steps.push(into_runnable_any(other));
94 RunnableSequence {
95 steps,
96 _marker: PhantomData,
97 }
98 }
99
100 pub fn len(&self) -> usize {
102 self.steps.len()
103 }
104
105 pub fn is_empty(&self) -> bool {
107 self.steps.is_empty()
108 }
109
110 pub fn steps(&self) -> &[Box<dyn RunnableAny>] {
112 &self.steps
113 }
114}
115
116#[async_trait]
117impl<I: Send + Sync + 'static, O: Send + Sync + 'static> Runnable<I, O> for RunnableSequence<I, O> {
118 type Error = LcelError;
119
120 async fn invoke(&self, input: I, config: Option<RunnableConfig>) -> Result<O, LcelError> {
122 let mut current: Box<dyn Any + Send> = Box::new(input);
123 for step in &self.steps {
124 current = step.invoke_any(current, config.clone()).await?;
125 }
126 current.downcast::<O>().map(|b| *b).map_err(|_| {
127 LcelError::TypeMismatch(format!(
128 "final downcast failed: expected {}",
129 std::any::type_name::<O>()
130 ))
131 })
132 }
133
134 async fn batch(
138 &self,
139 inputs: Vec<I>,
140 config: Option<RunnableConfig>,
141 ) -> Result<Vec<O>, LcelError> {
142 let mut current: Vec<Box<dyn Any + Send>> = inputs
143 .into_iter()
144 .map(|i| Box::new(i) as Box<dyn Any + Send>)
145 .collect();
146
147 for step in &self.steps {
148 current = step.batch_any(current, config.clone()).await?;
149 }
150
151 current
152 .into_iter()
153 .map(|boxed| {
154 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
155 LcelError::TypeMismatch(format!(
156 "batch final downcast: expected {}",
157 std::any::type_name::<O>()
158 ))
159 })
160 })
161 .collect()
162 }
163
164 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 if self.steps.is_empty() {
174 return Ok(Box::pin(futures_util::stream::empty()));
175 }
176
177 let mut steps = self.steps.iter();
178 let Some(first) = steps.next() else {
179 return Ok(Box::pin(futures_util::stream::empty()));
180 };
181
182 let input_boxed: Box<dyn Any + Send> = Box::new(input);
183 let mut current_stream = first.stream_any(input_boxed, config.clone()).await?;
184
185 for step in steps {
187 current_stream = step.transform_any(current_stream, config.clone()).await?;
188 }
189
190 let output_stream = current_stream.map(|result| {
192 result.and_then(|boxed| {
193 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
194 LcelError::TypeMismatch(format!(
195 "stream final downcast: expected {}",
196 std::any::type_name::<O>()
197 ))
198 })
199 })
200 });
201
202 Ok(Box::pin(output_stream))
203 }
204
205 async fn transform(
208 &self,
209 input: Pin<Box<dyn Stream<Item = Result<I, LcelError>> + Send>>,
210 config: Option<RunnableConfig>,
211 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send + '_>>, LcelError> {
212 let mut current_stream: Pin<
214 Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>,
215 > = Box::pin(input.map(|result| result.map(|item| Box::new(item) as Box<dyn Any + Send>)));
216
217 for step in &self.steps {
219 current_stream = step.transform_any(current_stream, config.clone()).await?;
220 }
221
222 let output_stream = current_stream.map(|result| {
224 result.and_then(|boxed| {
225 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
226 LcelError::TypeMismatch(format!(
227 "transform final downcast: expected {}",
228 std::any::type_name::<O>()
229 ))
230 })
231 })
232 });
233
234 Ok(Box::pin(output_stream))
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use futures_util::StreamExt;
242
243 struct Double;
244
245 #[async_trait]
246 impl Runnable<i32, i32> for Double {
247 type Error = std::convert::Infallible;
248
249 async fn invoke(
250 &self,
251 input: i32,
252 _config: Option<RunnableConfig>,
253 ) -> Result<i32, Self::Error> {
254 Ok(input * 2)
255 }
256 }
257
258 struct AddOne;
259
260 #[async_trait]
261 impl Runnable<i32, i32> for AddOne {
262 type Error = std::convert::Infallible;
263
264 async fn invoke(
265 &self,
266 input: i32,
267 _config: Option<RunnableConfig>,
268 ) -> Result<i32, Self::Error> {
269 Ok(input + 1)
270 }
271 }
272
273 struct I32ToString;
274
275 #[async_trait]
276 impl Runnable<i32, String> for I32ToString {
277 type Error = std::convert::Infallible;
278
279 async fn invoke(
280 &self,
281 input: i32,
282 _config: Option<RunnableConfig>,
283 ) -> Result<String, Self::Error> {
284 Ok(format!("value={}", input))
285 }
286 }
287
288 #[tokio::test]
289 async fn invoke_two_steps() {
290 let seq = RunnableSequence::from_pair(Double, AddOne);
292 let result = seq.invoke(5, None).await.unwrap();
293 assert_eq!(result, 11);
294 }
295
296 #[tokio::test]
297 async fn invoke_three_steps() {
298 let seq = RunnableSequence::from_pair(Double, AddOne).pipe(I32ToString);
300 let result = seq.invoke(3, None).await.unwrap();
301 assert_eq!(result, "value=7");
302 }
303
304 #[tokio::test]
305 async fn batch_works() {
306 let seq = RunnableSequence::from_pair(Double, AddOne);
307 let results = seq.batch(vec![1, 2, 3], None).await.unwrap();
308 assert_eq!(results, vec![3, 5, 7]);
309 }
310
311 #[tokio::test]
312 async fn stream_works() {
313 let seq = RunnableSequence::from_pair(Double, AddOne);
314 let mut stream = seq.stream(10, None).await.unwrap();
315 let result = stream.next().await.unwrap().unwrap();
316 assert_eq!(result, 21);
317 }
318
319 #[tokio::test]
320 async fn transform_works_elementwise() {
321 let seq = RunnableSequence::from_pair(Double, AddOne);
322 let input = Box::pin(futures_util::stream::iter(vec![
323 Ok(1i32),
324 Ok(2i32),
325 Ok(3i32),
326 ])) as Pin<Box<dyn Stream<Item = Result<i32, LcelError>> + Send>>;
327
328 let mut output = seq.transform(input, None).await.unwrap();
331 let mut results = Vec::new();
332 while let Some(item) = output.next().await {
333 results.push(item.unwrap());
334 }
335 assert_eq!(results, vec![3, 5, 7]);
336 }
337
338 struct StreamingTokenLLM;
342
343 #[async_trait]
344 impl Runnable<i32, i32> for StreamingTokenLLM {
345 type Error = std::convert::Infallible;
346
347 async fn invoke(
348 &self,
349 input: i32,
350 _config: Option<RunnableConfig>,
351 ) -> Result<i32, Self::Error> {
352 Ok(input * 1000)
353 }
354
355 async fn stream(
356 &self,
357 input: i32,
358 _config: Option<RunnableConfig>,
359 ) -> Result<Pin<Box<dyn Stream<Item = Result<i32, Self::Error>> + Send>>, Self::Error>
360 {
361 let stream =
362 futures_util::stream::iter(vec![Ok(input), Ok(input + 100), Ok(input + 200)]);
363 Ok(Box::pin(stream))
364 }
365 }
366
367 #[tokio::test]
368 async fn stream_uses_real_streaming_for_first_step() {
369 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(StreamingTokenLLM);
373 let mut stream = seq.stream(5, None).await.unwrap();
374 let mut results = Vec::new();
375 while let Some(item) = stream.next().await {
376 results.push(item.unwrap());
377 }
378 assert_eq!(results, vec![5, 105, 205]);
379 }
380
381 #[tokio::test]
382 async fn stream_chains_subsequent_steps_elementwise() {
383 let seq = RunnableSequence::from_pair(StreamingTokenLLM, AddOne);
386 let mut stream = seq.stream(5, None).await.unwrap();
387 let mut results = Vec::new();
388 while let Some(item) = stream.next().await {
389 results.push(item.unwrap());
390 }
391 assert_eq!(results, vec![6, 106, 206]);
392 }
393
394 #[tokio::test]
395 async fn from_single_works() {
396 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(Double);
397 let result = seq.invoke(4, None).await.unwrap();
398 assert_eq!(result, 8);
399 }
400
401 #[tokio::test]
402 async fn pipe_on_sequence_works() {
403 let seq = RunnableSequence::from_single(Double)
404 .pipe(AddOne)
405 .pipe(I32ToString);
406 let result = seq.invoke(5, None).await.unwrap();
407 assert_eq!(result, "value=11"); }
409
410 #[tokio::test]
411 async fn len_and_empty() {
412 let seq = RunnableSequence::from_pair(Double, AddOne);
413 assert_eq!(seq.len(), 2);
414 assert!(!seq.is_empty());
415 }
416}