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