From 37ea7aab2c14601d762e80ca8ef139e78a231630 Mon Sep 17 00:00:00 2001 From: KooshaPari <42529354+KooshaPari@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:04:20 -0700 Subject: [PATCH] fix(usage): strict validation for xAI exact provider-reported cost (#6856) extractUsageFromResponse() and normalizeUsage() used Number(x) coercion for cost_in_usd_ticks, which silently turned null/"" into 0 -- accepted downstream as a valid $0 exact cost instead of falling back to the token-based estimate. Both call sites now require typeof === "number" && Number.isFinite && >= 0. Rebased onto current release/v3.8.47 tip (already carries #6711) and trimmed to just the incremental validation fix + 2 regression tests, replacing the stale-base diff that re-added the whole already-merged feature. Co-authored-by: KooshaPari Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --- ...6856-usage-exact-cost-strict-validation.md | 1 + open-sse/handlers/usageExtractor.ts | 6 +++-- open-sse/utils/usageTracking.ts | 11 ++++++-- tests/unit/xai-exact-cost-2453.test.ts | 26 +++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 changelog.d/fixes/6856-usage-exact-cost-strict-validation.md diff --git a/changelog.d/fixes/6856-usage-exact-cost-strict-validation.md b/changelog.d/fixes/6856-usage-exact-cost-strict-validation.md new file mode 100644 index 0000000000..abc5c3bc84 --- /dev/null +++ b/changelog.d/fixes/6856-usage-exact-cost-strict-validation.md @@ -0,0 +1 @@ +- **fix(usage):** xAI's exact provider-reported `cost_in_usd_ticks` no longer silently accepts `null`/`""`/negative values as a valid `$0` exact cost — `extractUsageFromResponse()` (`open-sse/handlers/usageExtractor.ts`) and `normalizeUsage()` (`open-sse/utils/usageTracking.ts`) now require `typeof value === "number" && Number.isFinite(value) && value >= 0` instead of coercing with `Number(x)`, so a malformed exact cost correctly falls back to the token-based estimate instead of masking it with a bogus `$0`. Regression guard: `tests/unit/xai-exact-cost-2453.test.ts` (rejects null/empty/negative exact costs on both call sites). ([#6856](https://github.com/diegosouzapw/OmniRoute/pull/6856) — thanks @KooshaPari) diff --git a/open-sse/handlers/usageExtractor.ts b/open-sse/handlers/usageExtractor.ts index d68013e720..114ccefa50 100644 --- a/open-sse/handlers/usageExtractor.ts +++ b/open-sse/handlers/usageExtractor.ts @@ -35,8 +35,10 @@ export function extractUsageFromResponse(responseBody, provider) { // @ryanngit). Only set the key when present so non-xAI OpenAI-shaped usage // (Codex, DeepSeek, etc.) is unaffected. Ticks → USD conversion happens in // costCalculator.ts, not here. - ...(Number.isFinite(Number(responseBody.usage.cost_in_usd_ticks)) - ? { cost_in_usd_ticks: Number(responseBody.usage.cost_in_usd_ticks) } + ...(typeof responseBody.usage.cost_in_usd_ticks === "number" && + Number.isFinite(responseBody.usage.cost_in_usd_ticks) && + responseBody.usage.cost_in_usd_ticks >= 0 + ? { cost_in_usd_ticks: responseBody.usage.cost_in_usd_ticks } : {}), }; } diff --git a/open-sse/utils/usageTracking.ts b/open-sse/utils/usageTracking.ts index 6aaacb04c5..116bdfe575 100644 --- a/open-sse/utils/usageTracking.ts +++ b/open-sse/utils/usageTracking.ts @@ -253,7 +253,7 @@ export function filterUsageForFormat(usage, targetFormat) { export function normalizeUsage(usage) { if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null; - const normalized = {}; + const normalized: Record = {}; const assignNumber = (key, value) => { if (value === undefined || value === null) return; const numeric = Number(value); @@ -271,7 +271,14 @@ export function normalizeUsage(usage) { assignNumber("reasoning_tokens", usage?.reasoning_tokens); // xAI's exact provider-reported cost (port of decolua/9router#2453, capability A — // @ryanngit). Ticks → USD conversion happens in costCalculator.ts, not here. - assignNumber("cost_in_usd_ticks", usage?.cost_in_usd_ticks); + const exactCostTicks = usage?.cost_in_usd_ticks; + if ( + typeof exactCostTicks === "number" && + Number.isFinite(exactCostTicks) && + exactCostTicks >= 0 + ) { + normalized.cost_in_usd_ticks = exactCostTicks; + } if (Object.keys(normalized).length === 0) return null; return normalized; diff --git a/tests/unit/xai-exact-cost-2453.test.ts b/tests/unit/xai-exact-cost-2453.test.ts index 8bca0fa08f..085a5305de 100644 --- a/tests/unit/xai-exact-cost-2453.test.ts +++ b/tests/unit/xai-exact-cost-2453.test.ts @@ -91,6 +91,32 @@ test("normalizeUsage: drops a non-finite cost_in_usd_ticks", () => { assert.equal(normalized.cost_in_usd_ticks, undefined); }); +test("normalizeUsage: rejects null, empty, and negative exact costs", () => { + for (const value of [null, "", -1]) { + const normalized = normalizeUsage({ prompt_tokens: 10, cost_in_usd_ticks: value }); + assert.equal(normalized.cost_in_usd_ticks, undefined, `unexpected exact cost for ${value}`); + } +}); + +test("extractUsageFromResponse: rejects malformed exact cost values", () => { + for (const value of [null, "", -1]) { + const usage = extractUsageFromResponse( + { + usage: { + prompt_tokens: 12, + completion_tokens: 8, + cost_in_usd_ticks: value, + }, + }, + "xai" + ); + assert.ok( + !("cost_in_usd_ticks" in usage), + `must not add cost_in_usd_ticks for malformed value ${value}` + ); + } +}); + test("extractUsageFromResponse: xAI OpenAI-shaped usage carries cost_in_usd_ticks through", () => { const usage = extractUsageFromResponse( {