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 first = steps.next().expect("steps is non-empty");
179
180 let input_boxed: Box<dyn Any + Send> = Box::new(input);
181 let mut current_stream = first.stream_any(input_boxed, config.clone()).await?;
182
183 for step in steps {
185 current_stream = step.transform_any(current_stream, config.clone()).await?;
186 }
187
188 let output_stream = current_stream.map(|result| {
190 result.and_then(|boxed| {
191 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
192 LcelError::TypeMismatch(format!(
193 "stream final downcast: expected {}",
194 std::any::type_name::<O>()
195 ))
196 })
197 })
198 });
199
200 Ok(Box::pin(output_stream))
201 }
202
203 async fn transform(
206 &self,
207 input: Pin<Box<dyn Stream<Item = Result<I, LcelError>> + Send>>,
208 config: Option<RunnableConfig>,
209 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send>>, LcelError> {
210 let mut current_stream: Pin<
212 Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>,
213 > = Box::pin(input.map(|result| result.map(|item| Box::new(item) as Box<dyn Any + Send>)));
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.downcast::<O>().map(|b| *b).map_err(|_| {
224 LcelError::TypeMismatch(format!(
225 "transform final downcast: expected {}",
226 std::any::type_name::<O>()
227 ))
228 })
229 })
230 });
231
232 Ok(Box::pin(output_stream))
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use futures_util::StreamExt;
240
241 struct Double;
242
243 #[async_trait]
244 impl Runnable<i32, i32> for Double {
245 type Error = std::convert::Infallible;
246
247 async fn invoke(
248 &self,
249 input: i32,
250 _config: Option<RunnableConfig>,
251 ) -> Result<i32, Self::Error> {
252 Ok(input * 2)
253 }
254 }
255
256 struct AddOne;
257
258 #[async_trait]
259 impl Runnable<i32, i32> for AddOne {
260 type Error = std::convert::Infallible;
261
262 async fn invoke(
263 &self,
264 input: i32,
265 _config: Option<RunnableConfig>,
266 ) -> Result<i32, Self::Error> {
267 Ok(input + 1)
268 }
269 }
270
271 struct I32ToString;
272
273 #[async_trait]
274 impl Runnable<i32, String> for I32ToString {
275 type Error = std::convert::Infallible;
276
277 async fn invoke(
278 &self,
279 input: i32,
280 _config: Option<RunnableConfig>,
281 ) -> Result<String, Self::Error> {
282 Ok(format!("value={}", input))
283 }
284 }
285
286 #[tokio::test]
287 async fn invoke_two_steps() {
288 let seq = RunnableSequence::from_pair(Double, AddOne);
290 let result = seq.invoke(5, None).await.unwrap();
291 assert_eq!(result, 11);
292 }
293
294 #[tokio::test]
295 async fn invoke_three_steps() {
296 let seq = RunnableSequence::from_pair(Double, AddOne).pipe(I32ToString);
298 let result = seq.invoke(3, None).await.unwrap();
299 assert_eq!(result, "value=7");
300 }
301
302 #[tokio::test]
303 async fn batch_works() {
304 let seq = RunnableSequence::from_pair(Double, AddOne);
305 let results = seq.batch(vec![1, 2, 3], None).await.unwrap();
306 assert_eq!(results, vec![3, 5, 7]);
307 }
308
309 #[tokio::test]
310 async fn stream_works() {
311 let seq = RunnableSequence::from_pair(Double, AddOne);
312 let mut stream = seq.stream(10, None).await.unwrap();
313 let result = stream.next().await.unwrap().unwrap();
314 assert_eq!(result, 21);
315 }
316
317 #[tokio::test]
318 async fn transform_works_elementwise() {
319 let seq = RunnableSequence::from_pair(Double, AddOne);
320 let input = Box::pin(futures_util::stream::iter(vec![
321 Ok(1i32),
322 Ok(2i32),
323 Ok(3i32),
324 ])) as Pin<Box<dyn Stream<Item = Result<i32, LcelError>> + Send>>;
325
326 let mut output = seq.transform(input, None).await.unwrap();
329 let mut results = Vec::new();
330 while let Some(item) = output.next().await {
331 results.push(item.unwrap());
332 }
333 assert_eq!(results, vec![3, 5, 7]);
334 }
335
336 struct StreamingTokenLLM;
340
341 #[async_trait]
342 impl Runnable<i32, i32> for StreamingTokenLLM {
343 type Error = std::convert::Infallible;
344
345 async fn invoke(
346 &self,
347 input: i32,
348 _config: Option<RunnableConfig>,
349 ) -> Result<i32, Self::Error> {
350 Ok(input * 1000)
351 }
352
353 async fn stream(
354 &self,
355 input: i32,
356 _config: Option<RunnableConfig>,
357 ) -> Result<Pin<Box<dyn Stream<Item = Result<i32, Self::Error>> + Send>>, Self::Error>
358 {
359 let stream =
360 futures_util::stream::iter(vec![Ok(input), Ok(input + 100), Ok(input + 200)]);
361 Ok(Box::pin(stream))
362 }
363 }
364
365 #[tokio::test]
366 async fn stream_uses_real_streaming_for_first_step() {
367 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(StreamingTokenLLM);
371 let mut stream = seq.stream(5, None).await.unwrap();
372 let mut results = Vec::new();
373 while let Some(item) = stream.next().await {
374 results.push(item.unwrap());
375 }
376 assert_eq!(results, vec![5, 105, 205]);
377 }
378
379 #[tokio::test]
380 async fn stream_chains_subsequent_steps_elementwise() {
381 let seq = RunnableSequence::from_pair(StreamingTokenLLM, AddOne);
384 let mut stream = seq.stream(5, None).await.unwrap();
385 let mut results = Vec::new();
386 while let Some(item) = stream.next().await {
387 results.push(item.unwrap());
388 }
389 assert_eq!(results, vec![6, 106, 206]);
390 }
391
392 #[tokio::test]
393 async fn from_single_works() {
394 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(Double);
395 let result = seq.invoke(4, None).await.unwrap();
396 assert_eq!(result, 8);
397 }
398
399 #[tokio::test]
400 async fn pipe_on_sequence_works() {
401 let seq = RunnableSequence::from_single(Double)
402 .pipe(AddOne)
403 .pipe(I32ToString);
404 let result = seq.invoke(5, None).await.unwrap();
405 assert_eq!(result, "value=11"); }
407
408 #[tokio::test]
409 async fn len_and_empty() {
410 let seq = RunnableSequence::from_pair(Double, AddOne);
411 assert_eq!(seq.len(), 2);
412 assert!(!seq.is_empty());
413 }
414}