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