NeoGraph 0.10.0
A C++17 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 BarrierSpecsbarrier_specs () const
 Read-only view of declared barriers (mainly for tests).
 
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.
 
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={})
 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 87 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/3]

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/3]

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 (matching the engine's historical last-writer-wins behavior under parallel dispatch; note this is intrinsically non-deterministic under Taskflow).
  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/3]

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.

Note: under the current implementation barrier state is per-run and in-memory only — a checkpoint/resume round trip drops all partial signals. See module docs for the follow-up schema-versioned persistence plan.

◆ 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. If the route key is missing from the routes map, the LAST entry (by map ordering) is used as a fallback — matching the pre-refactor engine behavior.

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: