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:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user