Skip to content

#327: Visualise the Graph in LangGraph

The more complex our control flow in our LangGraph application, the harder it is to understand what is going on. Luckily for us, we have multiple ways to visualise our graphs. Let us find out how we can do that.

The example application

We reuse our branching example from last week's post, which looked like this:

import random
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END

# 1. Define the State
class State(TypedDict):
    number: int
    result_message: str

# 2. Define the Nodes
def generator_node(state: State):
    """Generates a random number."""
    num = random.randint(1, 100)
    print(f"--- GENERATED: {num} ---")
    return {"number": num}

def even_processor(state: State):
    """Logic specifically for even numbers."""
    return {"result_message": f"You entered {state['number']}, it is even."}

def odd_processor(state: State):
    """Logic specifically for odd numbers."""
    return {"result_message": f"You entered {state['number']}, it is odd."}

# 3. Define the Routing Logic
def route_decision(state: State) -> Literal["even_path", "odd_path"]:
    """Determines which node to visit next."""
    if state["number"] % 2 == 0:
        return "even_path"
    return "odd_path"

# 4. Build the Graph
workflow = StateGraph(State)

# Add Nodes
workflow.add_node("generator", generator_node)
workflow.add_node("even_node", even_processor)
workflow.add_node("odd_node", odd_processor)

# Define Edges
workflow.add_edge(START, "generator")

# Add Conditional Edges
# Arguments: (Source Node, Routing Function, Mapping of function output to Node names)
workflow.add_conditional_edges(
    "generator",
    route_decision,
    {
        "even_path": "even_node",
        "odd_path": "odd_node"
    }
)

# Connect branch ends to the END
workflow.add_edge("even_node", END)
workflow.add_edge("odd_node", END)

# Compile and Run
app = workflow.compile()

# --- ADD visualisation here

final_output = app.invoke({"number": 0, "result_message": ""})

print(f"FINAL RESULT: {final_output['result_message']}")

Generate a PNG with Mermaid

The simplest way I found is to use Mermaid.js to create PNG files. We do not need to install any additional packages and can visualise our graph with this code snipped that we insert after line 61:

1
2
3
png_bytes = app.get_graph().draw_mermaid_png()
with open("branching.png", "wb") as f:
    f.write(png_bytes)

This gives us a PNG file with the visualisation you can find in the post from last week:

We get a Mermaid.js generated PNG file.

Install Grandalf

For most other visualisations, we need to install Grandalf first:

uv pip install grandalf

After a successful installation, we can proceed and try the other visualisation approaches.

Generate Mermaid.js instructions

If we want to get the raw definition for Mermaid.js, we can use Grandalf and add this code to our application:

mermaid_str = app.get_graph().draw_mermaid()
print(mermaid_str)

This gives us the instructions for Mermaid.js:

---
config:
  flowchart:
    curve: linear
---
graph TD;
        __start__([<p>__start__</p>]):::first
        generator(generator)
        even_node(even_node)
        odd_node(odd_node)
        __end__([<p>__end__</p>]):::last
        __start__ --> generator;
        generator -. &nbsp;even_path&nbsp; .-> even_node;
        generator -. &nbsp;odd_path&nbsp; .-> odd_node;
        even_node --> __end__;
        odd_node --> __end__;
        classDef default fill:#f2f0ff,line-height:1.2
        classDef first fill-opacity:0
        classDef last fill:#bfb6fc

We can take this output and go to Mermaid.ai/live and create our visualisation:

The rendered Mermaid.js instructions look a bit nicer than the PNG.

Generate ASCII

With Grandalf in place, we can use this method to render our graph to ASCII:

ascii = app.get_graph(xray=True).print_ascii()
print(ascii)

This gives us this representation of our graph:

            +-----------+
            | __start__ |
            +-----------+
                  *
                  *
                  *
            +-----------+
            | generator |
            +-----------+
           ...         ...
          .               .
        ..                 ..
+-----------+           +----------+
| even_node |           | odd_node |
+-----------+           +----------+
           ***         ***
              *       *
               **   **
             +---------+
             | __end__ |
             +---------+

Jupyter Notebook and Mermaid PNG

When we run our application inside a Jupyter Notebook, we can create a cell to render the Mermaid.js PNG with this content:

from IPython.display import Image, display
display(Image(app.get_graph().draw_mermaid_png()))

When we run the cell, it renders our graph:

We can see the visualisation of our graph directly inside the notebook.

Jupyter Notebook and Mermaid instructions

If we want to render the Mermaid.js instructions, we first need to install this package:

uv pip install mermaid-py

This allows us then to create a cell with this code:

1
2
3
4
from mermaid import Mermaid

mermaid_str = app.get_graph().draw_mermaid()
Mermaid(mermaid_str)

If we run the cell, it takes a lot longer to render but we end up with the same visualisation: Our graph is now rendered with Mermaid.js in a Jupyter Notebook.

Next

With these different approaches to visualise our graphs we should be able to find something that suits our needs. It is much simpler to spot an error in our graph when we have a visualisation to work with. Next week we continue our journey with LangGraph and build our own tools.