You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Pi Coding Agent instrumentation double-counts tokens and estimated cost in trace rollups (AgentSession.prompt carries summed LLM usage) #2659
With wrapPiCodingAgentSDK (braintrust 3.39.0, @earendil-works/pi-coding-agent 1.4.0), each AgentSession.prompt task span gets the summed token usage of its child llm spans, and it also carries metadata.model. Braintrust's trace rollups add the parent's metrics to the children's. As a result, the trace summary, the logs table and the trace header report exactly 2× the prompt, completion and cached tokens and 2× estimated_cost for every Pi run.
The instrumentation spec says parent task spans "MAY include aggregate token metrics for display, but SDKs SHOULD avoid making parent rollups look like independent billable LLM calls" (braintrust-spec/skills/instrumentation-spec/references/features/token-and-cost-metrics.md). Here the parent is priced and summed like an LLM call.
Where
js/src/instrumentation/plugins/pi-coding-agent-plugin.ts at braintrust@3.39.0:
finishPiLlmSpan logs the usage on the LLM span and also calls addMetrics(promptState.metrics, usageMetrics) (L703-L707).
finishPiPromptRun logs cleanMetrics(state.metrics) on the AgentSession.prompt span, together with extractModelMetadata(...) (L655-L667).
Minimal reproduction
Call braintrust.wrapPiCodingAgentSDK(pi), create an AgentSession with any model, and run one session.prompt(...) that takes at least one LLM turn under initLogger(...).
Run these two project-log queries on that trace:
-- trace rollupSELECT id, metrics.prompt_tokens, metrics.estimated_costFROM project_logs('<project_id>', shape =>'summary') WHERE root_span_id ='<root>'-- per span, groupedSELECTspan_attributes.name, span_attributes.type, sum(metrics.prompt_tokens) AS pt, sum(metrics.estimated_cost) AS cost
FROM project_logs('<project_id>') WHERE root_span_id ='<root>'ANDmetrics.prompt_tokensIS NOT NULLGROUP BY1, 2
Observed on one trace (gpt-6.1-sol via openai.responses.create, 11 LLM calls):
span
type
prompt_tokens
estimated_cost
openai.responses.create (×11)
llm
443,901
0.1708972
AgentSession.prompt
task
443,901
0.1708972
summary rollup
887,802
0.3417944
Across 19 consecutive traces, the ratio of summary to leaf-llm sums was 2.000 for both tokens and cost in every trace.
Expected
A trace's rollup equals the sum of its leaf llm spans. Either the prompt span should not carry summed usage (or should carry it under a non-rolled-up name), or it should not be priced or summed as a billable call.
Workaround we use
We sum span_attributes.type = 'llm' AND span_attributes.purpose IS NULL spans and ignore the summary's token and cost metrics.
From my understanding, finishPiLlmSpan() records usage metrics on each LLM span and accumulates them in promptState.metrics. Then, finishPiPromptRun() records the accumulated metrics on the parent AgentSession.prompt task span, along with model metadata.
Since Braintrust's trace rollups sum the parent and child metrics, the same token usage and estimated cost are counted twice. This causes prompt, completion, and cached tokens, as well as estimated_cost, to be reported at exactly twice their expected values.
I'd like to investigate how the instrumentation handles aggregate metrics and implement a fix that ensures the trace rollup reflects the actual usage of individual LLM calls without double-counting the parent task's metrics.
Activity
sharonadhitya commentedon Oct 24, 2026
Hi! I'd like to work on this issue.
From my understanding,
finishPiLlmSpan()records usage metrics on each LLM span and accumulates them inpromptState.metrics. Then,finishPiPromptRun()records the accumulated metrics on the parentAgentSession.prompttask span, along with model metadata.Since Braintrust's trace rollups sum the parent and child metrics, the same token usage and estimated cost are counted twice. This causes prompt, completion, and cached tokens, as well as
estimated_cost, to be reported at exactly twice their expected values.I'd like to investigate how the instrumentation handles aggregate metrics and implement a fix that ensures the trace rollup reflects the actual usage of individual LLM calls without double-counting the parent task's metrics.
Would it be okay for me to work on this issue?
github-actions commentedon Oct 26, 2026
A release containing the fix/implementation for this issue has been published:
Included via #2672.