How the LangGraph StateGraph is Constructed in OpenMAIC's Director Graph
OpenMAIC constructs a LangGraph StateGraph in lib/orchestration/director-graph.ts that orchestrates multi-agent conversations through a typed state object, two pure-function nodes (director and agent generator), and conditional edges implementing a single-round execution contract.
OpenMAIC leverages LangGraph to manage complex multi-agent interactions through a deterministic, stateless orchestration pipeline. The core construction logic resides in the director-graph.ts module, where the StateGraph is defined, compiled, and exported for server-side execution.
Defining the Typed State with Annotation.Root
The foundation of the director graph is a strongly-typed state definition using LangGraph's Annotation.Root. Located at lines 48–77 in lib/orchestration/director-graph.ts, the OrchestratorState interface enumerates all fields required for conversation management:
- Immutable inputs: Messages, agent list, and the language model instance
- Mutable fields:
currentAgentId,turnCount,agentResponses,whiteboardLedger, and the termination flagshouldEnd
This annotation ensures that every node receives a consistent, typed state object while allowing specific fields to be updated and passed to subsequent nodes in the graph.
Implementing Graph Nodes
The StateGraph consists of two primary computational nodes implemented as pure functions that transform the state and emit Server-Sent Events (SSE).
Director Node Logic
The directorNode function (lines 91–223) serves as the orchestration brain. It determines which agent should speak next by handling three distinct cases:
- Single-agent fast-path: Immediately selects the sole available agent
- Multi-agent first-turn trigger: Invokes a specific agent for conversation initialization
- General LLM decision: Queries the language model to select the next speaker based on conversation context
Throughout execution, the node emits real-time SSE events (including thinking and cue_user types) and returns a partial state update containing the selected currentAgentId and the shouldEnd termination signal.
Agent Generation Node
The agentGenerateNode function (lines 331–667) executes the selected agent's response generation. This node:
- Streams text deltas and tool actions via SSE
- Records whiteboard actions to the ledger
- Updates mutable state fields including
turnCount,totalActions, andagentResponses
Controlling Execution Flow with Conditional Edges
After the director node completes, flow control passes to directorCondition (lines 124–127), a conditional edge function that inspects state.shouldEnd. The logic follows:
- If
shouldEndistrue, the graph transitions to theENDnode, terminating execution - Otherwise, the graph follows the
"agent_generate"edge to invoke the generator node
This conditional routing ensures the graph respects the single-round contract, preventing infinite loops within a single request.
Assembling the StateGraph Topology
The createOrchestrationGraph function (lines 71–95) wires the components into a compiled, executable graph. The topology implements a linear flow with a conditional branch:
export function createOrchestrationGraph() {
const graph = new StateGraph(OrchestratorState)
.addNode('director', directorNode) // ← director logic
.addNode('agent_generate', agentGenerateNode) // ← agent generation
.addEdge(START, 'director') // START → director
.addConditionalEdges('director', directorCondition, {
agent_generate: 'agent_generate',
[END]: END,
}) // director → (agent_generate | END)
.addEdge('agent_generate', END); // agent_generate → END
return graph.compile();
}
The Single-Round Contract Architecture
Unlike persistent conversation loops, OpenMAIC's director graph implements a single-round contract where each HTTP request flows through START → director → (agent_generate) → END. The client serializes multiple requests to achieve multi-turn dialogs, ensuring the server graph remains stateless and bounded. This design prevents resource exhaustion and simplifies horizontal scaling.
Initializing State for Stateless Execution
To support the stateless architecture, buildInitialState (lines 98–149) constructs a fresh OrchestratorState instance for every request. The function accepts a StatelessChatRequest and a LanguageModel, injecting request-scoped agent configuration overrides. This guarantees that no conversation state persists in memory between separate graph invocations.
Practical Implementation Examples
Creating the Orchestration Graph
import { createOrchestrationGraph } from '@/lib/orchestration/director-graph';
const graph = createOrchestrationGraph(); // compiled LangGraph ready for execution
Building the Initial State
import { buildInitialState } from '@/lib/orchestration/director-graph';
import { myLanguageModel } from '@/lib/ai-models';
const request = /* a StatelessChatRequest from the client */;
const initState = buildInitialState(request, myLanguageModel, /* optional ThinkingConfig */);
Executing the Graph with SSE Streaming
import { Runnable } from '@langchain/langgraph';
const runnable: Runnable = graph; // the compiled graph implements Runnable
const result = await runnable.invoke(initState, {
writer: (event) => sendSSE(event), // stream events to the client
});
The writer callback receives StatelessEvent objects (e.g., thinking, text_delta, action, cue_user) emitted by both nodes during execution.
Summary
- State definition: Uses
Annotation.Rootat lines 48–77 to create a typedOrchestratorStatewith immutable inputs and mutable conversation tracking fields. - Node architecture: Implements
directorNode(lines 91–223) for agent selection andagentGenerateNode(lines 331–667) for response generation, both emitting SSE events. - Flow control: Employs
directorCondition(lines 124–127) to conditionally route toENDor the agent generation node based onshouldEnd. - Topology:
createOrchestrationGraph(lines 71–95) assembles a linear StateGraph with a conditional branch, compiling to aRunnableinterface. - Statelessness:
buildInitialState(lines 98–149) ensures each request initializes fresh state, supporting horizontal scaling via the single-round contract.
Frequently Asked Questions
What is the single-round contract in OpenMAIC's director graph?
The single-round contract is an architectural constraint where the LangGraph StateGraph processes exactly one conversational turn per invocation, following the path START → director → (agent_generate) → END. The graph never loops internally; instead, the client maintains conversation history and re-invokes the graph for subsequent turns. This design ensures bounded execution time and complete statelessness on the server.
How does the director node determine which agent speaks next?
According to the source code in lib/orchestration/director-graph.ts (lines 91–223), the directorNode implements a three-tier decision hierarchy: it checks for a single-agent fast-path, handles multi-agent conversation initialization, or prompts the configured language model to analyze conversation context and select the next speaker. The node updates currentAgentId in state and emits thinking events via SSE during deliberation.
Where is the LangGraph StateGraph state defined in OpenMAIC?
The StateGraph state is defined in lib/orchestration/director-graph.ts starting at line 48, where OrchestratorState is declared using LangGraph's Annotation.Root utility. The definition includes fields for messages, agent lists, the language model reference, and mutable tracking fields like whiteboardLedger and turnCount that nodes update during execution.
How does OpenMAIC handle multi-turn conversations without graph loops?
OpenMAIC handles multi-turn conversations by treating each turn as an independent, stateless request. The buildInitialState function (lines 98–149) reconstructs the conversation context from the request payload for every invocation, and the compiled graph always terminates at END after one pass. The client application sequences multiple HTTP requests to create the illusion of a continuous dialog, while the server graph remains a single-round, deterministic processor.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →