LangGraph
LangGraph builds on langchain-openai’s ChatOpenAI, so the
integration shape is identical to LangChain — configure
the LLM with base_url + default_headers, then use it as any node’s
LLM.
Minimal example
import os
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
llm = ChatOpenAI(
base_url=os.environ.get("CM_GATEWAY_URL", "https://api.curate-me.ai/v1/openai"),
default_headers={"X-CM-API-Key": os.environ["CM_API_KEY"]},
model="gpt-4o",
)
def respond(state):
return {"messages": state["messages"] + [llm.invoke(state["messages"])]}
graph = StateGraph(dict)
graph.add_node("respond", respond)
graph.set_entry_point("respond")
graph.add_edge("respond", END)
app = graph.compile()Full single-node and multi-agent examples live at
examples/integrations/langgraph/.
What’s traced
Each node’s LLM call shows up as its own trace row in the dashboard,
keyed by X-CM-Request-Id. The trace carries the node name as a
custom attribute when you set tags=["node:respond"] on the LLM
invocation — this lets you slice cost by node.
Supported versions
| Library | Tested range |
|---|---|
langgraph | >=0.2.40,<0.4.0 |
langchain-openai | >=0.2.0,<0.4.0 |
Known limitations
- Checkpointer: the gateway only sees per-node LLM calls. State
snapshots persisted by
MemorySaver/PostgresSaver/SqliteSaverrun entirely on your infrastructure. We don’t yet store cross-step memory diffs. - Conditional routing: edges that don’t make LLM calls (pure-Python routers) don’t show up in the trace — that’s by design, the gateway is invoked only when an LLM call happens.
- Subgraphs: each subgraph node is a separate trace. Use the
W3C
traceparentheader on outer calls to link them.