NeoGraph 0.16.0
A C++20 Graph Agent Engine Library — LangGraph for C++
Loading...
Searching...
No Matches
neograph::graph::Scheduler Class Reference

Immutable routing oracle for a compiled graph. More...

#include <scheduler.h>

Public Member Functions

const BarrierSpecs & barrier_specs () const
 Read-only view of declared barriers (mainly for tests).
 
bool ends_implicitly (const std::string &node) const
 True when node has neither a conditional edge nor a static edge, so it reaches END_NODE only implicitly.
 
const std::string * failure_target (const std::string &node) const
 The failure target of node, or null when it has no failure edge.
 
const JoinSpecs & join_specs () const
 Read-only view of declared any_of joins (#363).
 
NextStepPlan plan_next_step (const std::vector< std::string > &just_ran, const std::vector< NodeResult > &results, const GraphState &state) const
 Convenience overload: pair up ready[i] with results[i].
 
NextStepPlan plan_next_step (const std::vector< std::string > &just_ran, const std::vector< NodeResult > &results, const GraphState &state, BarrierState &barrier_state) const
 NodeResult-taking barrier-aware overload.
 
NextStepPlan plan_next_step (const std::vector< StepRouting > &routings, const GraphState &state) const
 Decide the next ready set from this super-step's routing.
 
NextStepPlan plan_next_step (const std::vector< StepRouting > &routings, const GraphState &state, BarrierState &barrier_state) const
 Barrier-aware planning.
 
NextStepPlan plan_next_step (const std::vector< StepRouting > &routings, const GraphState &state, BarrierState &barrier_state, const std::vector< bool > &failed) const
 Barrier-aware planning with handled failures (#364).
 
std::vector< std::string > plan_start_step () const
 Nodes directly routed from __start__ at step 0.
 
std::vector< std::string > resolve_next_nodes (const std::string &current, const GraphState &state) const
 Resolve the direct successors of a node.
 
 Scheduler (const std::vector< Edge > &edges, const std::vector< ConditionalEdge > &conditional_edges, BarrierSpecs barrier_specs, JoinSpecs join_specs, std::map< std::string, std::string > failure_targets, std::shared_ptr< const GraphRegistry > registry)
 Also bind failure edges (#364): failing node -> failure target.
 
 Scheduler (const std::vector< Edge > &edges, const std::vector< ConditionalEdge > &conditional_edges, BarrierSpecs barrier_specs, JoinSpecs join_specs, std::shared_ptr< const GraphRegistry > registry)
 Bind routing with legacy barriers and any_of joins (#363).
 
 Scheduler (const std::vector< Edge > &edges, const std::vector< ConditionalEdge > &conditional_edges, BarrierSpecs barrier_specs, std::shared_ptr< const GraphRegistry > registry)
 Bind routing to a per-engine registry overlay.
 
 Scheduler (const std::vector< Edge > &edges, const std::vector< ConditionalEdge > &conditional_edges, BarrierSpecs barrier_specs={})
 Bind the scheduler to the graph's edge topology.
 

Detailed Description

Immutable routing oracle for a compiled graph.

Scheduler is constructed once per engine compile and shared across all runs on that engine. It reads (but never mutates) the edge vectors passed at construction, so it is inherently thread-safe for concurrent plan_next_step / resolve_next_nodes calls — provided the caller-supplied GraphState is read-thread-safe (which GraphState guarantees via its shared_mutex).

Definition at line 114 of file scheduler.h.

Constructor & Destructor Documentation

◆ Scheduler()

neograph::graph::Scheduler::Scheduler ( const std::vector< Edge > &  edges,
const std::vector< ConditionalEdge > &  conditional_edges,
BarrierSpecs  barrier_specs = {} 
)

Bind the scheduler to the graph's edge topology.

Parameters
edgesRegular (unconditional) edges.
conditional_edgesEdges that route on a runtime condition.
barrier_specsOptional declaration of barrier nodes and their required upstream signal sets. Copied by value so caller ownership is simple.

edges and conditional_edges must out-live the Scheduler — typical usage has GraphEngine own them and pass references.

Member Function Documentation

◆ plan_next_step() [1/4]

NextStepPlan neograph::graph::Scheduler::plan_next_step ( const std::vector< std::string > &  just_ran,
const std::vector< NodeResult > &  results,
const GraphState &  state 
) const

Convenience overload: pair up ready[i] with results[i].

The engine's super-step guarantees results is pushed in the same order as just_ran in both the single- and parallel-node paths. This overload bakes that pairing — plus the Command.goto_node extraction — into the scheduler so the engine doesn't have to restate the invariant at the call site (where forgetting to zip correctly is the kind of mistake today's multi-node checkpoint bug came from).

just_ran and results must be the same size; mismatched lengths throw std::invalid_argument rather than silently truncating.

Parameters
just_ranNode names executed in this super-step, in order.
resultsNodeResults parallel to just_ran.
stateState snapshot for conditional-edge evaluation.

◆ plan_next_step() [2/4]

NextStepPlan neograph::graph::Scheduler::plan_next_step ( const std::vector< StepRouting > &  routings,
const GraphState &  state 
) const

Decide the next ready set from this super-step's routing.

Rules:

  1. If any StepRouting carries a command_goto, that node wins — ALL regular edge routing from this super-step is discarded. When multiple Commands fire in the same super-step the LAST iteration's value wins. Parallel branch completion order is not a stable routing contract, so graphs should not emit competing Commands in one super-step.
  2. Otherwise, union resolve_next_nodes(n, state) for every n in routings. Any END_NODE occurrence trips hit_end and is excluded from ready.
  3. Deduplicate via std::set so parallel fan-in (two upstream nodes routing to the same downstream in the same super-step) executes that downstream exactly once.

Note: this does NOT implement an implicit AND-join. A downstream that is signalled across super-steps (asymmetric path lengths) will fire once per reaching super-step — that is the documented contract of signal dispatch.

Parameters
routingsSignals from nodes that just executed.
stateState snapshot to evaluate conditional edges against.

◆ plan_next_step() [3/4]

NextStepPlan neograph::graph::Scheduler::plan_next_step ( const std::vector< StepRouting > &  routings,
const GraphState &  state,
BarrierState &  barrier_state 
) const

Barrier-aware planning.

Identical to the 2-arg form, except any candidate listed in barrier_specs_ is gated on accumulated upstream signals. When a candidate b would normally be added to ready, the scheduler:

  1. Records every upstream node currently signaling b into barrier_state[b].
  2. If barrier_state[b] ⊇ barrier_specs_[b], the barrier has all required upstream signals → it fires this super-step (added to ready) and barrier_state[b] is CLEARED so the next round starts fresh (important for graphs that loop back through the barrier).
  3. Otherwise, the candidate is DEFERRED (not added to ready; signals remain in barrier_state[b] for the next call).

Engines call this with a persistent barrier_state owned across super-steps. Ephemeral callers (unit tests, single-shot routing decisions) can use the 2-arg overload, which ignores barrier_specs_ entirely — there's no memory to gate against.

GraphEngine stores this state in checkpoint schema v2. A checkpoint or interrupt/resume round trip therefore preserves partial signals; the barrier clears only after all required upstreams have arrived and it fires.

◆ plan_next_step() [4/4]

NextStepPlan neograph::graph::Scheduler::plan_next_step ( const std::vector< StepRouting > &  routings,
const GraphState &  state,
BarrierState &  barrier_state,
const std::vector< bool > &  failed 
) const

Barrier-aware planning with handled failures (#364).

failed[i] marks routings[i] as a handled failure: that node routes to its failure target only, and its edges and routes are not signaled. A failure target is never __end__ and never a join, so a failure arrival never fills a slot. Everything else is the barrier-aware rule above; in particular a Command.goto in the same super-step preempts every route, failure routes included. failed must be empty or have one entry per routing; a failed node without a failure edge throws std::logic_error.

◆ plan_start_step()

std::vector< std::string > neograph::graph::Scheduler::plan_start_step ( ) const

Nodes directly routed from __start__ at step 0.

Returns
All e.to for edges with from == START_NODE, excluding any that target END_NODE.

◆ resolve_next_nodes()

std::vector< std::string > neograph::graph::Scheduler::resolve_next_nodes ( const std::string &  current,
const GraphState &  state 
) const

Resolve the direct successors of a node.

If a conditional edge originates from current, the condition is evaluated against state and the matching route is returned as a single-element vector. An open condition with no exact match uses the reserved default route when present. Otherwise resolution throws with the source node, condition, and returned label. Closed conditions always throw when their implementation returns an undeclared label. Schema-v0 graphs retain their historical fallback target internally.

Otherwise, every regular edge with from == current contributes its to to the result. If no edge matches at all, {END_NODE} is returned so callers can uniformly check for termination.

Parameters
currentNode whose successors to resolve.
stateSnapshot used to evaluate any conditional-edge predicate originating from current.
Returns
Vector of direct successor node names, or {END_NODE}.

The documentation for this class was generated from the following file: