Skip to content

Documentation

Operate your agents

LangMonitor watches and controls LangGraph agents from the outside. Wrap a compiled graph in one line and a dashboard goes live inside your process — then stream events and drive the run over a plain REST and WebSocket API.

shell
$ pip install langmonitor

Start here

Quickstart

Install the package, wrap your compiled graph, and run it as you normally would. The dashboard is served from the same process.

app.py
from langmonitor import monitor
# Wrap your compiled LangGraph graph and choose a port.
monitored = monitor(compiled_graph, port=8000, open_browser=True)
# Run it like any compiled graph — sync or async.
result = monitored.invoke({"input": "hello"})
print(monitored.dashboard_url) # http://127.0.0.1:8000

Open http://localhost:8000 for the interactive dashboard — watch every node, then kill, pause, resume, inject state, roll back, or A/B-swap the running agent. Prefer raw REST? The Swagger UI is at /docs. Requires Python 3.10+.

monitor()

The three modes

monitor() returns a drop-in stand-in for your graph — same invoke and ainvoke. Which mode you get depends on what you pass.

python
# 1. Embedded (default) — a dashboard in this process on the given port
monitor(graph, port=8000)
# 2. Remote — connect to a LangMonitor server running elsewhere
monitor(graph, server_url="ws://monitor.internal:8000", api_key="...")
# 3. In-process engine — route events straight to a MainEngine (used in tests)
monitor(graph, in_process_engine=engine)

If the dashboard can't start, or a remote server is down, monitoring fails open — your agent keeps running, just unmonitored.

Control

Operating a run

Every run gets a run_id (emitted as the run_started event and listed at GET /api/v1/runs). Use it to drive the run — from Swagger, from Python, or straight from the shell.

shell
$ curl -X POST localhost:8000/api/v1/runs/<run_id>/pause
$ curl -X POST localhost:8000/api/v1/runs/<run_id>/resume \
-d '{"state_patch": {"context": "updated"}}'
$ curl -X POST localhost:8000/api/v1/runs/<run_id>/kill

Kill and pause take effect before the next node — the wrapper checks for them between steps.

Time travel

Checkpoints & rollback

Checkpoints sit on top of LangGraph's native checkpointer. Save one by hand, then roll back to it at any time.

shell
# Save a named checkpoint
$ curl -X POST localhost:8000/api/v1/runs/<run_id>/checkpoints \
-d '{"label": "before-tool-call"}'
# Restore it — auto-pauses the run so you can inspect before resuming
$ curl -X POST \
localhost:8000/api/v1/runs/<run_id>/checkpoints/<id>/rollback

With CHECKPOINT_AUTO_SAVE=true (the default) a checkpoint is also taken after every node end, so you can always step backwards.

Automatic

Guardrails

Guardrails run after every node end. When one trips it fires the configured action — kill, pause, or alert.

shell
$ curl -X POST localhost:8000/api/v1/guardrails -d '{
"name": "cost cap",
"rule_type": "max_cost_usd",
"config": { "threshold": 2.0 },
"action": "kill"
}'

Built-in rule types:

  • max_tool_calls, max_node_repeats, max_latency_ms, max_cost_usd — numeric thresholds.
  • custom_condition — a sandboxed boolean expression evaluated against the current node. No eval, no attribute access or calls.
json
{
"rule_type": "custom_condition",
"config": { "expression": "node_name == 'planner' and latency_ms > 5000" },
"action": "alert"
}

Available names in an expression: node_name, latency_ms, tokens_used, sequence_order.

Experiment

A/B prompt swaps

Register two prompts for a node, then swap the active variant while the agent runs. The wrapper picks up the active variant before each node — no code changes needed.

shell
# Create the test
$ curl -X POST localhost:8000/api/v1/ab-tests -d '{
"node_name": "planner",
"prompt_a": "You are a careful planner.",
"prompt_b": "You are an aggressive planner."
}'
# Swap the active variant mid-run
$ curl -X POST localhost:8000/api/v1/ab-tests/<id>/swap

WebSocket

Streaming events

Consume the live event stream from any client. Two channels:

  • /ws/runs/{run_id} — events for a single run.
  • /ws/all — every event across all runs.

Every message has the same envelope:

json
{
"type": "node_end",
"run_id": "<uuid>",
"timestamp": "<iso8601>",
"payload": { "node_name": "planner", "latency_ms": 312, "tokens": 148 }
}
EventKey payload fields
run_startedgraph_name, input
node_startnode_name, sequence, input_state
node_endnode_name, output_state, latency_ms, tokens
llm_callnode_name, prompt, response, model, tokens
state_updatedsequence, state, diff
guardrail_alertrule_name, rule_type, action
checkpoint_savedcheckpoint_id, label, sequence
run_endedstatus, total_tokens, total_cost_usd, duration_ms

Environment

Configuration

The dashboard reads the same settings as the standalone server. Set them in the environment or a .env file — see .env.example for the full list.

VariableDefaultDescription
DATABASE_URLsqlite+aiosqlite://…SQLAlchemy async URL. Use postgresql+asyncpg://… for Postgres.
SERVER_HOST0.0.0.0Bind host for the standalone server.
SERVER_PORT8000Bind port for the standalone server.
API_KEY""Shared secret on every request. Empty = unauthenticated (dev only).
ENABLE_DOCStrueExpose /docs, /redoc, /openapi.json. Set false in production.
CHECKPOINT_AUTO_SAVEtrueAuto-save a checkpoint after every node end.
CORS_ORIGINS[localhost:3000]JSON list, comma-separated string, or single origin.
LOG_LEVELINFOPython log level.

Before you expose it

Security

LangMonitor is a control plane — anyone who can reach it can kill, pause, or inject state into your agents. The embedded dashboard binds to 127.0.0.1 by default, so it's local-only. Before putting it on a network:

  • Set API_KEY. Once set, every route and WebSocket requires it (X-API-Key header, or ?api_key= for browsers). When empty the server runs open and warns at startup — fine for local dev only.
  • Lock down CORS to origins you trust; the * + credentials combination is force-disabled.
  • Disable docs with ENABLE_DOCS=false in production.
  • Connection caps, rule-count limits, and payload-size bounds blunt trivial DoS vectors, and custom_condition guardrails are AST-sandboxed.