MCPcopy Index your code
hub / github.com/AI45Lab/Code / execute_steps_parallel_resumable

Function execute_steps_parallel_resumable

core/src/orchestration/combinators.rs:102–215  ·  view source on GitHub ↗

Like [`execute_steps_parallel`](super::execute_steps_parallel), but resumable**: progress is journaled to `store` under `workflow_id`, so an interrupted run picks up from the last completed step. On entry, any steps already recorded in a prior checkpoint are skipped and their cached outcomes reused; only the rest are dispatched. As each step completes, the checkpoint is rewritten (the step bounda

(
    executor: Arc<dyn AgentExecutor>,
    specs: Vec<AgentStepSpec>,
    workflow_id: &str,
    store: Arc<dyn SessionStore>,
    event_tx: Option<broadcast::Sender<AgentEvent>>,
)

Source from the content-addressed store, hash-verified

100/// checkpoint is deleted (the workflow is terminal); only a crash leaves one
101/// behind for resume.
102pub async fn execute_steps_parallel_resumable(
103 executor: Arc<dyn AgentExecutor>,
104 specs: Vec<AgentStepSpec>,
105 workflow_id: &str,
106 store: Arc<dyn SessionStore>,
107 event_tx: Option<broadcast::Sender<AgentEvent>>,
108) -> Vec<StepOutcome> {
109 // Prior progress. An unreadable checkpoint — e.g. one written by a newer,
110 // incompatible schema version, which the store rejects via
111 // `ensure_loadable` — is treated as *no* prior progress: the workflow
112 // re-runs from scratch rather than resuming from state it can't interpret.
113 // That's a fail-safe (do the work), but surface it rather than swallow it.
114 let done: HashMap<String, StepOutcome> = match store.load_workflow_checkpoint(workflow_id).await
115 {
116 Ok(Some(cp)) => cp.completed(),
117 Ok(None) => HashMap::new(),
118 Err(e) => {
119 tracing::warn!(
120 workflow_id = %workflow_id,
121 error = %e,
122 "workflow checkpoint unreadable; re-running the workflow from scratch"
123 );
124 HashMap::new()
125 }
126 };
127
128 let pending: Vec<AgentStepSpec> = specs
129 .iter()
130 .filter(|s| !done.contains_key(&s.task_id))
131 .cloned()
132 .collect();
133 let labels: Vec<(String, String)> = pending
134 .iter()
135 .map(|s| (s.task_id.clone(), s.agent.clone()))
136 .collect();
137
138 // Accumulator seeded with prior progress; persisted at every step boundary.
139 let acc = Arc::new(tokio::sync::Mutex::new(done.clone()));
140 let limit = executor.concurrency_hint();
141 let workflow_id_owned = workflow_id.to_string();
142 let store_steps = Arc::clone(&store);
143
144 let results = run_ordered_parallel_with_limit(pending, limit, move |_idx, spec| {
145 let executor = Arc::clone(&executor);
146 let event_tx = event_tx.clone();
147 let acc = Arc::clone(&acc);
148 let store = Arc::clone(&store_steps);
149 let workflow_id = workflow_id_owned.clone();
150 async move {
151 let outcome = executor.execute_step(spec, event_tx).await;
152 // Step boundary: record only *successful* steps, so a failed step
153 // is retried on resume (its effect didn't complete) while a
154 // succeeded step's work is never redone.
155 if outcome.success {
156 let mut guard = acc.lock().await;
157 guard.insert(outcome.task_id.clone(), outcome.clone());
158 let checkpoint =
159 WorkflowCheckpoint::from_completed(&workflow_id, &guard, now_epoch_ms());

Calls 13

completedMethod · 0.80
removeMethod · 0.80
allMethod · 0.80
now_epoch_msFunction · 0.70
cloneMethod · 0.45
concurrency_hintMethod · 0.45
execute_stepMethod · 0.45
insertMethod · 0.45
getMethod · 0.45