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(
167 &self,
168 input: I,
169 config: Option<RunnableConfig>,
170 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send>>, LcelError> {
171 let input_stream: Pin<
173 Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>,
174 > = Box::pin(futures_util::stream::once(async {
175 Ok(Box::new(input) as Box<dyn Any + Send>)
176 }));
177
178 let mut current_stream = input_stream;
179
180 for step in &self.steps {
182 current_stream = step.transform_any(current_stream, config.clone()).await?;
183 }
184
185 let output_stream = current_stream.map(|result| {
187 result.and_then(|boxed| {
188 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
189 LcelError::TypeMismatch(format!(
190 "stream final downcast: expected {}",
191 std::any::type_name::<O>()
192 ))
193 })
194 })
195 });
196
197 Ok(Box::pin(output_stream))
198 }
199
200 async fn transform(
203 &self,
204 input: Pin<Box<dyn Stream<Item = Result<I, LcelError>> + Send>>,
205 config: Option<RunnableConfig>,
206 ) -> Result<Pin<Box<dyn Stream<Item = Result<O, LcelError>> + Send>>, LcelError> {
207 let mut current_stream: Pin<
209 Box<dyn Stream<Item = Result<Box<dyn Any + Send>, LcelError>> + Send>,
210 > = Box::pin(input.map(|result| result.map(|item| Box::new(item) as Box<dyn Any + Send>)));
211
212 for step in &self.steps {
214 current_stream = step.transform_any(current_stream, config.clone()).await?;
215 }
216
217 let output_stream = current_stream.map(|result| {
219 result.and_then(|boxed| {
220 boxed.downcast::<O>().map(|b| *b).map_err(|_| {
221 LcelError::TypeMismatch(format!(
222 "transform final downcast: expected {}",
223 std::any::type_name::<O>()
224 ))
225 })
226 })
227 });
228
229 Ok(Box::pin(output_stream))
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use futures_util::StreamExt;
237
238 struct Double;
239
240 #[async_trait]
241 impl Runnable<i32, i32> for Double {
242 type Error = std::convert::Infallible;
243
244 async fn invoke(
245 &self,
246 input: i32,
247 _config: Option<RunnableConfig>,
248 ) -> 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(
260 &self,
261 input: i32,
262 _config: Option<RunnableConfig>,
263 ) -> Result<i32, Self::Error> {
264 Ok(input + 1)
265 }
266 }
267
268 struct I32ToString;
269
270 #[async_trait]
271 impl Runnable<i32, String> for I32ToString {
272 type Error = std::convert::Infallible;
273
274 async fn invoke(
275 &self,
276 input: i32,
277 _config: Option<RunnableConfig>,
278 ) -> Result<String, Self::Error> {
279 Ok(format!("value={}", input))
280 }
281 }
282
283 #[tokio::test]
284 async fn invoke_two_steps() {
285 let seq = RunnableSequence::from_pair(Double, AddOne);
287 let result = seq.invoke(5, None).await.unwrap();
288 assert_eq!(result, 11);
289 }
290
291 #[tokio::test]
292 async fn invoke_three_steps() {
293 let seq = RunnableSequence::from_pair(Double, AddOne).pipe(I32ToString);
295 let result = seq.invoke(3, None).await.unwrap();
296 assert_eq!(result, "value=7");
297 }
298
299 #[tokio::test]
300 async fn batch_works() {
301 let seq = RunnableSequence::from_pair(Double, AddOne);
302 let results = seq.batch(vec![1, 2, 3], None).await.unwrap();
303 assert_eq!(results, vec![3, 5, 7]);
304 }
305
306 #[tokio::test]
307 async fn stream_works() {
308 let seq = RunnableSequence::from_pair(Double, AddOne);
309 let mut stream = seq.stream(10, None).await.unwrap();
310 let result = stream.next().await.unwrap().unwrap();
311 assert_eq!(result, 21);
312 }
313
314 #[tokio::test]
315 async fn transform_works() {
316 let seq = RunnableSequence::from_pair(Double, AddOne);
317 let input = Box::pin(futures_util::stream::iter(vec![
318 Ok(1i32),
319 Ok(2i32),
320 Ok(3i32),
321 ])) as Pin<Box<dyn Stream<Item = Result<i32, LcelError>> + Send>>;
322
323 let mut output = seq.transform(input, None).await.unwrap();
324 let result = output.next().await.unwrap().unwrap();
326 assert_eq!(result, 7);
327 }
328
329 #[tokio::test]
330 async fn from_single_works() {
331 let seq: RunnableSequence<i32, i32> = RunnableSequence::from_single(Double);
332 let result = seq.invoke(4, None).await.unwrap();
333 assert_eq!(result, 8);
334 }
335
336 #[tokio::test]
337 async fn pipe_on_sequence_works() {
338 let seq = RunnableSequence::from_single(Double)
339 .pipe(AddOne)
340 .pipe(I32ToString);
341 let result = seq.invoke(5, None).await.unwrap();
342 assert_eq!(result, "value=11"); }
344
345 #[tokio::test]
346 async fn len_and_empty() {
347 let seq = RunnableSequence::from_pair(Double, AddOne);
348 assert_eq!(seq.len(), 2);
349 assert!(!seq.is_empty());
350 }
351}