Debug LangGraph Workflow Execution and View State Transitions in Open‑Notebook
You can debug LangGraph workflows in Open‑Notebook by inspecting checkpointed states via graph.get_state(), stepping through nodes manually, and querying the SQLite persistence layer to view every state transition.
Open‑Notebook is an open‑source knowledge management system that leverages LangGraph to power conversational AI workflows. When building complex multi‑step pipelines like the ask and chat graphs, knowing how to debug LangGraph workflow execution and view state transitions is critical for verifying conditional routing and ensuring data integrity between asynchronous nodes.
How LangGraph Structures Workflows in Open‑Notebook
StateGraph Compilation and Typed State
In open_notebook/graphs/ask.py, each workflow begins as a StateGraph that declares typed state structures using TypedDict models. These structures define the exact schema of data flowing between nodes. The graph is compiled into a callable object via agent_state.compile(), which enables asynchronous execution with automatic checkpointing.
The Ask Workflow Architecture
The ask workflow implements a complex routing pattern defined in open_notebook/graphs/ask.py:
- The agent node (implemented by
call_model_with_messages) parses user input into aStrategyobject containing search terms and instructions. - Conditional edges route to
trigger_queries, which createsSendobjects for each search term, dynamically spawning parallel paths. - The provide_answer node executes vector searches via
vector_searchand synthesizes responses using an LLM. - write_final_answer formats the final output before reaching
END.
This state‑machine architecture means you can pause execution after any node to inspect the exact values being passed downstream.
Inspecting State Transitions and Checkpointed Data
Retrieving the Full State Snapshot
LangGraph persists state after each node execution in a SQLite checkpoint. To inspect the complete state history for a given thread, use the get_state method on the compiled graph:
import asyncio
from langchain_core.runnables import RunnableConfig
from open_notebook.graphs.ask import graph
async def dump_state(thread_id: str):
config = RunnableConfig(configurable={"thread_id": thread_id})
# Returns a StateSnapshot containing values and next node
state = await asyncio.to_thread(graph.get_state, config=config)
print("Current values:", state.values)
print("Next node to execute:", state.next)
The graph.get_state method returns a StateSnapshot with values (the current TypedDict) and next (the pending node name), allowing you to debug LangGraph workflow execution by viewing exact data at any transition point.
Querying Message Counts and Session Data
For quick diagnostics without manually parsing the checkpoint, use the utility in open_notebook/utils/graph_utils.py:
from open_notebook.utils.graph_utils import get_session_message_count
async def check_session(thread_id: str):
count = await get_session_message_count(graph, thread_id)
print(f"Messages stored for {thread_id}: {count}")
This helper demonstrates the standard pattern for querying SQLite checkpoints using the thread‑id scoping mechanism.
Step‑by‑Step Debugging Techniques
Manual Node Execution
To debug individual nodes without running the full graph, compile the graph without external checkpointers and invoke stepwise:
import asyncio
from langchain_core.runnables import RunnableConfig
from open_notebook.graphs.ask import agent_state
# In-memory compilation for isolated debugging
debug_graph = agent_state.compile()
async def step_through():
cfg = RunnableConfig(configurable={"thread_id": "debug-1"})
# Execute only the agent node
state = await debug_graph.ainvoke(
{"question": "How do conditional edges work?"},
config=cfg
)
print("Agent output:", state)
# Manually process subsequent sends if present
for send in state.get("__next_edges__", []):
sub_state = await debug_graph.ainvoke(send["payload"], config=cfg)
print(f"After {send['node']}: {sub_state}")
asyncio.run(step_through())
This technique lets you pause execution after any node to verify intermediate state dictionaries before LangGraph routes to the next step.
Common Debugging Patterns
When tracing issues in open_notebook/graphs/ask.py:
- Missing results in
provide_answer: Insert print statements before the LLM call (around lines 100‑110) to inspect raw vector search outputs. - Unexpected model selection: Verify
configurable["answer_model"]in yourRunnableConfig; override with{"configurable": {"answer_model": "gpt-4"}}to force a specific model. - Conditional edge failures: Log the list of
Sendobjects insidetrigger_queriesto confirm the routing logic produced the expected destinations.
Configuration and Persistence Details
SQLite Checkpoint Storage
Open‑Notebook configures persistence in open_notebook/config.py via the LANGGRAPH_CHECKPOINT_FILE variable, which defaults to a SQLite database in the repository root. This SqliteSaver checkpointer enables:
- Cross‑API call persistence using
thread_idscoping viaRunnableConfig. - Direct SQL inspection using any SQLite browser to view raw state rows.
- State pruning via
graph.checkpointer.wipe(thread_id)for long‑running sessions.
The checkpoint file stores serialized StateSnapshot rows, making it possible to audit every state transition offline.
Summary
- Open‑Notebook defines LangGraph workflows in
open_notebook/graphs/ask.pyandopen_notebook/graphs/chat.pyusingStateGraphwith typed states. - Use
graph.get_state(config)to retrieve full checkpoint snapshots and debug LangGraph workflow execution. - Step through nodes manually by compiling with
agent_state.compile()and inspecting__next_edges__. - Query session metrics via
get_session_message_count()inopen_notebook/utils/graph_utils.py. - State persists to SQLite via
LANGGRAPH_CHECKPOINT_FILE, enabling offline inspection of every transition.
Frequently Asked Questions
How do I view the intermediate state between nodes in a LangGraph workflow?
Call graph.get_state() with a RunnableConfig containing the target thread_id. This returns a StateSnapshot object with values (the current data) and next (the upcoming node), allowing you to inspect exactly what data is being passed between steps in open_notebook/graphs/ask.py.
Why is my conditional edge not triggering the expected next node?
Conditional edges in open_notebook/graphs/ask.py return Send objects that determine routing. Add logging inside the trigger_queries function to verify the list of Send objects being returned, and ensure your state contains the required keys (like search_terms) that the edge logic depends on.
Can I debug LangGraph workflows without using the SQLite checkpoint?
Yes. Compile the graph using agent_state.compile() without passing a checkpointer to create an in‑memory instance. This allows you to invoke ainvoke() repeatedly and inspect state dictionaries without persisting data to the LANGGRAPH_CHECKPOINT_FILE defined in open_notebook/config.py.
Where is conversation history stored when debugging the chat workflow?
The chat workflow in open_notebook/graphs/chat.py uses the same checkpoint mechanism as ask. Conversation messages are stored in the SQLite file specified by LANGGRAPH_CHECKPOINT_FILE, scoped to the thread_id in your RunnableConfig. Use get_session_message_count() to query the number of stored messages for a specific session.
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 →