diff --git a/open-sse/utils/stream.ts b/open-sse/utils/stream.ts index 3741510236..154192463b 100644 --- a/open-sse/utils/stream.ts +++ b/open-sse/utils/stream.ts @@ -8,6 +8,7 @@ import { logUsage, addBufferToUsage, filterUsageForFormat, + normalizeUsage as normalizeTokenUsage, } from "./usageTracking.ts"; import { parseSSELine, @@ -1012,7 +1013,9 @@ export function createSSEStream(options: StreamOptions = {}) { // JSON.parse every SSE line will crash on `: x-omniroute-*` comment lines. if (!sseCommentsEnabled()) return; - const costUsd = finalUsage ? await calculateCost(provider, model, finalUsage) : 0; + const costUsd = finalUsage + ? await calculateCost(provider, model, normalizeTokenUsage(finalUsage)) + : 0; const comment = buildOmniRouteSseMetadataComment({ provider, model, diff --git a/tests/unit/usage-extractor.test.ts b/tests/unit/usage-extractor.test.ts index cc81322cac..6fa9c7b100 100644 --- a/tests/unit/usage-extractor.test.ts +++ b/tests/unit/usage-extractor.test.ts @@ -2,7 +2,19 @@ import test from "node:test"; import assert from "node:assert/strict"; const { extractUsageFromResponse } = await import("../../open-sse/handlers/usageExtractor.ts"); -const { extractUsage } = await import("../../open-sse/utils/usageTracking.ts"); +const { extractUsage, normalizeUsage } = await import("../../open-sse/utils/usageTracking.ts"); + +test("normalizeUsage keeps only finite numeric fields for stream cost calculation", () => { + assert.deepEqual( + normalizeUsage({ + input_tokens: "12", + output_tokens: 3, + total_tokens: Number.POSITIVE_INFINITY, + input_tokens_details: { cached_tokens: 2 }, + }), + { input_tokens: 12, output_tokens: 3 } + ); +}); test("extractUsageFromResponse reads OpenAI chat completion usage", () => { const usage = extractUsageFromResponse(