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