lc_langgraph/compiled/
stream.rs1use super::graph::CompiledGraph;
6use super::types::{GraphInvocation, StreamEvent};
7use crate::edge::GraphEdge;
8use crate::errors::{GraphError, GraphResult};
9use crate::graph::END;
10use crate::node::NodeConfig;
11use crate::state::StateSchema;
12use futures_util::future::join_all;
13use futures_util::Stream;
14use std::collections::HashMap;
15use std::pin::Pin;
16use tokio_stream::wrappers::ReceiverStream;
17
18impl<S: StateSchema + Send + Sync + 'static> CompiledGraph<S> {
19 pub fn stream(
27 &self,
28 input: S,
29 ) -> Pin<Box<dyn Stream<Item = Result<StreamEvent<S>, GraphError>> + Send>> {
30 let (tx, rx) = tokio::sync::mpsc::channel(64);
31
32 let graph = self.clone();
33 tokio::spawn(async move {
34 let mut state = input;
35 let mut current_node = graph.entry_point.clone();
36 let mut recursion_count = 0;
37
38 if tx
39 .send(Ok(StreamEvent::start(state.clone())))
40 .await
41 .is_err()
42 {
43 return;
44 }
45
46 if let Some(ref checkpointer) = graph.checkpointer {
47 match checkpointer.lock().await.save(&state, 0).await {
48 Ok(_) => {}
49 Err(e) => {
50 let _ = tx.send(Err(e)).await;
51 return;
52 }
53 }
54 }
55
56 while current_node != END && recursion_count < graph.recursion_limit {
57 if graph.interrupt_before.contains(¤t_node) {
58 let _ = tx
59 .send(Err(GraphError::ExecutionInterrupted(current_node.clone())))
60 .await;
61 return;
62 }
63
64 recursion_count += 1;
65
66 if tx
67 .send(Ok(StreamEvent::enter_node(
68 current_node.clone(),
69 state.clone(),
70 )))
71 .await
72 .is_err()
73 {
74 return;
75 }
76
77 let node = match graph.get_node(¤t_node).await {
78 Ok(n) => n,
79 Err(e) => {
80 let _ = tx.send(Err(e)).await;
81 return;
82 }
83 };
84
85 let config = NodeConfig {
86 recursion_limit: graph.recursion_limit,
87 debug: false,
88 metadata: HashMap::new(),
89 };
90
91 let update = match node.execute(&state, Some(config)).await {
92 Ok(u) => u,
93 Err(e) => {
94 let _ = tx.send(Err(e)).await;
95 return;
96 }
97 };
98
99 if tx
100 .send(Ok(StreamEvent::node_complete(
101 current_node.clone(),
102 update.clone(),
103 )))
104 .await
105 .is_err()
106 {
107 return;
108 }
109
110 if let Some(new_state) = update.update {
111 state = graph.default_reducer.reduce(&state, &new_state);
112 if tx
113 .send(Ok(StreamEvent::state_update(state.clone())))
114 .await
115 .is_err()
116 {
117 return;
118 }
119 }
120
121 if graph.interrupt_after.contains(¤t_node) {
122 if let Some(ref checkpointer) = graph.checkpointer {
123 let _ = checkpointer.lock().await.save(&state, recursion_count).await;
124 }
125 let _ = tx
126 .send(Err(GraphError::ExecutionInterrupted(format!(
127 "after_{}",
128 current_node
129 ))))
130 .await;
131 return;
132 }
133
134 let next_node = match graph.find_next_node(¤t_node, &state).await {
135 Ok(n) => n,
136 Err(e) => {
137 let _ = tx.send(Err(e)).await;
138 return;
139 }
140 };
141
142 if let Some(ref checkpointer) = graph.checkpointer {
143 let _ = checkpointer.lock().await.save(&state, recursion_count).await;
144 }
145
146 current_node = next_node;
147 }
148
149 if current_node != END {
152 let _ = tx
153 .send(Err(GraphError::RecursionLimitReached(
154 graph.recursion_limit,
155 )))
156 .await;
157 return;
158 }
159
160 let _ = tx.send(Ok(StreamEvent::end(state))).await;
161 });
162
163 Box::pin(ReceiverStream::new(rx))
164 }
165
166 pub async fn stream_collected(&self, input: S) -> GraphResult<Vec<StreamEvent<S>>> {
171 let mut events = Vec::new();
172 let mut stream = self.stream(input);
173 use futures_util::StreamExt;
174 while let Some(event) = stream.next().await {
175 events.push(event?);
176 }
177 Ok(events)
178 }
179
180 pub(super) async fn find_next_node(&self, current: &str, state: &S) -> GraphResult<String> {
181 'rt: {
183 let edge = {
184 let re = self.runtime_edges.read().await;
185 match re.iter().find(|e| e.source() == current) {
186 Some(e) => e.clone(),
187 None => break 'rt,
188 }
189 }; match edge {
191 GraphEdge::Fixed { target, .. } => return Ok(target),
192 GraphEdge::Conditional {
193 router_name,
194 targets,
195 default_target,
196 ..
197 } => {
198 let router = self
199 .conditional_routers
200 .get(&router_name)
201 .cloned()
202 .or_else(|| {
203 self.runtime_conditional_routers
204 .try_read()
205 .ok()
206 .and_then(|guard| guard.get(&router_name).cloned())
207 })
208 .ok_or_else(|| {
209 GraphError::ExecutionError(format!(
210 "Router '{}' not found (runtime)",
211 router_name
212 ))
213 })?;
214 let route_key = router.route(state).await?;
215 let target = targets
216 .get(&route_key)
217 .or(default_target.as_ref())
218 .ok_or_else(|| {
219 GraphError::RoutingError(format!(
220 "No target for route '{}' (runtime)",
221 route_key
222 ))
223 })?;
224 return Ok(target.clone());
225 }
226 GraphEdge::FanOut { targets, .. } => {
227 if targets.is_empty() {
228 return Err(GraphError::RoutingError(
229 "FanOut has no targets (runtime)".to_string(),
230 ));
231 }
232 return Ok(targets[0].clone());
233 }
234 GraphEdge::FanIn { .. } => {}
235 }
236 }
237
238 for edge in &self.edges {
240 if edge.source() == current {
241 match edge {
242 GraphEdge::Fixed { target, .. } => {
243 return Ok(target.clone());
244 }
245 GraphEdge::Conditional {
246 router_name,
247 targets,
248 default_target,
249 ..
250 } => {
251 let router = self
252 .conditional_routers
253 .get(router_name)
254 .cloned()
255 .or_else(|| {
256 self.runtime_conditional_routers
257 .try_read()
258 .ok()
259 .and_then(|guard| guard.get(router_name).cloned())
260 })
261 .ok_or_else(|| {
262 GraphError::ExecutionError(format!(
263 "Router '{}' not found",
264 router_name
265 ))
266 })?;
267
268 let route_key = router.route(state).await?;
269
270 let target = targets
271 .get(&route_key)
272 .or(default_target.as_ref())
273 .ok_or_else(|| {
274 GraphError::RoutingError(format!(
275 "No target for route '{}'",
276 route_key
277 ))
278 })?;
279
280 return Ok(target.clone());
281 }
282 GraphEdge::FanOut { targets, .. } => {
283 if targets.is_empty() {
284 return Err(GraphError::RoutingError(
285 "FanOut has no targets".to_string(),
286 ));
287 }
288 return Ok(targets[0].clone());
289 }
290 GraphEdge::FanIn { .. } => {
291 continue;
292 }
293 }
294 }
295 }
296
297 if current == self.entry_point && self.nodes.len() == 1 {
298 return Ok(END.to_string());
299 }
300
301 Err(GraphError::RoutingError(format!(
302 "No outgoing edge from node '{}'",
303 current
304 )))
305 }
306
307 pub(super) async fn find_fan_out_targets(&self, current: &str) -> Option<Vec<String>> {
308 {
309 let re = self.runtime_edges.read().await;
310 if let Some(GraphEdge::FanOut { targets, .. }) =
311 re.iter().find(|e| e.source() == current)
312 {
313 return Some(targets.clone());
314 }
315 }
316 for edge in &self.edges {
317 if edge.source() == current {
318 if let GraphEdge::FanOut { targets, .. } = edge {
319 return Some(targets.clone());
320 }
321 }
322 }
323 None
324 }
325
326 pub(super) async fn find_fan_in_target(&self, sources: &[String]) -> Option<String> {
327 {
328 let re = self.runtime_edges.read().await;
329 if let Some(GraphEdge::FanIn {
330 sources: edge_sources,
331 target,
332 }) = re.iter().find(|e| matches!(e, GraphEdge::FanIn { .. }))
333 {
334 if edge_sources.iter().all(|s| sources.contains(s)) {
335 return Some(target.clone());
336 }
337 }
338 }
339 for edge in &self.edges {
340 if let GraphEdge::FanIn {
341 sources: edge_sources,
342 target,
343 } = edge
344 {
345 if edge_sources.iter().all(|s| sources.contains(s)) {
346 return Some(target.clone());
347 }
348 }
349 }
350 None
351 }
352
353 pub(super) async fn execute_parallel_branches(
354 &self,
355 targets: &[String],
356 state: &S,
357 recursion_count: usize,
358 ) -> GraphResult<Vec<(String, GraphInvocation<S>)>> {
359 let futures: Vec<_> = targets
364 .iter()
365 .filter(|t| *t != END)
366 .map(|target| {
367 let target = target.clone();
368 let state_clone = state.clone();
369 async move {
370 let result = self
371 .invoke_from_node_with_count(target.clone(), state_clone, recursion_count)
372 .await;
373 result.map(|inv| (target, inv))
374 }
375 })
376 .collect();
377
378 let results = join_all(futures).await;
379
380 let mut successful = Vec::new();
381 for result in results {
382 match result {
383 Ok((name, inv)) => successful.push((name, inv)),
384 Err(e) => return Err(e),
385 }
386 }
387
388 Ok(successful)
389 }
390}