fix(usage_helpers): skip zero-token usage in additional model records

- Added `buildAdditionalModelRecord` to filter out zero-token usage details.
- Introduced `hasNonZeroTokenUsage` helper function for token usage validation.
- Updated tests to cover scenarios for zero and non-zero token usage.
This commit is contained in:
Luis Pater
2026-04-27 10:56:22 +08:00
parent a325533f20
commit 04a336f7df
2 changed files with 40 additions and 3 deletions

View File

@@ -49,15 +49,26 @@ func (r *UsageReporter) Publish(ctx context.Context, detail usage.Detail) {
}
func (r *UsageReporter) PublishAdditionalModel(ctx context.Context, model string, detail usage.Detail) {
if r == nil {
record, ok := r.buildAdditionalModelRecord(model, detail)
if !ok {
return
}
usage.PublishRecord(ctx, record)
}
func (r *UsageReporter) buildAdditionalModelRecord(model string, detail usage.Detail) (usage.Record, bool) {
if r == nil {
return usage.Record{}, false
}
model = strings.TrimSpace(model)
if model == "" {
return
return usage.Record{}, false
}
detail = normalizeUsageDetailTotal(detail)
usage.PublishRecord(ctx, r.buildRecordForModel(model, detail, false))
if !hasNonZeroTokenUsage(detail) {
return usage.Record{}, false
}
return r.buildRecordForModel(model, detail, false), true
}
func (r *UsageReporter) PublishFailure(ctx context.Context) {
@@ -93,6 +104,14 @@ func normalizeUsageDetailTotal(detail usage.Detail) usage.Detail {
return detail
}
func hasNonZeroTokenUsage(detail usage.Detail) bool {
return detail.InputTokens != 0 ||
detail.OutputTokens != 0 ||
detail.ReasoningTokens != 0 ||
detail.CachedTokens != 0 ||
detail.TotalTokens != 0
}
// ensurePublished guarantees that a usage record is emitted exactly once.
// It is safe to call multiple times; only the first call wins due to once.Do.
// This is used to ensure request counting even when upstream responses do not

View File

@@ -62,3 +62,21 @@ func TestUsageReporterBuildRecordIncludesLatency(t *testing.T) {
t.Fatalf("latency = %v, want <= 3s", record.Latency)
}
}
func TestUsageReporterBuildAdditionalModelRecordSkipsZeroTokens(t *testing.T) {
reporter := &UsageReporter{
provider: "codex",
model: "gpt-5.4",
requestedAt: time.Now(),
}
if _, ok := reporter.buildAdditionalModelRecord("gpt-image-2", usage.Detail{}); ok {
t.Fatalf("expected all-zero token usage to be skipped")
}
if _, ok := reporter.buildAdditionalModelRecord("gpt-image-2", usage.Detail{InputTokens: 2}); !ok {
t.Fatalf("expected non-zero input token usage to be recorded")
}
if _, ok := reporter.buildAdditionalModelRecord("gpt-image-2", usage.Detail{CachedTokens: 2}); !ok {
t.Fatalf("expected non-zero cached token usage to be recorded")
}
}