diff --git a/packages/openrouter-realm/model-cost.test.gts b/packages/openrouter-realm/model-cost.test.gts index 666b0ce9ebe..de5fc96bf21 100644 --- a/packages/openrouter-realm/model-cost.test.gts +++ b/packages/openrouter-realm/model-cost.test.gts @@ -79,7 +79,7 @@ export function runTests() { assert.strictEqual(modelCostTierLabel('0.000003 USD', '0'), undefined); }); - test('maps blended cost across the 1/5/20 tier bounds', function (assert) { + test('maps blended cost across the 1/5/15 tier bounds', function (assert) { // Encode a target blended $/M as a completion-only per-token price: // blended = (completion/4)*1e6 => completion = blended*4/1e6. Values are // kept off the exact bounds so floating-point wobble can't flip a tier. @@ -89,17 +89,24 @@ export function runTests() { assert.strictEqual(byBlended(1.1), '$$', 'just above $1/M'); assert.strictEqual(byBlended(4.9), '$$', 'just below $5/M'); assert.strictEqual(byBlended(5.1), '$$$', 'just above $5/M'); - assert.strictEqual(byBlended(19), '$$$', 'just below $20/M'); - assert.strictEqual(byBlended(21), '$$$$', 'just above $20/M'); - assert.strictEqual(byBlended(200), '$$$$', 'well above $20/M'); + assert.strictEqual(byBlended(14), '$$$', 'just below $15/M'); + assert.strictEqual(byBlended(16), '$$$$', 'just above $15/M'); + assert.strictEqual(byBlended(200), '$$$$', 'well above $15/M'); }); - test('bounds are inclusive: exactly 1/5/20 land in the lower tier', function (assert) { + test('bounds are inclusive: exactly 1/5/15 land in the lower tier', function (assert) { // Completion-only prices whose blended $/M is float-exact at each bound: - // (completion/4)*1e6 with 0.000004 / 0.00002 / 0.00008 => 1 / 5 / 20. + // (completion/4)*1e6 with 0.000004 / 0.00002 / 0.00006 => 1 / 5 / 15. assert.strictEqual(modelCostTierLabel('0', '0.000004'), '$'); assert.strictEqual(modelCostTierLabel('0', '0.00002'), '$$'); - assert.strictEqual(modelCostTierLabel('0', '0.00008'), '$$$'); + assert.strictEqual(modelCostTierLabel('0', '0.00006'), '$$$'); + }); + + test('float noise cannot tip an exact-bound price into the tier above', function (assert) { + // gpt-4-turbo: in $10/M out $30/M => blended exactly 15, but the raw + // float math yields 15.000000000000002. The blend rounds that noise + // away, so the price lands on the inclusive bound and stays $$$. + assert.strictEqual(modelCostTierLabel('0.00001', '0.00003'), '$$$'); }); test('classifies representative frontier models (tier + label)', function (assert) { @@ -118,6 +125,11 @@ export function runTests() { tier: 3, label: '$$$', }); + // claude-fable-5: in $10/M out $50/M => blended 20 => $$$$ + assert.deepEqual(modelCostTier('0.00001', '0.00005'), { + tier: 4, + label: '$$$$', + }); // o1: in $15/M out $60/M => blended 26.25 => $$$$ assert.deepEqual(modelCostTier('0.000015', '0.00006'), { tier: 4, diff --git a/packages/openrouter-realm/model-cost.ts b/packages/openrouter-realm/model-cost.ts index e0e10601b51..4b80d9f5388 100644 --- a/packages/openrouter-realm/model-cost.ts +++ b/packages/openrouter-realm/model-cost.ts @@ -14,8 +14,10 @@ // Upper bounds (inclusive) for the blended $/M cost of each dollar tier. Fixed, // human-round thresholds — not quantiles — so a model's badge stays stable as -// the catalog churns. Grounded in the live OpenRouter price distribution. -export const COST_TIER_UPPER_BOUNDS = [1, 5, 20] as const; // $, $$, $$$ (>20 => $$$$) +// the catalog churns. Grounded in the live OpenRouter price distribution; the +// top bound sits below premium-priced frontier models (blended $20/M and up) +// so they read as $$$$ rather than sharing $$$ with standard Opus-tier pricing. +export const COST_TIER_UPPER_BOUNDS = [1, 5, 15] as const; // $, $$, $$$ (>15 => $$$$) const DOLLAR_LABELS = ['$', '$$', '$$$', '$$$$'] as const; @@ -59,7 +61,12 @@ export function blendedCostPerMillion( (3 * (prompt === 'absent' ? 0 : prompt) + (completion === 'absent' ? 0 : completion)) / 4; - return perToken * 1_000_000; + // Round to a micro-dollar per million tokens. The blend of per-token decimal + // strings picks up binary float noise (e.g. $10/$30 blends to + // 15.000000000000002), which would tip a price sitting exactly on an + // inclusive tier bound into the tier above. No real price distinction lives + // below $0.000001/M, so rounding here only removes representation error. + return Math.round(perToken * 1_000_000 * 1_000_000) / 1_000_000; } // Maps OpenRouter pricing to a cost tier, or `undefined` when pricing is