LangGraph Integration¶
A registered graph name becomes the OpenAI model value clients pass to
/v1/chat/completions.
Registration¶
GraphRegistry stores model names and GraphConfig values:
GraphRegistry(
registry={
"chat": GraphConfig(
graph=chat_graph,
description="General-purpose chat graph.",
streamable_node_names=["generate"],
),
"advanced-mcp-tools": GraphConfig(
graph=advanced_graph,
description="Chat graph with MCP tools.",
),
}
)
GraphConfig.graph can be a compiled graph, sync factory, or async factory.
Async factories support setup such as MCP-style tool loading before creating a
ReAct graph.
Adaptation¶
The default graph contract is:
- input:
{"messages": langchain_messages} - output: the last
AIMessageinresult["messages"]
Use request_to_input, context_factory, and output_to_message when the graph
has custom input, output, or context schemas. Those adapters keep the public HTTP
surface OpenAI-compatible while letting the graph stay idiomatic LangGraph.
Graphs that use LangGraph's MessagesState (or a messages channel reduced by
add_messages) explicitly decide which completed messages belong to their
conversation state. Shared-key subgraphs can update that channel directly;
private subgraphs must map selected results back in their wrapper. This state
choice does not, by itself, select live token streaming or concatenate several
messages into one OpenAI response.
Context Versus Config¶
LGOS keeps LangGraph's invocation channels separate:
graph.ainvoke( # use graph.astream(...) for streaming
graph_input,
context=runtime_context,
config=runnable_config,
)
graph_inputcontains mutable workflow state, including converted messages.runtime_contextstarts with optional validatedGraphConfig.client_settingssettings and can be composed with server-owned values byGraphConfig.context_factory(request, settings). Settings used directly must also be the graph'scontext_schema; every non-null factory result requires a context schema. LangGraph applies its native schema coercion when it runs the graph. Nodes receive the result asruntime.contexton an injectedRuntime[Context].runnable_configcontains callbacks and execution identity. For an interrupt-enabled graph, LGOS derivesconfig["configurable"]["thread_id"]from the server's checkpoint scope, registered model, and operation UUID. The UUID is generated by the server unless the initial request suppliesmetadata.langgraph_run_id. When callbacks are configured, LGOS also adds a stable run name and request/model/operation correlation metadata for tracing. LangGraph also propagates the primitive configurable checkpointthread_idto callbacks during interrupt-enabled execution.
Application settings that nodes consume belong in typed runtime context, not in
the configurable section of RunnableConfig. The internal checkpoint key is
different: the checkpointer needs it to restore state before node execution, so
it remains execution configuration. It is intentionally scoped by a
server-trusted value plus model and run; it is not derived from a UI conversation
ID.
Because LGOS supports Python 3.11 and newer, callback/config context propagates
automatically to nested async runnable calls. Node functions only need an
injected RunnableConfig when they inspect or modify execution configuration;
they do not need one solely to pass config to a nested model's ainvoke().
See Custom Graphs for a typed server-owned context example and Configure LangGraph Runtime Settings for public settings, discovery, and request handling. LangGraph's official runtime, streaming, and persistence documentation for the underlying conventions.
Runner Behavior¶
LGOS uses the LangGraph interface that matches the OpenAI response mode. Both paths use LangGraph's stable v2 output wrapper so interrupt handling remains durable.
When stream is omitted or false, the route awaits invoke_run(). The
runner calls graph.ainvoke(version="v2"). It does not subscribe to custom
events. After execution it reads durable pending state for interrupts;
otherwise it renders the returned value as the final AIMessage.
When stream=true, the route returns an SSE response backed by
stream_run(). The runner consumes messages, custom, and values. Only
AIMessageChunk values from configured streamable nodes become text chunks;
the list may include nodes in nested subgraphs. Returning a message through
the graph's messages state is not a live-streaming signal.
The chat service immediately maps explicitly public client_event() and
status_event() values into namespaced chunks when the request opts into v1
events. The final root value supplies durable citations, tool calls, and
provider-reported usage. After execution quiesces, it reads durable pending
state and renders a complete interrupt batch when present. Unknown custom
events stay private.
Internal model calls that must not reach delta.content use LangGraph's native
nostream tag. streamable_node_names selects calls whose text is intended for
the OpenAI assistant stream; the tag selects calls within those nodes. Graph
authors must follow the
assistant text parity contract
because a graph cannot retract an intermediate draft after it has streamed it.
Interrupt runs use exit durability and drain graph execution before exposing a
durable tool-call batch. A GraphConfig.run_coordinator lease covers state
inspection, validation, and execution; busy leases fail with HTTP 409. LGOS
retains only checkpoints it has exposed as an interrupt and otherwise performs
best-effort terminal cleanup. The complete lifecycle is specified in
OpenAI compatibility.
The graph therefore needs an async checkpointer implementing aget_tuple(),
alist(), aput(), aput_writes(), and adelete_thread(), plus a coordinator
shared by all workers. See
PostgreSQL Coordination for the
production adapter.
Test the interrupt contract before every LangGraph upgrade
Sequential interrupts can reuse their interrupt and checkpoint IDs, so the
opaque state_token fingerprints every checkpoint namespace and its
durable resume-channel generations.
Keep the sequential, parallel, nested, stale-resume, and restart tests as
an upgrade gate before widening the supported LangGraph range. See the
official
interrupt ordering rule.
Paused Runs Across Deployments¶
Paused state resumes against the graph implementation currently selected by the model. Drain, expire, migrate, or route pending operations before changing state schemas, pending nodes, or interrupt order. Keep the checkpoint scope stable when a release must resume old state; otherwise keep the old release routable. See LangGraph's official backward-compatibility contract.
Status updates use LangGraph's native
custom stream mode.
No middleware is needed. Graph code emits a status exactly where it has enough
application context to describe the long-running work in user-facing language.
See Custom Graphs for runnable examples.
Request Cancellation¶
For streaming chat completions (stream=true), LGOS ties graph iteration to the
HTTP response lifetime. A request-scoped FastAPI dependency owns the producer
task and memory channel behind StreamingResponse. When the client disconnects,
dependency cleanup cancels and awaits that producer, then closes the graph
iterator. This uses the normal OpenAI streaming connection; LGOS adds no custom
cancellation route, header, or SSE event.
Cancellation is cooperative
Asynchronous work stops at cancellation points. Blocking code may continue,
and a proxy that keeps consuming the upstream response also keeps the graph
alive. The upstream model provider decides whether closing its connection
stops remote generation or billing. This path does not cover stream=false
and does not create a durable, addressable cancellation record.