#[non_exhaustive]pub enum ExecutionPlan {
Sequence(Vec<ExecutionPlan>),
Parallel(Vec<ExecutionPlan>),
Execute {
node_id: NodeId,
},
Step {
node_id: NodeId,
handoffs: Vec<(NodeId, ExecutionPlan)>,
},
Loop {
node_id: NodeId,
body: Box<ExecutionPlan>,
max_iterations: Option<usize>,
until: LoopCondition,
carry_from: Option<NodeId>,
},
Branch {
node_id: NodeId,
arms: Vec<(String, ExecutionPlan)>,
},
Remote {
node_id: NodeId,
target: RemoteTarget,
plan: Box<ExecutionPlan>,
},
Composite {
node_ids: Vec<NodeId>,
},
Stream {
node_ids: Vec<NodeId>,
chunk_size: usize,
},
Empty,
}Expand description
A compiled execution plan produced by the compiler.
This is a recursive tree that the runtime walks to execute a pipeline. The compiler resolves caching, parallelism, and distribution before the runtime sees the plan.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Sequence(Vec<ExecutionPlan>)
Execute steps sequentially, one after another.
Parallel(Vec<ExecutionPlan>)
Execute branches concurrently (fork-join).
Execute
Execute a single filter node.
Fields
node_id: NodeIdThe graph node to execute.
Step
Run an effectful step to completion: poll, perform its effects,
repeat. Distinct from Execute because the runtime has to drive a
turn loop and journal what it performs, not call a function once.
Fields
node_id: NodeIdThe effectful node the runtime drives.
handoffs: Vec<(NodeId, ExecutionPlan)>Where this step may hand control, by target node id.
A handoff is a branch the step decides rather than a condition
value, so it compiles the same way: each target is claimed by the
step and appears exactly once, inside it. A Goto naming
something not listed here is an error, not a jump into the dark.
Loop
Iterate: run body until until says stop, or max_iterations is hit.
Fields
node_id: NodeIdThe loop controller node — the id events and assignments are
reported under, distinct from any node inside body.
body: Box<ExecutionPlan>The sub-plan executed once per iteration.
until: LoopConditionAlready resolved by the compiler — never BodyTerminal here.
The executor reads the signal from exactly this node.
carry_from: Option<NodeId>The node whose output each pass hands to the next one.
Separate from until on purpose: what a loop carries and what
tells it to stop are different questions. A debate that runs a
fixed number of rounds has no stop signal at all, but every round
still has to start from what the last one said — otherwise the
loop just repeats its first iteration.
None when the body has no single terminal to carry from.
Branch
Conditional branching: evaluate condition, pick an arm.
Fields
node_id: NodeIdThe node whose output selects an arm. The selector is control, not data: the chosen arm receives the branch’s input.
arms: Vec<(String, ExecutionPlan)>(label, sub-plan) per arm; the condition value picks by label.
Remote
Execute a sub-plan on a remote worker.
Fields
node_id: NodeIdThe node the distribution directive was attached to. The wrapped
plan names it again, which is why this wrapper contributes no
ids of its own to node_ids().
target: RemoteTargetWhere to run: a specific worker by id, or any worker with a tag.
plan: Box<ExecutionPlan>The sub-plan the remote worker executes.
Composite
Execute multiple differentiable nodes as a single block. The executor passes tensors directly between filters (no Value conversion), preserving PyTorch autograd for gradient flow.
Stream
Streaming execution: process input in chunks through a filter chain. Each filter’s StreamMode (FixedState/Evolving/Barrier) defines its per-chunk contract. Results flow progressively — no full materialization.
Fields
Empty
No-op: nothing to execute (e.g. empty graph).
Implementations§
Source§impl ExecutionPlan
impl ExecutionPlan
Sourcepub fn children(&self) -> impl Iterator<Item = (Option<&str>, &ExecutionPlan)>
pub fn children(&self) -> impl Iterator<Item = (Option<&str>, &ExecutionPlan)>
The sub-plans nested inside this one, each with its edge label if it has one — a branch arm’s label, a handoff’s target.
One structural walk, so the accessors below cannot disagree about
the shape of the tree. They used to: node_count skipped a step’s
handoffs while node_ids collected them, so an agentic plan
reported fewer nodes than it had.
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Count total nodes in the plan.
Sourcepub fn parallel_branch_count(&self) -> usize
pub fn parallel_branch_count(&self) -> usize
Count parallel branches at the top level of the plan.
Top level only, deliberately: this feeds a run’s summary, and a fan-out inside a loop body happens once per iteration rather than once per run.
Source§impl ExecutionPlan
impl ExecutionPlan
Sourcepub fn to_mermaid(&self) -> String
pub fn to_mermaid(&self) -> String
Render the execution plan as a Mermaid flowchart.
Sourcepub fn to_graph(&self) -> Graph
pub fn to_graph(&self) -> Graph
Synthesize a displayable Graph
from this plan — the same node synthesis as Self::to_mermaid
(fork nodes for Parallel, arm nodes for Branch, pills for
streams) — so every Graph renderer applies: to_svg(),
to_mermaid(), to_graphviz().
Trait Implementations§
Source§impl Clone for ExecutionPlan
impl Clone for ExecutionPlan
Source§fn clone(&self) -> ExecutionPlan
fn clone(&self) -> ExecutionPlan
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more