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
+4
View File
@@ -53,6 +53,8 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec
OutputTokens: record.Detail.OutputTokens, OutputTokens: record.Detail.OutputTokens,
ReasoningTokens: record.Detail.ReasoningTokens, ReasoningTokens: record.Detail.ReasoningTokens,
CachedTokens: record.Detail.CachedTokens, CachedTokens: record.Detail.CachedTokens,
CacheReadTokens: record.Detail.CacheReadTokens,
CacheCreationTokens: record.Detail.CacheCreationTokens,
TotalTokens: record.Detail.TotalTokens, TotalTokens: record.Detail.TotalTokens,
} }
if tokens.TotalTokens == 0 { if tokens.TotalTokens == 0 {
@@ -120,6 +122,8 @@ type tokenStats struct {
OutputTokens int64 `json:"output_tokens"` OutputTokens int64 `json:"output_tokens"`
ReasoningTokens int64 `json:"reasoning_tokens"` ReasoningTokens int64 `json:"reasoning_tokens"`
CachedTokens int64 `json:"cached_tokens"` CachedTokens int64 `json:"cached_tokens"`
CacheReadTokens int64 `json:"cache_read_tokens"`
CacheCreationTokens int64 `json:"cache_creation_tokens"`
TotalTokens int64 `json:"total_tokens"` TotalTokens int64 `json:"total_tokens"`
} }
@@ -116,6 +116,8 @@ func hasNonZeroTokenUsage(detail usage.Detail) bool {
detail.OutputTokens != 0 || detail.OutputTokens != 0 ||
detail.ReasoningTokens != 0 || detail.ReasoningTokens != 0 ||
detail.CachedTokens != 0 || detail.CachedTokens != 0 ||
detail.CacheReadTokens != 0 ||
detail.CacheCreationTokens != 0 ||
detail.TotalTokens != 0 detail.TotalTokens != 0
} }
@@ -356,17 +358,7 @@ func ParseClaudeUsage(data []byte) usage.Detail {
if !usageNode.Exists() { if !usageNode.Exists() {
return usage.Detail{} return usage.Detail{}
} }
detail := usage.Detail{ return parseClaudeUsageNode(usageNode)
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
} }
func ParseClaudeStreamUsage(line []byte) (usage.Detail, bool) { func ParseClaudeStreamUsage(line []byte) (usage.Detail, bool) {
@@ -378,16 +370,24 @@ func ParseClaudeStreamUsage(line []byte) (usage.Detail, bool) {
if !usageNode.Exists() { if !usageNode.Exists() {
return usage.Detail{}, false 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{ detail := usage.Detail{
InputTokens: usageNode.Get("input_tokens").Int(), InputTokens: usageNode.Get("input_tokens").Int(),
OutputTokens: usageNode.Get("output_tokens").Int(), OutputTokens: usageNode.Get("output_tokens").Int(),
CachedTokens: usageNode.Get("cache_read_input_tokens").Int(), CachedTokens: cacheReadTokens,
CacheReadTokens: cacheReadTokens,
CacheCreationTokens: cacheCreationTokens,
} }
if detail.CachedTokens == 0 { if detail.CachedTokens == 0 {
detail.CachedTokens = usageNode.Get("cache_creation_input_tokens").Int() detail.CachedTokens = detail.CacheCreationTokens
} }
detail.TotalTokens = detail.InputTokens + detail.OutputTokens detail.TotalTokens = detail.InputTokens + detail.OutputTokens
return detail, true return detail
} }
func parseGeminiFamilyUsageDetail(node gjson.Result) usage.Detail { func parseGeminiFamilyUsageDetail(node gjson.Result) usage.Detail {
+2
View File
@@ -38,6 +38,8 @@ type Detail struct {
OutputTokens int64 OutputTokens int64
ReasoningTokens int64 ReasoningTokens int64
CachedTokens int64 CachedTokens int64
CacheReadTokens int64
CacheCreationTokens int64
TotalTokens int64 TotalTokens int64
} }