Version
@voltagent/core: 2.8.0
Correction / clarification
This issue was originally framed as only totalTokens double-counting cached/reasoning token breakdowns. A more precise description is that the exported trace summary appears to mix two different input-token semantics.
The exported summary exposes promptTokens as gross input tokens, including cached input tokens, but totalTokens appears to treat promptTokens as if it were uncached input and then adds cachedTokens again.
Trace evidence
In an exported VoltAgent trace, the top-level trace.usage object reported:
{
"promptTokens": 389461,
"completionTokens": 1990,
"totalTokens": 726377,
"cachedTokens": 333440,
"reasoningTokens": 1486
}
The root/agent span in the same trace had these attributes:
usage.prompt_tokens = 389461
usage.completion_tokens = 1990
usage.cached_tokens = 333440
usage.reasoning_tokens = 1486
usage.total_tokens = 391451
The root/agent span is internally consistent if usage.prompt_tokens is gross input, including cached input:
The uncached input portion would be:
But the exported top-level summary reports:
726377 = 389461 + 1990 + 333440 + 1486
So the top-level summary appears to combine these fields as:
promptTokens + completionTokens + cachedTokens + reasoningTokens
Problem
The exported summary currently appears to expose:
promptTokens = gross input tokens, including cached input
cachedTokens = cached input token breakdown
But the exported totalTokens appears to calculate as if:
promptTokens = uncached input tokens
cachedTokens = additional cached input tokens
Those two interpretations conflict.
If cachedTokens is exposed separately and intended to be added to input for accounting, then promptTokens would need to represent uncached input. In this trace that would be 56021, not 389461.
If promptTokens represents gross input, then cachedTokens is a breakdown field and should not be treated as additional input in the exported summary total.
The same ambiguity applies to reasoningTokens if it is already included in completionTokens.
Impact
This affects downstream token accounting that reads the exported summary fields, for example:
{
inputTokens: usage.promptTokens,
outputTokens: usage.completionTokens,
totalTokens: usage.totalTokens,
reasoningTokens: usage.reasoningTokens,
cachedInputTokens: usage.cachedTokens,
}
With the current exported summary, downstream consumers cannot tell whether promptTokens is gross input or uncached input. This can lead to cached input being counted twice or input totals being interpreted incorrectly.
Related
Related but separate from #1360, which is about provider/model-specific usage semantics in the onEnd hook and root span usage.
Version
@voltagent/core:2.8.0Correction / clarification
This issue was originally framed as only
totalTokensdouble-counting cached/reasoning token breakdowns. A more precise description is that the exported trace summary appears to mix two different input-token semantics.The exported summary exposes
promptTokensas gross input tokens, including cached input tokens, buttotalTokensappears to treatpromptTokensas if it were uncached input and then addscachedTokensagain.Trace evidence
In an exported VoltAgent trace, the top-level
trace.usageobject reported:{ "promptTokens": 389461, "completionTokens": 1990, "totalTokens": 726377, "cachedTokens": 333440, "reasoningTokens": 1486 }The root/agent span in the same trace had these attributes:
The root/agent span is internally consistent if
usage.prompt_tokensis gross input, including cached input:The uncached input portion would be:
But the exported top-level summary reports:
So the top-level summary appears to combine these fields as:
Problem
The exported summary currently appears to expose:
But the exported
totalTokensappears to calculate as if:Those two interpretations conflict.
If
cachedTokensis exposed separately and intended to be added to input for accounting, thenpromptTokenswould need to represent uncached input. In this trace that would be56021, not389461.If
promptTokensrepresents gross input, thencachedTokensis a breakdown field and should not be treated as additional input in the exported summary total.The same ambiguity applies to
reasoningTokensif it is already included incompletionTokens.Impact
This affects downstream token accounting that reads the exported summary fields, for example:
With the current exported summary, downstream consumers cannot tell whether
promptTokensis gross input or uncached input. This can lead to cached input being counted twice or input totals being interpreted incorrectly.Related
Related but separate from #1360, which is about provider/model-specific usage semantics in the
onEndhook and root span usage.