1use async_trait::async_trait;
7use futures_util::Stream;
8use lc_core::runnables::{LcelError, Runnable, RunnableConfig};
9use std::pin::Pin;
10use std::sync::Arc;
11
12use crate::base::AgentExecutor;
13use crate::orchestration::{Orchestrator, RunContext};
14use crate::streaming::AgentStreamEvent;
15
16pub struct AgentRunnable {
25 executor: Arc<AgentExecutor>,
26}
27
28impl AgentRunnable {
29 pub fn new(executor: Arc<AgentExecutor>) -> Self {
31 Self { executor }
32 }
33}
34
35#[async_trait]
36impl Runnable<String, String> for AgentRunnable {
37 type Error = LcelError;
38
39 async fn invoke(
40 &self,
41 input: String,
42 config: Option<RunnableConfig>,
43 ) -> Result<String, LcelError> {
44 self.executor
46 .invoke_with_config(input, config)
47 .await
48 .map_err(|e| LcelError::Agent(e.to_string()))
49 }
50
51 async fn stream(
54 &self,
55 input: String,
56 _config: Option<RunnableConfig>,
57 ) -> Result<Pin<Box<dyn Stream<Item = Result<String, LcelError>> + Send>>, LcelError> {
58 use futures_util::StreamExt;
59
60 let event_stream = self.executor.stream(input);
61
62 let mapped = event_stream.filter_map(|event_result| async move {
64 match event_result {
65 Ok(event) => match event {
66 crate::streaming::AgentStreamEvent::FinalAnswer { content } => {
67 Some(Ok(content))
68 }
69 crate::streaming::AgentStreamEvent::Error { message } => {
70 Some(Err(LcelError::Agent(message)))
71 }
72 _ => None,
74 },
75 Err(e) => Some(Err(LcelError::Agent(e.to_string()))),
76 }
77 });
78
79 Ok(Box::pin(mapped))
80 }
81}
82
83pub struct AgentEventRunnable {
93 executor: Arc<AgentExecutor>,
94}
95
96impl AgentEventRunnable {
97 pub fn new(executor: Arc<AgentExecutor>) -> Self {
99 Self { executor }
100 }
101}
102
103#[async_trait]
104impl Runnable<String, AgentStreamEvent> for AgentEventRunnable {
105 type Error = LcelError;
106
107 async fn invoke(
108 &self,
109 input: String,
110 config: Option<RunnableConfig>,
111 ) -> Result<AgentStreamEvent, LcelError> {
112 let output = self
113 .executor
114 .invoke_with_config(input, config)
115 .await
116 .map_err(|e| LcelError::Agent(e.to_string()))?;
117 Ok(AgentStreamEvent::FinalAnswer { content: output })
118 }
119
120 async fn stream(
121 &self,
122 input: String,
123 _config: Option<RunnableConfig>,
124 ) -> Result<Pin<Box<dyn Stream<Item = Result<AgentStreamEvent, LcelError>> + Send>>, LcelError>
125 {
126 use futures_util::StreamExt;
127
128 let event_stream = self.executor.stream(input);
129 let mapped = event_stream
131 .map(|event_result| event_result.map_err(|e| LcelError::Agent(e.to_string())));
132 Ok(Box::pin(mapped))
133 }
134}
135
136pub struct OrchestratorRunnable<O: Orchestrator> {
142 orchestrator: O,
143}
144
145impl<O: Orchestrator> OrchestratorRunnable<O> {
146 pub fn new(orchestrator: O) -> Self {
148 Self { orchestrator }
149 }
150}
151
152#[async_trait]
153impl<O> Runnable<O::Input, O::Output> for OrchestratorRunnable<O>
154where
155 O: Orchestrator,
156 O::Input: Send + Sync + 'static,
157 O::Output: Send + Sync + 'static,
158{
159 type Error = LcelError;
160
161 async fn invoke(
162 &self,
163 input: O::Input,
164 config: Option<RunnableConfig>,
165 ) -> Result<O::Output, LcelError> {
166 let ctx = match &config {
167 Some(cfg) => RunContext::from_config(cfg),
168 None => RunContext::new_random(),
169 };
170 self.orchestrator
171 .run_with_context(input, &ctx)
172 .await
173 .map_err(|e| LcelError::Agent(e.to_string()))
174 }
175}
176
177impl From<crate::base::AgentError> for LcelError {
179 fn from(err: crate::base::AgentError) -> Self {
180 LcelError::Agent(err.to_string())
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::streaming::AgentStreamEvent;
188 use std::collections::HashMap;
189
190 #[test]
191 fn agent_error_into_lcel_error() {
192 let agent_err = crate::base::AgentError::MaxIterationsReached;
193 let lcel_err: LcelError = agent_err.into();
194 assert!(matches!(lcel_err, LcelError::Agent(_)));
195 assert!(lcel_err.to_string().contains("Max iterations"));
196 }
197
198 struct TestFinishAgent;
200
201 #[async_trait]
202 impl crate::BaseAgent for TestFinishAgent {
203 async fn plan(
204 &self,
205 _intermediate_steps: &[crate::types::AgentStep],
206 _inputs: &HashMap<String, String>,
207 _config: Option<&lc_core::runnables::RunnableConfig>,
208 ) -> Result<crate::types::AgentOutput, crate::base::AgentError> {
209 Ok(crate::types::AgentOutput::Finish(
210 crate::types::AgentFinish::new("answer".to_string(), String::new()),
211 ))
212 }
213 }
214
215 #[tokio::test]
218 async fn agent_event_runnable_preserves_all_events() {
219 use futures_util::StreamExt;
220
221 let executor = Arc::new(crate::base::AgentExecutor::new(
222 Arc::new(TestFinishAgent),
223 vec![],
224 ));
225 let runnable = AgentEventRunnable::new(executor);
226
227 let mut stream = runnable.stream("hi".to_string(), None).await.unwrap();
228 let mut events = Vec::new();
229 while let Some(item) = stream.next().await {
230 events.push(item.unwrap());
231 }
232
233 assert_eq!(events.len(), 2);
234 assert!(matches!(events[0], AgentStreamEvent::Text { .. }));
235 assert!(matches!(events[1], AgentStreamEvent::FinalAnswer { .. }));
236 }
237
238 #[tokio::test]
240 async fn agent_event_runnable_invoke_returns_final_answer() {
241 let executor = Arc::new(crate::base::AgentExecutor::new(
242 Arc::new(TestFinishAgent),
243 vec![],
244 ));
245 let runnable = AgentEventRunnable::new(executor);
246
247 let event = runnable.invoke("hi".to_string(), None).await.unwrap();
248 match event {
249 AgentStreamEvent::FinalAnswer { content } => assert_eq!(content, "answer"),
250 other => panic!("expected FinalAnswer, got {:?}", other),
251 }
252 }
253}