NeMo Relay LangGraph Integration

View as Markdown

Use the nemo_relay.integrations.langgraph package to add NeMo Relay observability to LangGraph workflows through public LangGraph APIs.

Setup

Install the LangGraph integration extra in your application environment.

$uv add "nemo-relay[langgraph]"

Installing the langgraph extra also installs the LangChain integration dependencies.

Usage Example

1from typing_extensions import TypedDict
2
3import nemo_relay
4from langgraph.graph import END, START, StateGraph
5from nemo_relay.integrations.langgraph import NemoRelayCallbackHandler
6
7class State(TypedDict):
8 value: int
9
10def increment(state: State) -> State:
11 return {"value": state["value"] + 1}
12
13builder = StateGraph(State)
14builder.add_node("increment", increment)
15builder.add_edge(START, "increment")
16builder.add_edge("increment", END)
17
18graph = builder.compile()
19
20with nemo_relay.scope.scope("langgraph-request", nemo_relay.ScopeType.Agent):
21 result = graph.invoke(
22 {"value": 1},
23 config={"callbacks": [NemoRelayCallbackHandler()]},
24 )
25
26print(result)

Managed ToolNode Calls

For a standalone LangGraph ToolNode, construct the node with create_tool_node. It routes each model-requested tool call through Relay’s managed execution pipeline while preserving LangGraph state, store, and runtime argument injection.

1from langchain_core.messages import AIMessage
2from langchain_core.tools import tool
3from langgraph.graph import END, START, MessagesState, StateGraph
4from nemo_relay.integrations.langgraph import (
5 NemoRelayCallbackHandler,
6 create_tool_node,
7)
8
9@tool
10def get_weather(location: str) -> str:
11 """Return the weather for a location."""
12 return f"Sunny in {location}"
13
14builder = StateGraph(MessagesState)
15builder.add_node("tools", create_tool_node([get_weather]))
16builder.add_edge(START, "tools")
17builder.add_edge("tools", END)
18graph = builder.compile()
19
20with nemo_relay.scope.scope("langgraph-request", nemo_relay.ScopeType.Agent):
21 result = graph.invoke(
22 {
23 "messages": [
24 AIMessage(
25 content="",
26 tool_calls=[{"name": "get_weather", "args": {"location": "Boston"}, "id": "call-1"}],
27 )
28 ]
29 },
30 config={"callbacks": [NemoRelayCallbackHandler()]},
31 )

NemoRelayCallbackHandler records the graph lifecycle and provides its scopes. create_tool_node provides managed tool execution; use both for complete standalone LangGraph instrumentation. For custom ToolNode wrapper composition, construct ToolNode directly and pass wrap_tool_call and awrap_tool_call from this integration.

For LangChain agents inside a LangGraph workflow, use NemoRelayMiddleware from this package the same way as the LangChain integration and pass the LangGraph config into the nested agent call:

1from langchain.agents import create_agent
2from langchain_core.runnables import RunnableConfig
3from nemo_relay.integrations.langgraph import NemoRelayMiddleware
4
5agent = create_agent(
6 model="nvidia:nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
7 tools=[],
8 middleware=[NemoRelayMiddleware()],
9)
10
11def agent_node(state: dict, config: RunnableConfig) -> dict:
12 return agent.invoke({"messages": state["messages"]}, config=config)

Install the NVIDIA LangChain provider if you want to run the nested agent example as written:

$uv add "nemo-relay[langgraph,langchain-nvidia]"

Verify the Integration

The integration works correctly when:

  • graph.invoke(...) returns the incremented state from the example.
  • The langgraph-request scope contains the LangGraph run.
  • Nested LangChain agents inherit the same callback config when you pass the LangGraph config through.

Observability

Refer to Observability for details on exporting NeMo Relay observability data to third-party systems.