1use super::graph::CompiledGraph;
5use super::types::{ExecutionStep, GraphExecution, GraphInvocation, ParallelBranch};
6use crate::errors::{GraphError, GraphResult};
7use crate::node::{NodeConfig, INTERRUPT_RESUME_KEY};
8use crate::state::{StateSchema, StateUpdate};
9use crate::END;
10use std::collections::HashMap;
11
12impl<S: StateSchema> CompiledGraph<S> {
13 async fn run_node(
18 &self,
19 state: &S,
20 current_node: &str,
21 recursion_count: usize,
22 resume: Option<&serde_json::Value>,
23 ) -> GraphResult<StateUpdate<S>> {
24 let node = self.get_node(current_node).await?;
25 let mut metadata = HashMap::new();
26 if let Some(value) = resume {
27 metadata.insert(INTERRUPT_RESUME_KEY.to_string(), value.clone());
28 }
29 let config = NodeConfig {
30 recursion_limit: self.recursion_limit,
31 debug: false,
32 metadata,
33 };
34 match node.execute(state, Some(config)).await {
35 Err(GraphError::InterruptRequest { ref payload }) => {
36 if let Some(ref cp) = self.checkpointer {
37 cp.lock().await.save(state, recursion_count).await?;
38 }
39 Err(GraphError::DynamicInterrupt {
40 node: current_node.to_string(),
41 payload: payload.clone(),
42 })
43 }
44 other => other,
45 }
46 }
47 pub async fn invoke(&self, input: S) -> GraphResult<GraphInvocation<S>> {
49 let mut state = input;
50 let mut current_node = self.entry_point.clone();
51 let mut steps: Vec<ExecutionStep> = Vec::new();
52 let mut recursion_count = 0;
53
54 if let Some(ref checkpointer) = self.checkpointer {
55 let checkpoint_id = checkpointer.lock().await.save(&state, 0).await?;
56 steps.push(ExecutionStep::checkpoint(
57 checkpoint_id,
58 current_node.clone(),
59 ));
60 }
61
62 loop {
63 if current_node == END {
64 break;
65 }
66
67 if recursion_count >= self.recursion_limit {
72 return Err(GraphError::RecursionLimitReached(self.recursion_limit));
73 }
74
75 if self.interrupt_before.contains(¤t_node) {
76 return Err(GraphError::ExecutionInterrupted(current_node.clone()));
77 }
78
79 if current_node != crate::START {
83 recursion_count += 1;
84
85 let update = self
86 .run_node(&state, ¤t_node, recursion_count, None)
87 .await?;
88
89 if let Some(new_state) = update.update {
90 state = self.default_reducer.reduce(&state, &new_state);
91 }
92
93 steps.push(ExecutionStep::node(
94 current_node.clone(),
95 update.metadata.clone(),
96 ));
97
98 if self.interrupt_after.contains(¤t_node) {
99 return Err(GraphError::ExecutionInterrupted(format!(
100 "after_{}",
101 current_node
102 )));
103 }
104 }
105
106 let fan_out_targets = self.find_fan_out_targets(¤t_node).await;
109 if let Some(targets) = fan_out_targets {
110 recursion_count += 1;
111 let mut parallel_branches: Vec<ParallelBranch<S>> = Vec::new();
112
113 let branch_results = self
116 .execute_parallel_branches(&targets, &state, recursion_count)
117 .await?;
118 for (name, inv) in branch_results {
119 parallel_branches.push(ParallelBranch {
120 name: name.clone(),
121 final_state: inv.final_state.clone(),
122 steps: inv.steps.clone(),
123 });
124 steps.push(ExecutionStep::ParallelNode {
125 branch: name,
126 metadata: HashMap::new(),
127 });
128 }
129
130 let merge_target = self.find_fan_in_target(&targets).await;
131 let base = state.clone();
133 if let Some(merge_node) = merge_target {
134 state = self.merge_parallel_states(¶llel_branches, &base)?;
135 current_node = merge_node;
136 } else {
137 state = self.merge_parallel_states(¶llel_branches, &base)?;
138 current_node = END.to_string();
139 }
140
141 if let Some(ref checkpointer) = self.checkpointer {
142 let checkpoint_id = checkpointer
143 .lock()
144 .await
145 .save(&state, recursion_count)
146 .await?;
147 steps.push(ExecutionStep::checkpoint(
148 checkpoint_id,
149 current_node.clone(),
150 ));
151 }
152 continue;
153 }
154
155 let next_node = self.find_next_node(¤t_node, &state).await?;
156
157 if let Some(ref checkpointer) = self.checkpointer {
158 let checkpoint_id = checkpointer
159 .lock()
160 .await
161 .save(&state, recursion_count)
162 .await?;
163 steps.push(ExecutionStep::checkpoint(checkpoint_id, next_node.clone()));
164 }
165
166 current_node = next_node;
167 }
168
169 Ok(GraphInvocation {
170 final_state: state,
171 steps,
172 recursion_count,
173 })
174 }
175
176 pub async fn invoke_with_execution(
178 &self,
179 execution: GraphExecution<S>,
180 ) -> GraphResult<GraphInvocation<S>> {
181 let mut state = execution.state;
182 let rerun_interrupted = execution.pending_interrupt.is_some()
186 || !execution.interrupted_at.starts_with("after_");
187 let mut current_node = if rerun_interrupted {
188 execution.current_node
189 } else {
190 self.find_next_node(&execution.current_node, &state).await?
191 };
192 let mut steps = execution.steps;
193 let mut recursion_count = execution.recursion_count;
194 let mut resume_value = execution.pending_interrupt.map(|p| p.value);
195 let first_node = current_node.clone();
196
197 loop {
198 if current_node == END {
199 break;
200 }
201
202 if recursion_count >= self.recursion_limit {
204 return Err(GraphError::RecursionLimitReached(self.recursion_limit));
205 }
206
207 if current_node != first_node && self.interrupt_before.contains(¤t_node) {
208 return Err(GraphError::ExecutionInterrupted(current_node.clone()));
209 }
210
211 recursion_count += 1;
212
213 let inject = resume_value.take();
215 let update = self
216 .run_node(&state, ¤t_node, recursion_count, inject.as_ref())
217 .await?;
218
219 if let Some(new_state) = update.update {
220 state = self.default_reducer.reduce(&state, &new_state);
221 }
222
223 steps.push(ExecutionStep::node(
224 current_node.clone(),
225 update.metadata.clone(),
226 ));
227
228 if self.interrupt_after.contains(¤t_node) {
229 return Err(GraphError::ExecutionInterrupted(format!(
230 "after_{}",
231 current_node
232 )));
233 }
234
235 let next_node = self.find_next_node(¤t_node, &state).await?;
236
237 if let Some(ref checkpointer) = self.checkpointer {
238 let checkpoint_id = checkpointer
239 .lock()
240 .await
241 .save(&state, recursion_count)
242 .await?;
243 steps.push(ExecutionStep::checkpoint(checkpoint_id, next_node.clone()));
244 }
245
246 current_node = next_node;
247 }
248
249 Ok(GraphInvocation {
250 final_state: state,
251 steps,
252 recursion_count,
253 })
254 }
255
256 pub async fn resume(&self, execution: GraphExecution<S>) -> GraphResult<GraphInvocation<S>> {
258 self.invoke_with_execution(execution).await
259 }
260
261 pub async fn invoke_from_node(
263 &self,
264 start_node: String,
265 input: S,
266 ) -> GraphResult<GraphInvocation<S>> {
267 self.invoke_from_node_with_count(start_node, input, 0).await
268 }
269
270 pub(super) async fn invoke_from_node_with_count(
278 &self,
279 start_node: String,
280 input: S,
281 mut recursion_count: usize,
282 ) -> GraphResult<GraphInvocation<S>> {
283 let mut state = input;
284 let mut current_node = start_node;
285 let mut steps: Vec<ExecutionStep> = Vec::new();
286
287 loop {
288 if current_node == END {
289 break;
290 }
291
292 if recursion_count >= self.recursion_limit {
293 return Err(GraphError::RecursionLimitReached(self.recursion_limit));
294 }
295
296 if self.interrupt_before.contains(¤t_node) {
297 return Err(GraphError::ExecutionInterrupted(current_node.clone()));
298 }
299
300 recursion_count += 1;
301
302 let update = self
303 .run_node(&state, ¤t_node, recursion_count, None)
304 .await?;
305
306 if let Some(new_state) = update.update {
307 state = self.default_reducer.reduce(&state, &new_state);
308 }
309
310 steps.push(ExecutionStep::node(
311 current_node.clone(),
312 update.metadata.clone(),
313 ));
314
315 if self.interrupt_after.contains(¤t_node) {
316 return Err(GraphError::ExecutionInterrupted(format!(
317 "after_{}",
318 current_node
319 )));
320 }
321
322 current_node = self.find_next_node(¤t_node, &state).await?;
323 }
324
325 Ok(GraphInvocation {
326 final_state: state,
327 steps,
328 recursion_count,
329 })
330 }
331}