The headers
Graceful degradation. A trace id alone gives you a flat run rollup — every call in
the run plus its total cost. Adding a span path gives you the waterfall — the calls
grouped under named tool/agent steps, with cost rolled up each step. Start with just a trace
id; add paths when you want the tree.
/ is the reserved separator between step names. If a step name itself contains a /,
percent-encode it so it doesn’t split into extra nodes.Modeling the tree
Send, on each LLM call, the path of the steps above it. Interior tool/agent nodes don’t make their own request — they’re inferred from the paths of the calls beneath them. For an agent that plans, then calls asearch_db tool that makes two calls:
tool:search_db node’s cost ($0.048) is the sum of the calls beneath it; the planner
node totals the whole run.
Sending the headers
Frameworks that hide per-request headers
Pydantic-AI, Mastra, and similar frameworks don’t expose a per-request header hook onagent.run() — they own the model-call loop internally. Two options:
- A client per conversation — build the model/client with the trace id in its default headers and use it for that conversation’s calls.
- Context-scoped injection — keep one client and inject the current run’s headers from a context store, so every model request the framework makes picks them up automatically. This is usually the cleaner option, shown below.
contextvars in Python, AsyncLocalStorage in Node), and give the model client a
custom HTTP layer that reads that store on every request. You then set the store once around
each agent.run() — every internal model call in that run (including tool-loop calls) inherits
the headers. The natural granularity is per agent/tool step, which is exactly the
span_path you want: set the store before each agent or tool boundary.
AsyncLocalStorage isolates concurrent runs automatically; in Python, set the contextvars
value inside each task so parallel runs don’t share one store.
Viewing runs
In the dashboard, open Observe → Runs:- The runs list shows one row per run with its rolled-up cost, token totals, call count, and duration.
- Opening a run shows the waterfall: the nested steps and LLM calls with per-node cost. Click any LLM node to open its full request detail.
Querying runs directly
The headers are stored as first-class columns onllm_requests, so a run’s cost is a plain
aggregate (see the data model):
Runs vs. metadata
Use agent run tracking to group the calls of one execution into a tree. Use metadata headers (feature, team, environment, user) to attribute cost across dimensions. They compose: stamp a run with a trace id and aX-Majordomo-Feature so you can both drill into a single run and roll runs up by feature.