Skip to main content

7.5 YAML → graph-flow compiler

// app/src/engine/compiler.rs
use std::time::Duration;
use graph_flow::{Graph, GraphBuilder};
use crate::{definitions::WorkflowDef, kinds::KindRegistry, platform::Platform};

pub fn compile(def: &WorkflowDef, kinds: &KindRegistry, p: &Platform) -> anyhow::Result<Graph> {
let mut b = GraphBuilder::new(def.id.clone());

// graph-flow uses the first added task as the start task, so add `entry` first (source-verified).
let mut nodes: Vec<_> = def.nodes.iter().collect();
nodes.sort_by_key(|n| n.id != def.entry);
for n in nodes {
let factory = kinds.get(&n.kind).ok_or_else(|| {
anyhow::anyhow!("workflow `{}` node `{}`: unknown kind `{}` (known: {:?})", def.id, n.id, n.kind, kinds.names())
})?;
let task = factory(n, p).map_err(|e| anyhow::anyhow!("workflow `{}` node `{}`: {e}", def.id, n.id))?;
b = b.add_task(task);
// TODO(impl): wrap `task` in a RetryTask when n.retry is set; wrap in an AuditTask for the audit log + OTel span.
}

for e in &def.edges {
b = match (&e.when, &e.otherwise) {
(Some(cond), Some(no)) => {
let cond = cond.clone();
b.add_conditional_edge(e.from.clone(), move |ctx| cond.eval(ctx), e.to.clone(), no.clone()) // source-verified signature
}
(Some(_), None) => anyhow::bail!("workflow `{}`: edge from `{}` has `when` without `else`", def.id, e.from),
_ => b.add_edge(e.from.clone(), e.to.clone()),
};
}

Ok(b.with_task_timeout(Duration::from_secs(def.limits.task_timeout_sec))
.with_max_execution_steps(def.limits.max_steps)
.build()?)
}