Local-first observability for LangChain/LangGraph multi-agent systems
Project description
tracesage
See what your LangChain & LangGraph agents actually did. Local-first observability — drop in one line, watch every run live in your browser.
Try it in 30 seconds
pip install "tracesage[langchain]"
tracesage demo # seeds a sample run and opens the live UI in your browser
No API key, no config, no code — tracesage demo opens a real trace so you can click
around the graph immediately. Then add one line to your own agent:
result = await graph.ainvoke(
{"input": payload},
config={"callbacks": [tracer.handler]}, # ← the only line you add
)
# tracesage prints a link: 🔍 http://localhost:7842/ui — open it to watch the run live
When a run breaks, see exactly where
A failed tool call shows up as a red node on the exact step that broke — the tool, its input, and the error — instead of a stack trace buried in your logs.
Contents
- Why tracesage
- Install
- Quick start
- Screenshots
- Concepts: topology node kinds
- Features
- Examples
- Documentation
- Comparison
- Performance
- Status
- Contributing
- License
Why tracesage
LangChain agents emit a rich callback stream — chain start/end, tool start/end, LLM start/end, retrieval, errors. tracesage captures all of it without changing your workflow logic, persists it locally (SQLite + gzipped blobs), and renders it in an interactive graph + timeline UI in real time.
- Zero infrastructure. No Docker. No Postgres. No external services.
pip install. The UI is fully self-contained (assets vendored, no CDN) and works offline. - Minimal-change integration. Add one callback to your existing
ainvoke— no rewrites. - Crash-safe by design. The handler never raises and the tracer never crashes your pipeline.
- Interactive graph view. Custom SVG graph (no framework), auto-laid-out. Hover, click, replay any run.
- MCP-aware. Tools loaded from MCP servers are attributed by source, so you can see which tools came from which server vs. which are hardcoded. See docs/mcp.md.
- OpenTelemetry export. Optionally ship every trace as OTel spans to your collector / Tempo / Jaeger / Datadog / Honeycomb — the bridge from the local dev view to your production stack (config-driven; see docs/configuration.md).
- Multi-app friendly. Run several apps at once: each auto-binds a free port (7842, 7843, …),
shows its
TRACESAGE_PROJECT_NAMEin the UI header, and keeps its own data dir — no clashes. - Pluggable storage. SQLite today; Postgres / remote-collector / object-store backends planned (see
production_roadmap.md). - MIT licensed. Free forever.
Install
pip install "tracesage[langchain]"
Requires Python 3.11+. Quote the extra ("tracesage[langchain]") so zsh —
the default macOS shell — doesn't glob the brackets and fail with no matches found. The [langchain] extra pulls langchain-core; that's the only mandatory
third-party dep beyond the standard FastAPI / aiosqlite / pydantic stack. If your
app uses LangGraph, also pip install langgraph (tracesage doesn't pull it).
tracesage is provider-agnostic — it traces LangChain's callback stream, so OpenAI / Anthropic / local models are all captured automatically; there's no provider setting in tracesage. Install whichever provider you use:
pip install langchain-openai # OPENAI_API_KEY=...
pip install langchain-anthropic # ANTHROPIC_API_KEY=...
For MCP tool-source attribution (loads tools from MCP servers and tags them by
source), install the mcp extra:
pip install 'tracesage[mcp]'
Quick start
See it in 5 seconds — seed a sample trace and open the UI:
tracesage demo
Sync scripts / notebooks — wrap your code; every LangChain call is captured
automatically (no callbacks= wiring) and a clickable trace link is printed:
import tracesage
with tracesage.trace() as tl: # starts the UI + global capture
result = agent.invoke("your input") # 🔍 tracesage: http://127.0.0.1:7842/ui/#run=...
input("Trace ready — open the printed link, then Enter to exit.") # keep the UI up
(The embedded UI stops when the with block / process exits; traces persist to
~/.tracesage, so you can also reopen them later with tracesage serve.)
Async apps — use the context manager (or await TraceSage.create() for full control):
import asyncio
from tracesage import TraceSage
async def main():
async with TraceSage.session(install=True) as tl: # install=True → global capture
await graph.ainvoke({"input": "your payload"}, config={"tags": ["my-system"]})
await tl.flush() # ensure events are persisted
print(tl.run_url("<run_id>")) # deep link to a run
asyncio.run(main())
Prefer explicit wiring? Pass config={"callbacks": [tl.handler]} instead of install=True.
That's it. Open the URL tracesage prints on startup (🔍 tracesage: http://…/ui/#run=…)
and explore — it's http://localhost:7842/ui by default, but auto-port picks the next
free port (7843, …) if 7842 is taken, so trust the printed link (tracer.ui_url).
Developer workflow
Once you have traces, debug them without leaving your terminal:
tracesage show <run_id> # render a run as a tree in the terminal
tracesage watch <run_id> # live-tail events as they stream
tracesage diff <run_a> <run_b> # compare two runs (tokens, tools, errors)
tracesage view trace.jsonl # open an exported trace in the UI directly
Test your agents — the tracesage_capture pytest fixture is auto-registered:
def test_agent_uses_search(tracesage_capture):
agent.invoke("find me a hotel")
tracesage_capture.assert_tool_called("search")
tracesage_capture.assert_no_errors()
assert tracesage_capture.total_tokens()[0] < 5000
See docs/development.md for the full developer guide, and
examples/showcase/ for 30 before/after apps across popular use cases.
Screenshots
| Live topology — agents, tools, LLMs and MCP servers across a run. | Run list — every run at a glance: green completed, red failed. |
| Run trace — the path a run took, with a step-by-step timeline you can replay. | Click any step for its full request and response payloads, tokens and errors. |
| Inspect an LLM — token usage (in / out, total across calls) and latency. | “Tools by source” — every tool grouped by origin (MCP servers vs. local). |
Concepts: topology node kinds
When you open the UI, every node in the topology graph is one of five event-based
kinds — plus a synthesized mcp node when you attribute tools to an MCP server.
Knowing what each means is the prerequisite to reading a trace:
| Kind | What it is | Examples you'll see |
|---|---|---|
agent |
A function you registered as a LangGraph node, that calls other components | agent:billing_agent, agent:fact_extractor, agent:supervisor |
tool |
A side-effect function (DB query, API call, calculation) decorated with @tool |
tool:lookup_account, tool:run_sql, tool:cite_sources |
llm |
A language-model call (chat or completion) | llm:FakeListChatModel, llm:ChatOpenAI, llm:ChatAnthropic |
retriever |
A BaseRetriever subclass — the "R" in RAG |
retriever:Chroma, retriever:FAISS, retriever:_FixedCorpusRetriever |
chain |
Plumbing — LCEL primitives, the LangGraph orchestrator, routing functions | chain:LangGraph, chain:RunnableSequence, chain:ChatPromptTemplate, chain:route_after_quality |
mcp |
An MCP server (synthesized) — groups the tools loaded from it | mcp:weather, mcp:math, mcp:github |
Quick mental model:
agentis your code that calls something. It does reasoning.tooldoes the actual side-effect work and returns a result.llmis what you count, cost, and cache.retrieveris its own dimension — "did we find the right docs?" is a different question from "did the LLM use them well?".chainis the wrapping machinery (theprompt | llm | parserpipe operator, the LangGraph state machine, routing functions). It's infrastructure, not business logic.mcpgroups tools by the MCP server they came from — provenance, so you can tell server-provided tools from your hardcoded ones (see docs/mcp.md).
Read the full reference at docs/concepts.md —
it covers how tracesage classifies events into kinds, why agents with no
descendants get demoted to chain, and walks through a research-pipeline
topology piece by piece.
Features
Live interactive UI
- Run list with status badges (running / completed / failed), search, status filter, and a toast when a watched run finishes ("Run completed" / "Run failed: …")
- Topology vs. Run-trace — one graph pane toggles between the all-up system architecture (nodes in per-kind columns) and a single run laid out as a left → right call tree in call order. Picking a run opens its trace; the Topology button returns.
- Step-through replay — explicit Start / Pause / Resume plus Prev / Next manual stepping walk a run on the graph (1x / 2x / 5x); clicking a timeline step during replay pauses and jumps the cursor there
- Scoped topology — the graph + "Tools by source" default to the selected run (a toolbar selector switches to last-N-runs / all-time), so removed tools, agents, or MCP servers don't linger across app versions as you iterate
- MCP attribution —
mcp:server nodes with distinct per-server colors (tools tinted by source), agent→server and server→tool edges, and a draggable "Tools by source" panel; a large tool fan-out wraps into columns and the graph caps its zoom so nodes stay legible instead of shrinking to fit - Timeline with click-to-expand step cards — a
*_startshows its request, a*_endthe full request + response payloads, plus tokens, duration, and errors; MCP-backed tools are tagged with their server - Node inspector — click any node for its stats; LLM nodes show token usage (in / out, total across N calls) and each node's invocations are grouped one-per-call
- Header stats —
ev/s(1-min rolling event rate),running(in-progress runs),dropped(events lost to backpressure; red if non-zero), and a heartbeat-backed connection dot that reflects a dropped link instead of going stale - Dark / light themes, persisted in
localStorage - Keyboard shortcuts:
j/knext/prev run,/focus search,ttoggle theme,Esc,?
Production safety
- Refuses to bind non-loopback addresses without
auth_token(hard fail-stop) - Bearer-token HTTP auth + WebSocket auth via
?token=or subprotocol - Constant-time token comparison
- Path-traversal blocked at runtime
- Per-run event cap (circuit breaker) and root-level sampling for high volumes
- Bounded internal state (no unbounded memory growth in long-running processes)
- Kill switch:
TRACESAGE_ENABLED=falsemakes it a complete no-op (no server, no DB/worker, no-op handler) — integrate once, disable per-environment. See docs/production.md.
OpenTelemetry export (bridge to production)
tracesage's own UI/CLI is the local developer-loop view. To get agent traces into a
production observability stack, point otlp_endpoint at any OTLP/HTTP collector —
every trace is also emitted as OpenTelemetry spans (the local SQLite store stays too):
pip install "tracesage[otel]"
export TRACESAGE_OTLP_ENDPOINT=http://localhost:4318 # or set in TraceSageConfig
from tracesage import TraceSageConfig
# pass to TraceSage.create() / .session() / tracesage.trace():
cfg = TraceSageConfig(otlp_endpoint="http://localhost:4318", otlp_service_name="my-agent")
Where it helps: the spans land in whatever OTLP-compatible backend you already run —
Grafana Tempo, Jaeger, Datadog, Honeycomb, Arize/Phoenix, an OTel Collector — so agent
traces sit alongside the rest of your services (correlated with HTTP/DB spans, long-term
retention, alerting, multi-service/multi-process views) with no vendor lock-in. Mapping:
root_run_id→trace, run_id→span, parent_run_id→parent; tokens/errors/MCP server become
span attributes. Best-effort — if the collector is down, tracing continues and your app is
never affected.
Note — it's config-driven, not a UI toggle. There's no button in tracesage's UI to turn this on. You enable it via config/env before tracing starts, and the exported traces appear in your OTel backend's UI (Grafana/Jaeger/Datadog/…), not in tracesage's own UI. tracesage's UI always shows the local SQLite view. See docs/configuration.md.
CLI
The tracesage command ships with the package — a viewer + utilities over a
data directory. It never ingests events; ingestion happens only when your code calls
TraceSage.create(). The fastest way to see it working, with zero code:
tracesage demo # seed a sample trace, serve + open the UI
Inspect traces in the browser:
| Command | What it does |
|---|---|
tracesage serve -d ~/.tracesage [--open] |
Read-only viewer over an existing data dir (auto-picks the next free port; --auth-token to gate it) |
tracesage view trace.jsonl [--open] |
Open an exported JSONL trace directly in the UI |
Inspect traces in the terminal (no server):
| Command | What it does |
|---|---|
tracesage runs --status failed --limit 20 |
List root runs (filter by --status / --tag, --json for NDJSON) |
tracesage show <run_id> |
Render a run as a colour-coded indented call tree (MCP tools tagged mcp:<server>) |
tracesage watch <run_id> |
Live-tail a run's events as they're written (--once to print and exit) |
tracesage diff <run_a> <run_b> |
Compare two runs side by side — status, steps, tokens, tools, errors |
tracesage stats [--json] |
Summary stats — run counts by status, avg duration, token totals, DB size |
Move, retain, diagnose:
| Command | What it does |
|---|---|
tracesage export [RUN_ID] --all -o trace.jsonl |
Dump one run or --all to JSONL (-o - for stdout) |
tracesage import -i trace.jsonl -d DIR |
Read a JSONL export back into a data dir (backups / move between machines) |
tracesage gc --max-runs 10000 [--dry-run] |
Retention — delete oldest runs/blobs beyond a count or --max-blob-size-gb |
tracesage doctor -d ~/.tracesage |
Read-only diagnostics — schema version, run counts, orphan/missing-blob checks |
tracesage version |
Print the installed version |
Most commands take --data-dir, -d (default ~/.tracesage). See
docs/cli.md for the full per-command reference.
Examples
The examples/ directory has three tiers:
getting_started/— 3 standalone demos driven byFakeListChatModel(no API key): smart-search agent, research supervisor, RAG.mcp/— MCP attribution demos (needstracesage[mcp]), including two real-world before/after apps:trip_demo/— one agent over three bundled stdio MCP servers (flights / weather / hotels, 7 tools each) + a local tool; no external installs, just an LLM key. Best place to see multi-server topology + "Tools by source".gmail_youtube_demo/— a ReAct agent that reads a real Gmail inbox and summarises YouTube transcripts (YouTube needs no auth; Gmail is optional, via Google ADC).
showcase/— 30 real before/after apps across popular use cases (customer support, RAG, multi-agent, MCP, reasoning loops, finance/legal/insurance). Each ships a plainbefore.pyand anafter.pywith tracesage added, sodiffshows the exact integration.
# instant, no key:
python examples/getting_started/01_smart_search_agent.py # then open http://localhost:7842/ui
# the real-world gallery (needs an LLM key):
pip install -r examples/showcase/requirements.txt
export OPENAI_API_KEY=...
python examples/showcase/01_support_faq_router/after.py
The showcase gallery has 30 before/after apps spanning LangChain + LangGraph: routing, parallel fan-out, the supervisor pattern, RAG variants, writer-critic loops, map-reduce, MCP, self-correction, and finance/legal/insurance verticals.
Documentation
| Doc | What's in it |
|---|---|
| Quickstart | First trace in five minutes |
| Developer guide | Trace links, sync/notebook setup, CLI debugging, pytest fixture |
| Configuration | Every TRACESAGE_* env var explained |
| CLI reference | All tracesage subcommands |
| Production guide | Sampling, auth, retention, deployment |
| Troubleshooting | "Where are my runs?", install/port issues, FAQ |
| Comparison | tracesage vs LangSmith / LangFuse / Phoenix |
| Extending tracesage | Adding framework adapters and storage backends |
| Examples | 30 before/after apps with tracesage added |
Comparison
When to use tracesage vs alternatives:
| tracesage | LangSmith | LangFuse | Phoenix | |
|---|---|---|---|---|
| Zero infra | ✓ | cloud / enterprise self-host | Docker + Postgres | ✓ |
| Pure pip install | ✓ | ✓ (cloud) | ✗ | ✓ |
| Live UI | ✓ | ✓ | ✓ | ✓ |
| MIT licensed | ✓ | proprietary | MIT | Elastic v2 |
| Eval framework | non-goal | ✓ | ✓ | ✓ |
| OpenTelemetry export | ✓ (0.2+) | partial | ✓ | ✓ |
See docs/comparison.md for the full breakdown.
Performance
Bench results (5,000 events, 100 distinct run_ids, 20% blob-eligible):
| Platform | Sustained | p99 write | Drops |
|---|---|---|---|
| Linux x86 + NVMe | 800–1200 ev/s | 80–150 ms | 0 |
| Windows NTFS | 60–100 ev/s | 1–2 s | 0 |
Windows NTFS is the bottleneck (gzip + fsync amplification). For very high
throughput on Windows, raise TRACESAGE_WORKER_BATCH_SIZE to 200 and
TRACESAGE_WORKER_BATCH_TIMEOUT to 0.5.
Status
Beta. API may still shift before v1.0. The PyPI badge at the top shows the published
version; it's stamped by the release workflow when a version actually ships (so it
matches PyPI and never gets ahead of a release). Built for local development and
single-process tracing; centralized multi-process / remote-collector mode is
on the roadmap (see production_roadmap.md). Today the only
shipped adapter is LangChain / LangGraph — the core is framework-neutral and
CrewAI / AutoGen / LlamaIndex adapters are planned, not yet available.
See the changelog for release notes.
Contributing
Issues and pull requests are welcome.
- Read
docs/contributing.mdbefore sending a PR - For non-trivial changes, open a discussion first
- Bugs and feature requests use the issue templates
- Security reports: see
SECURITY.md— please don't open public issues for vulnerabilities - All participation is governed by the Code of Conduct
License
MIT © tracesage contributors
Project details
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 tracesage-0.3.1.tar.gz.
File metadata
- Download URL: tracesage-0.3.1.tar.gz
- Upload date:
- Size: 7.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d1c0152912fcadc72dd28bb053dd00ba2446370baef626562d465a792dc8e75
|
|
| MD5 |
561975e192aed152001f28517e780ade
|
|
| BLAKE2b-256 |
35db895afb7e631858b666cf7872e5f6e57398eaf38798feca3cf69410202d16
|
Provenance
The following attestation bundles were made for tracesage-0.3.1.tar.gz:
Publisher:
release.yml on kjgpta/tracesage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracesage-0.3.1.tar.gz -
Subject digest:
0d1c0152912fcadc72dd28bb053dd00ba2446370baef626562d465a792dc8e75 - Sigstore transparency entry: 2031346372
- Sigstore integration time:
-
Permalink:
kjgpta/tracesage@d2232ca5d565a6b6d0669f56710afc57e3863d61 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/kjgpta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d2232ca5d565a6b6d0669f56710afc57e3863d61 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tracesage-0.3.1-py3-none-any.whl.
File metadata
- Download URL: tracesage-0.3.1-py3-none-any.whl
- Upload date:
- Size: 162.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9310a360c18c51f43a61e3aefcfdd768ecd82bd5327b00b36a61601c1d07dee0
|
|
| MD5 |
74b1a8590e99bb92eb0e2913cb879213
|
|
| BLAKE2b-256 |
9a3c50da9d7489dc97fbc5855ab21d578e11dc2bbd2a18820d94841078fbce75
|
Provenance
The following attestation bundles were made for tracesage-0.3.1-py3-none-any.whl:
Publisher:
release.yml on kjgpta/tracesage
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracesage-0.3.1-py3-none-any.whl -
Subject digest:
9310a360c18c51f43a61e3aefcfdd768ecd82bd5327b00b36a61601c1d07dee0 - Sigstore transparency entry: 2031346443
- Sigstore integration time:
-
Permalink:
kjgpta/tracesage@d2232ca5d565a6b6d0669f56710afc57e3863d61 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/kjgpta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d2232ca5d565a6b6d0669f56710afc57e3863d61 -
Trigger Event:
push
-
Statement type: