Runtimes
Track any SDK agent#
The 27 supported runtimes all write sessions to disk. Your own production
agent does not — a service built on the OpenAI Agents SDK, LangChain, the
Vercel AI SDK, LlamaIndex, E2B or a plain httpx loop keeps its state in your
application, not in a transcript directory.
The interceptor covers that case. It monkey-patches the HTTP client, captures every LLM call, and writes them where the daemon already looks.
Two lines#
import clawmetry.track # activate the interceptor
clawmetry.track.set_source("support-agent") # name this productThat is the whole integration. From here every LLM call the process makes is
recorded with its model, provider, token split, latency, cost and error status,
attributed to support-agent.
Without touching the code#
CLAWMETRY_TRACK=1 CLAWMETRY_SOURCE=billing-agent python my_agent.pyCLAWMETRY_TRACK=1 activates on import of the clawmetry package;
CLAWMETRY_INTERCEPT=1 activates it when ClawMetry itself starts. Either works;
the environment form is the one to use in a container where you would rather not
patch an image to add an import.
What gets intercepted#
The interceptor patches httpx.Client.send and requests.Session.send — the
transport both major Python SDK families use underneath — and matches on the
request host:
| Provider | Host |
|---|---|
| Anthropic | api.anthropic.com |
| OpenAI | api.openai.com |
generativelanguage.googleapis.com | |
| OpenRouter | openrouter.ai |
Non-LLM outbound calls are captured separately as external calls, which is
how the dashboard can show you that your agent spent nine seconds waiting on a
third-party API rather than on a model. Loopback and internal hosts are excluded
by default, and CLAWMETRY_INTERCEPT_HOSTS_EXCLUDE takes a comma-separated list
of additional substrings to skip.
If neither httpx nor requests is installed, the interceptor silently
no-ops. It never raises into the host application — an observability tool that
can crash the thing it observes is worse than no observability tool.
Naming sources#
set_source() is what makes this useful at more than one agent. Each named
source becomes its own first-class line in the dashboard's out-loop sources
card, with calls, providers, latency and error rate per agent.
import clawmetry.track
clawmetry.track.set_source("support-agent")
# ... later, from anywhere in the process:
stats = clawmetry.track.get_stats() # {"calls": …, "input_tokens": …, "cost_usd": …}Set it once, early. A process that never names itself still gets captured; it just lands under a default source, and telling three unlabelled agents apart afterwards is not fun.
Where it writes#
The interceptor appends JSONL to the workspace, and the daemon ingests it on its normal cycle — the same DuckDB store, the same rollups, the same end-to-end-encrypted cloud snapshot as every on-disk runtime. There is no separate pipeline to operate.
Turning it off#
| Variable | Effect |
|---|---|
CLAWMETRY_NO_INTERCEPT=1 | Opt out even when clawmetry.track is explicitly imported |
CLAWMETRY_INTERCEPT=0 | Do not auto-activate at ClawMetry startup |
When to use the ingest API instead#
The interceptor sees LLM calls. If you want to record agent structure — runs, steps, tool invocations, a session boundary — push those explicitly:
# a run with structure, rather than a stream of model calls
POST /api/v1/runs
POST /api/v1/runs/<id>/events
POST /api/v1/runs/<id>/endNothing stops you doing both: the interceptor for cost truth, the ingest API for the shape of the run.
Overhead#
The patch adds a small amount of work per request — reading the response's usage block and appending a line. Measure it against your own workload rather than a tight loop of no-op calls, which is not a realistic shape and will make the relative overhead look far worse than it is in a real agent that spends most of its wall clock waiting on a model.