mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(usage): honor xAI provider-reported exact cost (#6711)
OmniRoute's calculateCost() always estimated request cost from token counts x static pricing, discarding xAI's exact provider-reported cost when present. xAI's chat-completions usage object reports the precise billed cost via cost_in_usd_ticks (docs.x.ai/developers/cost-tracking and the API reference's usage schema: "TICKS_IN_USD_CENT: i64 = 100_000_000" => 1e10 ticks/USD, e.g. 37756000 ticks ~= $0.0038). calculateCost()/computeCostFromPricing() now short-circuit to this exact figure when present -- before any pricing DB lookup, so it also works for models without a local pricing row -- and still fall back to the token-based estimate when it is absent. The field is threaded through both the streaming (extractUsage/normalizeUsage) and non-streaming (extractUsageFromResponse) usage-extraction paths. Corrected divisor vs upstream: the upstream PR used /1e12 (a 100x under-report, e.g. reporting $0.00123 as the doc's $0.123 example); this port uses the doc-verified /1e10 instead, confirmed against both the cost-tracking guide and the API reference's usage-object schema. Inspired-by: https://github.com/decolua/9router/pull/2453 Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5e1a325e72
commit
427ee244a3
@@ -3370,6 +3370,7 @@ And thank you to the OmniRoute community for the bug reports, reproductions, and
|
||||
|
||||
### Fixed
|
||||
|
||||
- **usage:** use xAI's exact provider-reported cost when present instead of always estimating from token counts. (thanks @ryanngit)
|
||||
- **memory:** the `recent` retrieval strategy no longer drops recent memories whose
|
||||
text doesn't overlap the current prompt. It was internally mapped to the `exact`
|
||||
path, which relevance-filtered by the forwarded prompt (`score > 0`), so
|
||||
|
||||
@@ -31,6 +31,13 @@ export function extractUsageFromResponse(responseBody, provider) {
|
||||
responseBody.usage.completion_tokens_details?.reasoning_tokens ??
|
||||
responseBody.usage.output_tokens_details?.reasoning_tokens ??
|
||||
responseBody.usage.reasoning_tokens,
|
||||
// xAI's exact provider-reported cost (port of decolua/9router#2453, capability A —
|
||||
// @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) }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,9 @@ export function normalizeUsage(usage) {
|
||||
assignNumber("cache_creation_input_tokens", usage?.cache_creation_input_tokens);
|
||||
assignNumber("cached_tokens", usage?.cached_tokens);
|
||||
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);
|
||||
|
||||
if (Object.keys(normalized).length === 0) return null;
|
||||
return normalized;
|
||||
@@ -387,6 +390,8 @@ export function extractUsage(chunk) {
|
||||
chunk.usage.completion_tokens_details?.reasoning_tokens ??
|
||||
chunk.usage.output_tokens_details?.reasoning_tokens ??
|
||||
chunk.usage.reasoning_tokens,
|
||||
// xAI's exact provider-reported cost (port of decolua/9router#2453, capability A).
|
||||
cost_in_usd_ticks: chunk.usage.cost_in_usd_ticks,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,34 @@ export type CostCalculationOptions = {
|
||||
flatRateAsZero?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* xAI reports the exact provider-billed cost of a request in the chat-completions
|
||||
* `usage` object via `cost_in_usd_ticks` (port of decolua/9router#2453, capability
|
||||
* A — @ryanngit). Per the official docs — both
|
||||
* https://docs.x.ai/developers/cost-tracking and the API reference's usage schema
|
||||
* ("TICKS_IN_USD_CENT: i64 = 100_000_000") — there are 10_000_000_000 (1e10) ticks
|
||||
* per USD. Example from the docs: 37756000 ticks ≈ $0.0038.
|
||||
*
|
||||
* NOTE: this divisor is intentionally 1e10, not the 1e12 used by the upstream PR
|
||||
* (which under-reports cost 100x) — verified directly against the xAI docs.
|
||||
*/
|
||||
const USD_TICKS_PER_DOLLAR = 10_000_000_000;
|
||||
|
||||
/**
|
||||
* Extract an exact, provider-reported USD cost from a token/usage record when one
|
||||
* is present, so callers can trust it over the token × pricing estimate. Currently
|
||||
* only xAI's `cost_in_usd_ticks` field is handled — see comment above.
|
||||
*/
|
||||
function extractExactCostUsd(
|
||||
tokens: Record<string, number | undefined> | null | undefined
|
||||
): number | null {
|
||||
const ticks = tokens?.cost_in_usd_ticks;
|
||||
if (typeof ticks === "number" && Number.isFinite(ticks) && ticks >= 0) {
|
||||
return ticks / USD_TICKS_PER_DOLLAR;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function toNumber(value: unknown, fallback = 0): number {
|
||||
if (typeof value === "number" && Number.isFinite(value)) return value;
|
||||
if (typeof value === "string" && value.trim().length > 0) {
|
||||
@@ -95,7 +123,12 @@ export function computeCostFromPricing(
|
||||
tokens: Record<string, number | undefined> | null | undefined,
|
||||
options: CostCalculationOptions = {}
|
||||
): number {
|
||||
if (!pricing || !tokens) return 0;
|
||||
if (!tokens) return 0;
|
||||
// Trust an exact, provider-reported cost over the token × pricing estimate
|
||||
// when one is present — works even when no local pricing row exists yet.
|
||||
const exactCostUsd = extractExactCostUsd(tokens);
|
||||
if (exactCostUsd !== null) return exactCostUsd;
|
||||
if (!pricing) return 0;
|
||||
// Flat-rate (subscription / cookie-web) providers don't bill per token — their
|
||||
// per-token pricing rows exist only for estimation, so display surfaces opt in
|
||||
// to show $0 instead of an inflated estimate (#5552).
|
||||
@@ -138,6 +171,11 @@ export async function calculateCost(
|
||||
): Promise<number> {
|
||||
if (!tokens || !provider || !model) return 0;
|
||||
|
||||
// Short-circuit before any pricing DB lookup when an exact, provider-reported
|
||||
// cost is present (currently xAI's `cost_in_usd_ticks` — see extractExactCostUsd).
|
||||
const exactCostUsd = extractExactCostUsd(tokens);
|
||||
if (exactCostUsd !== null) return exactCostUsd;
|
||||
|
||||
try {
|
||||
const { getPricingForModel } = await import("@/lib/localDb");
|
||||
|
||||
|
||||
138
tests/unit/xai-exact-cost-2453.test.ts
Normal file
138
tests/unit/xai-exact-cost-2453.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* xAI exact provider-reported cost passthrough (port of decolua/9router#2453,
|
||||
* capability A — @ryanngit).
|
||||
*
|
||||
* xAI's chat-completions `usage` object reports the exact billed cost of a
|
||||
* request via `cost_in_usd_ticks`. Per the official docs
|
||||
* (https://docs.x.ai/developers/cost-tracking and the API reference's usage
|
||||
* schema): "TICKS_IN_USD_CENT: i64 = 100_000_000" ⇒ 10_000_000_000 (1e10)
|
||||
* ticks per USD. Example given in the docs: 37756000 ticks ≈ $0.0038.
|
||||
*
|
||||
* NOTE: the upstream PR used a /1e12 divisor (100x under-report) — this port
|
||||
* uses the doc-verified /1e10 divisor instead.
|
||||
*
|
||||
* OmniRoute previously always estimated cost from token counts × static
|
||||
* pricing, discarding this exact figure. This test proves calculateCost()/
|
||||
* computeCostFromPricing() now trust the exact figure when present, and
|
||||
* still fall back to the token-based estimate when it is absent (control).
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { calculateCost, computeCostFromPricing } from "../../src/lib/usage/costCalculator.ts";
|
||||
import { extractUsageFromResponse } from "../../open-sse/handlers/usageExtractor.ts";
|
||||
import { extractUsage, normalizeUsage } from "../../open-sse/utils/usageTracking.ts";
|
||||
|
||||
// $1/1M input, $2/1M output → 1M+1M tokens would estimate to $3 at the
|
||||
// metered rate. Chosen so the exact-cost value (~$0.0038) is unmistakably
|
||||
// NOT the token-based estimate — proves the early return actually fires.
|
||||
const PRICING = { input: 1, output: 2 };
|
||||
const TOKENS_1M_EACH = { input: 1_000_000, output: 1_000_000 };
|
||||
|
||||
// Doc example: 37756000 ticks ≈ $0.0038 (docs.x.ai/developers/cost-tracking).
|
||||
const DOC_EXAMPLE_TICKS = 37_756_000;
|
||||
const DOC_EXAMPLE_USD = 0.0037756; // 37756000 / 1e10, exact
|
||||
|
||||
test("computeCostFromPricing: xAI exact cost_in_usd_ticks overrides the token-based estimate", () => {
|
||||
const cost = computeCostFromPricing(PRICING, {
|
||||
...TOKENS_1M_EACH,
|
||||
cost_in_usd_ticks: DOC_EXAMPLE_TICKS,
|
||||
});
|
||||
assert.ok(
|
||||
Math.abs(cost - DOC_EXAMPLE_USD) < 1e-9,
|
||||
`expected ${DOC_EXAMPLE_USD}, got ${cost}`
|
||||
);
|
||||
assert.notEqual(cost, 3, "must not fall back to the $3 token-based estimate");
|
||||
});
|
||||
|
||||
test("computeCostFromPricing: xAI exact cost works even with no pricing record at all", () => {
|
||||
const cost = computeCostFromPricing(null, {
|
||||
...TOKENS_1M_EACH,
|
||||
cost_in_usd_ticks: DOC_EXAMPLE_TICKS,
|
||||
});
|
||||
assert.ok(Math.abs(cost - DOC_EXAMPLE_USD) < 1e-9);
|
||||
});
|
||||
|
||||
test("computeCostFromPricing CONTROL: no cost_in_usd_ticks still falls back to the token-based estimate", () => {
|
||||
assert.equal(computeCostFromPricing(PRICING, TOKENS_1M_EACH), 3);
|
||||
});
|
||||
|
||||
test("calculateCost: xAI exact cost_in_usd_ticks overrides whatever the token-based estimate would be", async () => {
|
||||
// Baseline: the token-based estimate calculateCost would otherwise compute
|
||||
// for this provider/model/token-count (whatever xai/grok-4.3's local
|
||||
// pricing table says — not hardcoded here, so this test doesn't break if
|
||||
// pricing data changes).
|
||||
const baseline = await calculateCost("xai", "grok-4.3", { input: 500, output: 500 });
|
||||
|
||||
const cost = await calculateCost("xai", "grok-4.3", {
|
||||
input: 500,
|
||||
output: 500,
|
||||
cost_in_usd_ticks: DOC_EXAMPLE_TICKS,
|
||||
});
|
||||
|
||||
assert.ok(Math.abs(cost - DOC_EXAMPLE_USD) < 1e-9, `expected ${DOC_EXAMPLE_USD}, got ${cost}`);
|
||||
assert.notEqual(cost, baseline, "exact cost must override the token-based estimate");
|
||||
});
|
||||
|
||||
test("calculateCost CONTROL: no cost_in_usd_ticks still falls back to the token-based estimate (unchanged)", async () => {
|
||||
const before = await calculateCost("xai", "grok-4.3", { input: 500, output: 500 });
|
||||
const after = await calculateCost("xai", "grok-4.3", { input: 500, output: 500 });
|
||||
assert.equal(after, before, "identical calls without the exact field must stay deterministic");
|
||||
assert.notEqual(after, DOC_EXAMPLE_USD, "must not coincidentally match the exact-cost value");
|
||||
});
|
||||
|
||||
test("normalizeUsage: passes through a finite cost_in_usd_ticks", () => {
|
||||
const normalized = normalizeUsage({ prompt_tokens: 10, cost_in_usd_ticks: DOC_EXAMPLE_TICKS });
|
||||
assert.equal(normalized.cost_in_usd_ticks, DOC_EXAMPLE_TICKS);
|
||||
});
|
||||
|
||||
test("normalizeUsage: drops a non-finite cost_in_usd_ticks", () => {
|
||||
const normalized = normalizeUsage({ prompt_tokens: 10, cost_in_usd_ticks: "not-a-number" });
|
||||
assert.equal(normalized.cost_in_usd_ticks, undefined);
|
||||
});
|
||||
|
||||
test("extractUsageFromResponse: xAI OpenAI-shaped usage carries cost_in_usd_ticks through", () => {
|
||||
const usage = extractUsageFromResponse(
|
||||
{
|
||||
usage: {
|
||||
prompt_tokens: 12,
|
||||
completion_tokens: 8,
|
||||
cost_in_usd_ticks: DOC_EXAMPLE_TICKS,
|
||||
},
|
||||
},
|
||||
"xai"
|
||||
);
|
||||
assert.equal(usage.cost_in_usd_ticks, DOC_EXAMPLE_TICKS);
|
||||
});
|
||||
|
||||
test("extractUsageFromResponse CONTROL: non-xAI OpenAI usage without the field stays unchanged (no stray key)", () => {
|
||||
const usage = extractUsageFromResponse(
|
||||
{
|
||||
usage: {
|
||||
prompt_tokens: 12,
|
||||
completion_tokens: 8,
|
||||
prompt_tokens_details: { cached_tokens: 3 },
|
||||
completion_tokens_details: { reasoning_tokens: 2 },
|
||||
},
|
||||
},
|
||||
"openai"
|
||||
);
|
||||
assert.deepEqual(usage, {
|
||||
prompt_tokens: 12,
|
||||
completion_tokens: 8,
|
||||
cached_tokens: 3,
|
||||
reasoning_tokens: 2,
|
||||
});
|
||||
assert.ok(!("cost_in_usd_ticks" in usage), "must not add a stray undefined key");
|
||||
});
|
||||
|
||||
test("extractUsage (streaming): xAI OpenAI-format chunk carries cost_in_usd_ticks through", () => {
|
||||
const usage = extractUsage({
|
||||
usage: {
|
||||
prompt_tokens: 12,
|
||||
completion_tokens: 8,
|
||||
cost_in_usd_ticks: DOC_EXAMPLE_TICKS,
|
||||
},
|
||||
});
|
||||
assert.equal(usage.cost_in_usd_ticks, DOC_EXAMPLE_TICKS);
|
||||
});
|
||||
Reference in New Issue
Block a user