GraphLoupe GraphLoupe docs GitHub ↗

GraphLoupe documentation

A self-hosted, node-level LangGraph debugger for VS Code. This page is the copy-paste guide: how to install, connect your graph, and enable each feature.

What it is

GraphLoupe runs your compiled LangGraph and lets you step through it, inspect and diff state between nodes, time-travel, count tokens per LLM node, and pause a node to paste an answer from any chat back in as the model's output — all locally, no account or cloud. The IDE never imports your graph; an isolated Python sidecar worker does.

Install

  1. From the Marketplace: search "GraphLoupe" in the Extensions view, or code --install-extension byGao.graphloupe.
  2. From a .vsix: download a release (or build with npm install && npm run package), then Extensions view → Install from VSIX….

Python: the sidecar needs Python with the deps in requirements.lock. GraphLoupe resolves the interpreter in this order: graphloupe.pythonPath setting → the Python extension's selected interpreter (ms-python.python) → py -3 / python3 / python on PATH. It builds a managed environment for the sidecar and runs your graph in your interpreter. The Health tab shows exactly which interpreter and langgraph version are in use.

If load times out on the first run, that's a cold langchain/langgraph import — retry, or raise graphloupe.loadTimeout (default 30s).

Connect your graph

Ctrl/Cmd+Shift+PGraphLoupe: Open Graph Panel, then GraphLoupe: Select Graph. It AST-scans your project (no code is run) for a graph factory. Pick one, or choose ✎ Specify manually… to point at a file + symbol (a factory function, or an already-compiled graph exposed as a module variable). Your choice is saved to .vscode/settings.json (graphloupe.graphEntry), per-workspace.

An entry is module.path:callable, e.g. pipeline.graph:build_app. To just try everything, use the built-in graphloupe_sidecar.graph:showcase_graph.

Minimal graph GraphLoupe can load

Any file with a top-level factory that returns a compiled graph:

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


class State(TypedDict):
    messages: list
    steps: int


def build_graph():
    def prepare(state: State) -> dict:
        """Seed the state (this docstring shows under the node)."""
        return {"steps": state.get("steps", 0) + 1}

    g = StateGraph(State)
    g.add_node("prepare", prepare)
    g.add_edge(START, "prepare")
    g.add_edge("prepare", END)
    return g.compile()

A docstring on each node function shows as the node's purpose in the overview.

Step debugging — add a checkpointer

Breakpoints, state snapshot + diff, single-step, and time-travel all need a checkpointer. Compile with one:

from langgraph.checkpoint.memory import MemorySaver

def build_graph():
    g = StateGraph(State)
    # … add_node / add_edge …
    return g.compile(checkpointer=MemorySaver())   # ← this line unlocks debugging
No checkpointer? The graph still runs to completion and renders, but can't pause. The Health tab flags this.

Manual inference — pause and paste an answer

Turn "human + any chat" into a pluggable model. Call interrupt() with a contract-shaped payload; the run pauses, you copy the prompt into ChatGPT/Copilot/Claude, paste the answer back, and it resumes with zero state loss.

from langgraph.types import interrupt

def review(state: State) -> dict:
    prompt = "Review this plan and reply 'redo' / 'abort' / anything to proceed."
    answer = interrupt({
        "renderedText": prompt,
        "messages": [{"role": "human", "content": prompt, "name": None, "toolCallId": None}],
        "expects": "text",          # or "tool_call"
        "toolSchema": None,          # a JSON Schema when expects == "tool_call"
        "promptTokens": {"prompt": 0, "completion": None, "source": "sidecar_estimate"},
    })
    return {"messages": [answer]}

On a run, the Manual tab opens with the rendered prompt → Copy prompt → paste into any chat → paste the reply back → Send resume. For tool_call, paste a JSON args object; a bad paste is rejected and the run stays paused so you can fix it.

Token economy — invoke a chat model in a node

Any node that invokes a chat model shows per-node prompt/completion counts in the Tokens tab. Exact when the model returns usage_metadata (real APIs), otherwise a flagged ~ estimate.

async def plan(state: State) -> dict:
    reply = await model.ainvoke(state["messages"])   # model closed over / read from a global
    return {"messages": [reply]}

Referencing a BaseChatModel also puts the node in the ⚡ llm lane on the canvas.

Run-input form — describe your inputs

GraphLoupe reads get_input_jsonschema() and renders a field per input. Use a Pydantic input/state with Field(description=…) to get hints under each field (a plain TypedDict yields names only):

from pydantic import BaseModel, Field

class Input(BaseModel):
    repo_path: str = Field(description="Path to the repo to analyze")
    target: str = Field(description="What to summarize")

Name path-like fields with repo/dir/out/path/file to get a Browse… folder picker. Toggle JSON for a raw box.

Jump to source

Each node row in the left overview has a — click it to open that node's function in your editor at its def line (beside the graph, reusing one tab). Works for functions wrapped by langgraph too. Nothing to configure.

Health panel

The Health tab is a one-glance compatibility check of the loaded graph: whether it loaded, whether it has a checkpointer (debugging), the run-input schema (and whether fields are described), how many LLM/inference nodes, and node→source coverage — plus the GraphLoupe version, the langgraph version, and the interpreter running your graph. If a graph fails to load, it shows the cause here.

Seeing an unexpected langgraph version? The Health tab shows which interpreter is running the graph — if it isn't your project's venv, select it via Python: Select Interpreter or set graphloupe.pythonPath.

Troubleshooting

SymptomFix
"Select Graph" finds nothingName your factory build_graph/build_app/…, or use ✎ Specify manually….
graph_load_failed: …The entry couldn't import / has no such callable; the message names the cause. The Health tab shows it too.
run failed: KeyError: 'x'Your graph needs input key x — fill that field in the run-input form.
Load timed outCold langchain/langgraph import — retry, or raise graphloupe.loadTimeout.
Missing dep (e.g. fastapi)The doctor names the interpreter and the fix; install into that interpreter or pick another.
Can't pause / stepCompile with checkpointer=…. The Health tab flags a missing checkpointer.

How it works

The VS Code extension hosts a Webview (React Flow canvas, read-only execution view) and spawns a Python sidecar (FastAPI). The sidecar runs your graph in an isolated worker subprocess and streams events back over a frozen protocol contract using astream_events(version="v2"). Discovery is a static AST scan (never runs your code); execution is isolated (guards against buggy graphs, not deliberately malicious code). 100 % local — the only traffic is a localhost WebSocket. See SECURITY.md for the threat model.

MIT-licensed · github.com/byGao/GraphLoupe