GONATURELLE for LangGraph
Capture node-by-node trajectories and state updates from LangGraph StateGraph applications, then assert on transitions, tool calls and checkpointed state.
from langgraph.checkpoint.memory import InMemorySaver
from gonaturelle import EvalSuite, TrajectoryTester
from gonaturelle.integrations.langgraph import LangGraphAdapter
from app.graph import builder # a StateGraph(BankingState) with nodes and edges
graph = builder.compile(checkpointer=InMemorySaver())
agent = LangGraphAdapter(
graph,
agent='servicing-graph',
input_key='messages',
stream_mode=['updates', 'messages'],
)
def test_servicing_graph():
suite = EvalSuite.load('fintech/servicing-balances')
results = suite.evaluate(agent=agent, max_concurrency=8)
t = TrajectoryTester(results)
t.assert_order('verify_identity', 'fetch_account_balance')
t.assert_state_before('identity_verified', True, node='fetch_account_balance')
t.assert_never_calls('transfer_funds')
t.assert_no_cycles(max_repeats=2)LangGraph models an agent as a StateGraph: typed state, nodes that return partial updates, and edges, including conditional edges, that decide what runs next. That structure is exactly what regulated teams need to test, because the dangerous failures are transitions: a graph that routes to the payment node before the verification node, or a reducer that overwrites a consent flag set two steps earlier.
The GONATURELLE adapter consumes the graph's own stream. When a suite runs, it calls graph.stream with stream_mode set to updates (and messages, if you want token-level model output), so every node execution yields the node name and the exact state delta it produced. Tool calls made through ToolNode or a prebuilt ReAct agent are expanded into individual tool steps with their arguments and results. If the graph is compiled with a checkpointer, GONATURELLE also records the thread's checkpoint ids so a failing case can be replayed from the step where it went wrong.
Nothing about the graph changes. You compile it as you do today, pass the compiled graph to the adapter, and write assertions in terms of node names and state keys. Assertions such as assert_state_before let you say that state["identity_verified"] must be true before the node that reads balances ever runs, which a final-answer comparison can never see. Tested against langgraph 1.x.
What you can test with LangGraph.
Transition assertions on node order
assert_order works on node names as well as tool names, so you can require that verify_identity runs before any node that touches account data, on every conditional path the graph can take.
State delta checks
Every update is stored as a diff against the previous state. assert_state_before and assert_state_never check flags, amounts and status fields as they change, not only at the end of the run.
Interrupt and human-in-the-loop coverage
Graphs that call interrupt() for human approval are run with scripted resume values from the case, so suites test both the approve and reject branches of an approval step deterministically.
Replay from a checkpoint
With a checkpointer configured, a failed case links to its thread and checkpoint id. Engineers resume the graph from the failing step locally instead of re-running the whole conversation.
Set up in 5 steps.
Most teams have a first suite running against LangGraph in under an hour, then add the CI gate.
Get an API key- 01
Install gonaturelle alongside langgraph and your model provider package in the environment where the graph is defined.
- 02
Export GONATURELLE_API_KEY and, for local runs, compile the graph with an in-memory checkpointer such as InMemorySaver so failing threads can be replayed.
- 03
Wrap the compiled graph with LangGraphAdapter, telling it which state key holds the input messages and which holds the final response.
- 04
Load a suite and call evaluate with the adapter as the agent; each case runs on a fresh thread_id derived from the case id.
- 05
Add node-order and state assertions, then commit the test and enable the GitHub Actions, GitLab CI or CircleCI gate.
LangGraph integration FAQ
Do we have to use a checkpointer?
No. Without one, GONATURELLE still captures every node update from the stream. A checkpointer adds replay from the failing step, which is worth enabling for local debugging even if production uses a different persistence setup.
Does it support subgraphs?
Yes. The adapter streams with subgraphs enabled, so updates from nested graphs carry their namespace. Assertions can target a node inside a subgraph with a path such as underwriting:score_application.
Does it work with the prebuilt ReAct agent?
Yes. create_react_agent and LangChain's create_agent both return compiled graphs, so they plug into the same adapter, and their tool calls are expanded into individual tool steps.
Related integrations
LangChain Agent frameworkCallback handler that records every tool call, retriever hit and LLM step from agents and runnables.
LlamaIndex Agent frameworkInstrumentation for AgentWorkflow and FunctionAgent runs, with retrieval nodes attached to each step.
CrewAI Agent frameworkPer-agent and per-task trajectories for Crew runs, so hand-offs between roles are assertable.
AutoGen Agent frameworkMessage-level capture for AgentChat teams, including tool call requests and execution results.
Stop testing regulated agents on generic prompts.
Run your agent against 100 industry-standard clinical or financial edge cases in 5 minutes. 14-day free production trial, no card.