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:
@@ -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) {
|
func (r *UsageReporter) PublishAdditionalModel(ctx context.Context, model string, detail usage.Detail) {
|
||||||
if r == nil {
|
record, ok := r.buildAdditionalModelRecord(model, detail)
|
||||||
|
if !ok {
|
||||||
return
|
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)
|
model = strings.TrimSpace(model)
|
||||||
if model == "" {
|
if model == "" {
|
||||||
return
|
return usage.Record{}, false
|
||||||
}
|
}
|
||||||
detail = normalizeUsageDetailTotal(detail)
|
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) {
|
func (r *UsageReporter) PublishFailure(ctx context.Context) {
|
||||||
@@ -93,6 +104,14 @@ func normalizeUsageDetailTotal(detail usage.Detail) usage.Detail {
|
|||||||
return 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.
|
// 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.
|
// 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
|
// This is used to ensure request counting even when upstream responses do not
|
||||||
|
|||||||
@@ -62,3 +62,21 @@ func TestUsageReporterBuildRecordIncludesLatency(t *testing.T) {
|
|||||||
t.Fatalf("latency = %v, want <= 3s", record.Latency)
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user