1use super::graph::CompiledGraph;
6use super::types::{GraphInvocation, ParallelBranch, 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 if current_node != crate::START {
69 recursion_count += 1;
70
71 if tx
72 .send(Ok(StreamEvent::enter_node(
73 current_node.clone(),
74 state.clone(),
75 )))
76 .await
77 .is_err()
78 {
79 return;
80 }
81
82 let node = match graph.get_node(¤t_node).await {
83 Ok(n) => n,
84 Err(e) => {
85 let _ = tx.send(Err(e)).await;
86 return;
87 }
88 };
89
90 let config = NodeConfig {
91 recursion_limit: graph.recursion_limit,
92 debug: false,
93 metadata: HashMap::new(),
94 };
95
96 let update = match node.execute(&state, Some(config)).await {
97 Ok(u) => u,
98 Err(e) => {
99 let _ = tx.send(Err(e)).await;
100 return;
101 }
102 };
103
104 if tx
105 .send(Ok(StreamEvent::node_complete(
106 current_node.clone(),
107 update.clone(),
108 )))
109 .await
110 .is_err()
111 {
112 return;
113 }
114
115 if let Some(new_state) = update.update {
116 state = graph.default_reducer.reduce(&state, &new_state);
117 if tx
118 .send(Ok(StreamEvent::state_update(state.clone())))
119 .await
120 .is_err()
121 {
122 return;
123 }
124 }
125
126 if graph.interrupt_after.contains(¤t_node) {
127 if let Some(ref checkpointer) = graph.checkpointer {
128 let _ = checkpointer
129 .lock()
130 .await
131 .save(&state, recursion_count)
132 .await;
133 }
134 let _ = tx
135 .send(Err(GraphError::ExecutionInterrupted(format!(
136 "after_{}",
137 current_node
138 ))))
139 .await;
140 return;
141 }
142 }
143
144 let fan_out_targets = graph.find_fan_out_targets(¤t_node).await;
148 if let Some(targets) = fan_out_targets {
149 recursion_count += 1;
150 let branch_results = match graph
151 .execute_parallel_branches(&targets, &state, recursion_count)
152 .await
153 {
154 Ok(r) => r,
155 Err(e) => {
156 let _ = tx.send(Err(e)).await;
157 return;
158 }
159 };
160 let mut parallel_branches: Vec<ParallelBranch<S>> = Vec::new();
161 for (name, inv) in branch_results {
162 parallel_branches.push(ParallelBranch {
163 name: name.clone(),
164 final_state: inv.final_state.clone(),
165 steps: inv.steps.clone(),
166 });
167 let _ = tx
168 .send(Ok(StreamEvent::node_complete(
169 name.clone(),
170 crate::state::StateUpdate {
171 update: Some(inv.final_state.clone()),
172 metadata: HashMap::new(),
173 },
174 )))
175 .await;
176 }
177 let base = state.clone();
178 state = match graph.merge_parallel_states(¶llel_branches, &base) {
179 Ok(s) => s,
180 Err(e) => {
181 let _ = tx.send(Err(e)).await;
182 return;
183 }
184 };
185 if tx
186 .send(Ok(StreamEvent::state_update(state.clone())))
187 .await
188 .is_err()
189 {
190 return;
191 }
192 current_node = match graph.find_fan_in_target(&targets).await {
193 Some(merge_node) => merge_node,
194 None => END.to_string(),
195 };
196 if let Some(ref checkpointer) = graph.checkpointer {
197 let _ = checkpointer
198 .lock()
199 .await
200 .save(&state, recursion_count)
201 .await;
202 }
203 continue;
204 }
205
206 let next_node = match graph.find_next_node(¤t_node, &state).await {
207 Ok(n) => n,
208 Err(e) => {
209 let _ = tx.send(Err(e)).await;
210 return;
211 }
212 };
213
214 if let Some(ref checkpointer) = graph.checkpointer {
215 let _ = checkpointer
216 .lock()
217 .await
218 .save(&state, recursion_count)
219 .await;
220 }
221
222 current_node = next_node;
223 }
224
225 if current_node != END {
228 let _ = tx
229 .send(Err(GraphError::RecursionLimitReached(
230 graph.recursion_limit,
231 )))
232 .await;
233 return;
234 }
235
236 let _ = tx.send(Ok(StreamEvent::end(state))).await;
237 });
238
239 Box::pin(ReceiverStream::new(rx))
240 }
241
242 pub async fn stream_collected(&self, input: S) -> GraphResult<Vec<StreamEvent<S>>> {
247 let mut events = Vec::new();
248 let mut stream = self.stream(input);
249 use futures_util::StreamExt;
250 while let Some(event) = stream.next().await {
251 events.push(event?);
252 }
253 Ok(events)
254 }
255
256 pub(super) async fn find_next_node(&self, current: &str, state: &S) -> GraphResult<String> {
257 'rt: {
259 let edge = {
260 let re = self.runtime_edges.read().await;
261 match re.iter().find(|e| e.source() == current) {
262 Some(e) => e.clone(),
263 None => break 'rt,
264 }
265 }; match edge {
267 GraphEdge::Fixed { target, .. } => return Ok(target),
268 GraphEdge::Conditional {
269 router_name,
270 targets,
271 default_target,
272 ..
273 } => {
274 let router = self
275 .conditional_routers
276 .get(&router_name)
277 .cloned()
278 .or_else(|| {
279 self.runtime_conditional_routers
280 .try_read()
281 .ok()
282 .and_then(|guard| guard.get(&router_name).cloned())
283 })
284 .ok_or_else(|| {
285 GraphError::ExecutionError(format!(
286 "Router '{}' not found (runtime)",
287 router_name
288 ))
289 })?;
290 let route_key = router.route(state).await?;
291 let target = targets
292 .get(&route_key)
293 .or(default_target.as_ref())
294 .ok_or_else(|| {
295 GraphError::RoutingError(format!(
296 "No target for route '{}' (runtime)",
297 route_key
298 ))
299 })?;
300 return Ok(target.clone());
301 }
302 GraphEdge::FanOut { targets, .. } => {
303 if targets.is_empty() {
304 return Err(GraphError::RoutingError(
305 "FanOut has no targets (runtime)".to_string(),
306 ));
307 }
308 return Ok(targets[0].clone());
309 }
310 GraphEdge::FanIn { .. } => {}
311 }
312 }
313
314 for edge in &self.edges {
316 if edge.source() == current {
317 match edge {
318 GraphEdge::Fixed { target, .. } => {
319 return Ok(target.clone());
320 }
321 GraphEdge::Conditional {
322 router_name,
323 targets,
324 default_target,
325 ..
326 } => {
327 let router = self
328 .conditional_routers
329 .get(router_name)
330 .cloned()
331 .or_else(|| {
332 self.runtime_conditional_routers
333 .try_read()
334 .ok()
335 .and_then(|guard| guard.get(router_name).cloned())
336 })
337 .ok_or_else(|| {
338 GraphError::ExecutionError(format!(
339 "Router '{}' not found",
340 router_name
341 ))
342 })?;
343
344 let route_key = router.route(state).await?;
345
346 let target = targets
347 .get(&route_key)
348 .or(default_target.as_ref())
349 .ok_or_else(|| {
350 GraphError::RoutingError(format!(
351 "No target for route '{}'",
352 route_key
353 ))
354 })?;
355
356 return Ok(target.clone());
357 }
358 GraphEdge::FanOut { targets, .. } => {
359 if targets.is_empty() {
360 return Err(GraphError::RoutingError(
361 "FanOut has no targets".to_string(),
362 ));
363 }
364 return Ok(targets[0].clone());
365 }
366 GraphEdge::FanIn { .. } => {
367 continue;
368 }
369 }
370 }
371 }
372
373 if current == self.entry_point && self.nodes.len() == 1 {
374 return Ok(END.to_string());
375 }
376
377 if self.is_fan_in_source(current).await {
380 return Ok(END.to_string());
381 }
382
383 Err(GraphError::RoutingError(format!(
384 "No outgoing edge from node '{}'",
385 current
386 )))
387 }
388
389 pub(super) async fn is_fan_in_source(&self, node: &str) -> bool {
391 {
392 let re = self.runtime_edges.read().await;
393 if re.iter().any(|e| {
394 matches!(e, GraphEdge::FanIn { sources, .. } if sources.iter().any(|s| s == node))
395 }) {
396 return true;
397 }
398 }
399 self.edges.iter().any(
400 |e| matches!(e, GraphEdge::FanIn { sources, .. } if sources.iter().any(|s| s == node)),
401 )
402 }
403
404 pub(super) async fn find_fan_out_targets(&self, current: &str) -> Option<Vec<String>> {
405 {
406 let re = self.runtime_edges.read().await;
407 if let Some(GraphEdge::FanOut { targets, .. }) =
408 re.iter().find(|e| e.source() == current)
409 {
410 return Some(targets.clone());
411 }
412 }
413 for edge in &self.edges {
414 if edge.source() == current {
415 if let GraphEdge::FanOut { targets, .. } = edge {
416 return Some(targets.clone());
417 }
418 }
419 }
420 None
421 }
422
423 pub(super) async fn find_fan_in_target(&self, sources: &[String]) -> Option<String> {
424 {
425 let re = self.runtime_edges.read().await;
426 if let Some(GraphEdge::FanIn {
427 sources: edge_sources,
428 target,
429 }) = re.iter().find(|e| matches!(e, GraphEdge::FanIn { .. }))
430 {
431 if edge_sources.iter().all(|s| sources.contains(s)) {
432 return Some(target.clone());
433 }
434 }
435 }
436 for edge in &self.edges {
437 if let GraphEdge::FanIn {
438 sources: edge_sources,
439 target,
440 } = edge
441 {
442 if edge_sources.iter().all(|s| sources.contains(s)) {
443 return Some(target.clone());
444 }
445 }
446 }
447 None
448 }
449
450 pub(super) async fn execute_parallel_branches(
451 &self,
452 targets: &[String],
453 state: &S,
454 recursion_count: usize,
455 ) -> GraphResult<Vec<(String, GraphInvocation<S>)>> {
456 let futures: Vec<_> = targets
461 .iter()
462 .filter(|t| *t != END)
463 .map(|target| {
464 let target = target.clone();
465 let state_clone = state.clone();
466 async move {
467 let result = self
468 .invoke_from_node_with_count(target.clone(), state_clone, recursion_count)
469 .await;
470 result.map(|inv| (target, inv))
471 }
472 })
473 .collect();
474
475 let results = join_all(futures).await;
476
477 let mut successful = Vec::new();
478 for result in results {
479 match result {
480 Ok((name, inv)) => successful.push((name, inv)),
481 Err(e) => return Err(e),
482 }
483 }
484
485 Ok(successful)
486 }
487}