feat(usage): add support for detailed token breakdown in usage tracking

- Introduced `CacheReadTokens` and `CacheCreationTokens` to enhance token breakdown.
- Refactored `parseClaudeUsageNode` for cleaner and reusable logic.
- Adjusted helpers and updated token calculations to align with the new fields.
This commit is contained in:
Luis Pater
2026-05-12 11:59:07 +08:00
parent 041ccf0195
commit bd8c05a830
3 changed files with 37 additions and 31 deletions

View File

@@ -116,6 +116,8 @@ func hasNonZeroTokenUsage(detail usage.Detail) bool {
detail.OutputTokens != 0 ||
detail.ReasoningTokens != 0 ||
detail.CachedTokens != 0 ||
detail.CacheReadTokens != 0 ||
detail.CacheCreationTokens != 0 ||
detail.TotalTokens != 0
}
@@ -356,17 +358,7 @@ func ParseClaudeUsage(data []byte) usage.Detail {
if !usageNode.Exists() {
return usage.Detail{}
}
detail := usage.Detail{
InputTokens: usageNode.Get("input_tokens").Int(),
OutputTokens: usageNode.Get("output_tokens").Int(),
CachedTokens: usageNode.Get("cache_read_input_tokens").Int(),
}
if detail.CachedTokens == 0 {
// fall back to creation tokens when read tokens are absent
detail.CachedTokens = usageNode.Get("cache_creation_input_tokens").Int()
}
detail.TotalTokens = detail.InputTokens + detail.OutputTokens
return detail
return parseClaudeUsageNode(usageNode)
}
func ParseClaudeStreamUsage(line []byte) (usage.Detail, bool) {
@@ -378,16 +370,24 @@ func ParseClaudeStreamUsage(line []byte) (usage.Detail, bool) {
if !usageNode.Exists() {
return usage.Detail{}, false
}
return parseClaudeUsageNode(usageNode), true
}
func parseClaudeUsageNode(usageNode gjson.Result) usage.Detail {
cacheReadTokens := usageNode.Get("cache_read_input_tokens").Int()
cacheCreationTokens := usageNode.Get("cache_creation_input_tokens").Int()
detail := usage.Detail{
InputTokens: usageNode.Get("input_tokens").Int(),
OutputTokens: usageNode.Get("output_tokens").Int(),
CachedTokens: usageNode.Get("cache_read_input_tokens").Int(),
InputTokens: usageNode.Get("input_tokens").Int(),
OutputTokens: usageNode.Get("output_tokens").Int(),
CachedTokens: cacheReadTokens,
CacheReadTokens: cacheReadTokens,
CacheCreationTokens: cacheCreationTokens,
}
if detail.CachedTokens == 0 {
detail.CachedTokens = usageNode.Get("cache_creation_input_tokens").Int()
detail.CachedTokens = detail.CacheCreationTokens
}
detail.TotalTokens = detail.InputTokens + detail.OutputTokens
return detail, true
return detail
}
func parseGeminiFamilyUsageDetail(node gjson.Result) usage.Detail {