mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 23:32:12 +03:00
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 <koosha@phenotype.io> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
@@ -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)
|
||||
@@ -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 }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<string, number> = {};
|
||||
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;
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user