1use async_trait::async_trait;
7use futures_util::{Stream, StreamExt};
8use lc_core::runnables::{LcelError, Runnable, RunnableConfig};
9use serde_json::Value;
10use std::collections::HashMap;
11use std::pin::Pin;
12use std::sync::Arc;
13
14use crate::base::BaseChain;
15
16pub struct ChainRunnable {
25 chain: Arc<dyn BaseChain>,
26}
27
28impl ChainRunnable {
29 pub fn new(chain: Arc<dyn BaseChain>) -> Self {
31 Self { chain }
32 }
33}
34
35#[async_trait]
36impl Runnable<HashMap<String, Value>, HashMap<String, Value>> for ChainRunnable {
37 type Error = LcelError;
38
39 async fn invoke(
40 &self,
41 input: HashMap<String, Value>,
42 _config: Option<RunnableConfig>,
43 ) -> Result<HashMap<String, Value>, LcelError> {
44 self.chain.invoke(input).await.map_err(|e| LcelError::Chain(e.to_string()))
45 }
46
47 async fn stream(
48 &self,
49 input: HashMap<String, Value>,
50 _config: Option<RunnableConfig>,
51 ) -> Result<Pin<Box<dyn Stream<Item = Result<HashMap<String, Value>, LcelError>> + Send>>, LcelError> {
52 let chain_stream = self.chain.stream(input).await.map_err(|e| LcelError::Stream(e.to_string()))?;
53
54 let mapped = chain_stream.map(|result| {
56 result
57 .map(|token| {
58 let mut map = HashMap::new();
59 map.insert("text".to_string(), Value::String(token.token));
60 map
61 })
62 .map_err(|e| LcelError::Stream(e.to_string()))
63 });
64
65 Ok(Box::pin(mapped))
66 }
67
68 }
70
71impl From<crate::base::ChainError> for LcelError {
73 fn from(err: crate::base::ChainError) -> Self {
74 LcelError::Chain(err.to_string())
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use lc_core::runnables::Runnable;
82
83 struct TestChain;
84
85 #[async_trait]
86 impl BaseChain for TestChain {
87 fn input_keys(&self) -> Vec<&str> {
88 vec!["input"]
89 }
90
91 fn output_keys(&self) -> Vec<&str> {
92 vec!["output"]
93 }
94
95 async fn invoke(
96 &self,
97 inputs: HashMap<String, Value>,
98 ) -> Result<HashMap<String, Value>, crate::base::ChainError> {
99 let input = inputs
100 .get("input")
101 .and_then(|v| v.as_str())
102 .unwrap_or("");
103 let mut result = HashMap::new();
104 result.insert("output".to_string(), Value::String(format!("echo: {}", input)));
105 Ok(result)
106 }
107 }
108
109 #[tokio::test]
110 async fn chain_runnable_invoke() {
111 let chain = ChainRunnable::new(Arc::new(TestChain));
112 let mut input = HashMap::new();
113 input.insert("input".to_string(), Value::String("hello".to_string()));
114 let result = chain.invoke(input, None).await.unwrap();
115 assert_eq!(result.get("output").unwrap(), &Value::String("echo: hello".to_string()));
116 }
117}