Time-travel debugger and branch explorer for LangGraph AI agents. Capture execution traces, inspect LLM and tool calls, and branch from any point with overridden outputs.
Project description
Agent Step
A time-travel debugger and branch explorer for LangGraph agents. Capture every LLM call and tool invocation as a span, browse them in a web timeline, then branch from any point — override the output and replay to see how the rest of the graph would behave differently.
Think pdb + a REPL for agent workflows, with a SQLite file you can hand to a teammate.
Why you'd use it
When an agent goes off the rails, you usually want to answer one of:
- "What did the LLM actually say at step 4?" — captured.
- "What would have happened if the weather tool returned snow instead of fog?" — branch and replay.
- "Why did the agent loop?" — the timeline shows every call with timing and full prompts/outputs.
Without this, you're adding print() statements and re-running with a different seed. With this, you replay against the original trace.
Install
pip install agentstep
Or from the repo (development):
git clone https://github.com/vanshvisariya/agent-replay
cd agent-replay
pip install -e .
Requires Python 3.13+.
Quick start
This walks through using Agent Replay on a LangGraph agent in your own project.
1. Wrap your graph execution
The SDK exposes one thing: replay_trace, a context manager that instruments your graph with OpenTelemetry callbacks and writes spans to a SQLite file.
from langgraph.graph import StateGraph, START, END
from agent_replay.sdk.tracer import replay_trace
# build your compiled graph the way you already do
graph = ...
# a thread_id identifies one conversation/run in the trace
config = {"configurable": {"thread_id": "user-42"}}
with replay_trace(config, sqlite_path="trace.sqlite") as cfg:
for chunk in graph.stream(inputs, cfg, stream_mode="values"):
print(chunk)
That's the entire API surface for instrumentation. The context manager:
- Sets up an OpenTelemetry tracer pointed at your SQLite file.
- Injects a callback handler into
config["callbacks"]. - Records every
llm_callandtool_callspan with timing, prompts, completions, and outputs.
The original config is mutated in place; you don't need to swap it back.
2. Launch the debugger
In a terminal:
replay-debugger trace.sqlite --app my_module:graph
trace.sqliteis the file you wrote spans to.--app my_module:graphis a Python import path to your compiled graph. Three forms work:my_module:graph—graphis a compiled LangGraph instance.my_module.graph— same thing, dotted form.my_module:make_graph—make_graphis a callable that returns a compiled graph (it gets called at startup).
Open http://localhost:7337.
You should see your thread in the left sidebar and a timeline of spans on the right.
3. Branch from any span
- Click any span — the right panel shows the checkpoint, attributes, and full completion.
- Click branch from here.
- Edit the override output (new tool result or new LLM completion).
- Click run_branch.
The original trace stays intact. The fork becomes a new branch in the timeline, labeled with a small b0 chip, color-coded so you can tell at a glance which branch you're looking at.
What gets captured
| Span type | What's recorded |
|---|---|
llm_call |
prompt, completion, system, input/output token counts, wall time |
tool_call |
tool name, input string, output string, wall time |
Every span carries:
lg.thread_id— the LangGraphthread_idso spans from one conversation group together.lg.branch_id— set automatically on spans created during a branch replay, so the debugger can group them separately.
Other graph node executions, sub-graphs, and conditional edges are not yet instrumented as spans — but the checkpoint data is still preserved by LangGraph itself, so branch replay works regardless.
Working example
The repo ships a runnable demo (sample.py) with a fake LLM so you don't need any API keys:
git clone https://github.com/vanshvisariya/replay
cd agent-replay
pip install -e .
python sample.py # writes trace.sqlite
replay-debugger trace.sqlite --app sample:graph
Then open http://localhost:7337. Click the LLM call → click branch from here → change the response → watch the timeline fork.
Development workflow
When hacking on the debugger itself, run the backend and frontend with hot reload:
# Terminal 1 — backend on :7337, API only
replay-debugger trace.sqlite --app sample:graph --dev-ui
# Terminal 2 — Vite dev server on :5173 (proxies /api/* to :7337)
cd ui
npm install
npm run dev
Open http://localhost:5173 instead. Edits to React files hot-reload; backend edits need a restart.
Programmatic branch replay
The web UI is the main way to branch, but the same operation is available as a function for scripted use:
from agent_replay.server.replayer import replay_branch
result = replay_branch(
thread_id="user-42",
checkpoint_id="1efb...", # from GET /api/traces/{tid}/checkpoints
node_name="tools", # or "agent"
span_type="tool_call", # or "llm_call"
tool_call_id="get_weather", # tool name for tool spans
new_output="It's snowing in SF.",
db_path="trace.sqlite",
)
print(result) # branch_id of the new replay
Useful for regression tests, CI, or batch-exploration of failure modes.
API reference
The FastAPI server (started by the replay-debugger CLI) exposes:
| Method | Path | Purpose |
|---|---|---|
GET |
/api/threads |
List all thread IDs in the database. |
GET |
/api/traces/{thread_id} |
All spans for a thread, grouped by branch. |
GET |
/api/traces/{thread_id}/checkpoints |
All checkpoints for a thread. |
POST |
/api/branch |
Fork the graph from a checkpoint with an overridden output. |
POST /api/branch body:
{
"thread_id": "user-42",
"checkpoint_id": "1efb...",
"node_name": "agent",
"span_type": "llm_call",
"tool_call_id": null,
"new_output": "The weather is sunny and 72°F."
}
Response: {"branch_id": "branch_a1b2c3...", "status": "ok"}.
Where things live in your file
After running the demo once:
trace.sqlite
├── spans table ← every llm_call / tool_call, with start/end nanoseconds + JSON attributes
├── checkpoints table ← LangGraph state snapshots (one per node execution)
└── thread metadata ← implicit, keyed off lg.thread_id in span attributes
Everything is one file. Copy it, share it, commit it for reproduction.
Limitations
- Python 3.13+ only — pinned in
pyproject.toml. - LangGraph checkpointers must use SQLite —
SqliteSaveris the only supported backend currently; the branch endpoint reads from the same file the tracer wrote to. - No remote export — spans stay local. (The exporter is OpenTelemetry-native, so wiring Jaeger/Zipkin out the side is doable but not built in.)
- Two span types — only LLM and tool calls. If you want full graph-node tracing, file an issue.
Contributing
git clone https://github.com/vanshvisariya/agent-replay
cd agent-replay
pip install -e .
cd ui && npm install
Layout:
src/agent_replay/
├── sdk/
│ ├── tracer.py ← replay_trace() + ReplayCallbackHandler
│ └── exporter.py ← OTel span exporter → SQLite
└── server/
├── api.py ← FastAPI endpoints
├── replayer.py ← branch replay logic (used by API + programmatic)
└── cli.py ← `replay-debugger` entry point
ui/
└── src/App.tsx ← single-file React app
sample.py ← runnable weather-agent demo
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentstep-0.1.2.tar.gz.
File metadata
- Download URL: agentstep-0.1.2.tar.gz
- Upload date:
- Size: 29.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9312e0175907794f05d022093ed02c924e63965c7ba30747401750290eeafd9
|
|
| MD5 |
54fada8fd476fd105c98072c91c62688
|
|
| BLAKE2b-256 |
10c75740d0c8395591933c85282aca7167c577acd99683aeb412222bcb883730
|
File details
Details for the file agentstep-0.1.2-py3-none-any.whl.
File metadata
- Download URL: agentstep-0.1.2-py3-none-any.whl
- Upload date:
- Size: 152.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fd5697f8210ad223e9b82187070e747c773b345e9484041e64686b4a199638f
|
|
| MD5 |
4aa3882f921fb061c8a39b6010e6f53b
|
|
| BLAKE2b-256 |
4d014d640513ca6fb29defcea7d45a6641bfeb86af461f053848fe58b42caf37
|