diff --git a/src/pricing.ts b/src/pricing.ts index 9907dd3..3915f00 100644 --- a/src/pricing.ts +++ b/src/pricing.ts @@ -17,13 +17,15 @@ export const MODEL_PRICING: Record grok-4.3", repriced 2026-09-07/09). The old per-id list + // prices here overstated grok-3 / grok-4-0709 by 2.4x/6x and understated + // the fast family, so they now carry what the gateway actually charges. + 'xai/grok-4-fast': { input: 0.2, output: 0.5 }, // not in the gateway catalog — historical only + 'xai/grok-4-fast-reasoning': { input: 1.25, output: 2.5 }, // served as grok-4.3 + 'xai/grok-4-1-fast': { input: 0.2, output: 0.5 }, // not in the gateway catalog — historical only + 'xai/grok-4-1-fast-reasoning': { input: 1.25, output: 2.5 }, // served as grok-4.3 // The two non-reasoning ids sit in the shared Router's SIMPLE chains and - // were charged today, yet had no row here — Franklin reported them as $0 + // were charged, yet once had no row here — Franklin reported them as $0 // and the portfolio scorer, which treats an unpriced id as infinitely - // expensive, could never pick them over a priced rung. Priced with their - // reasoning siblings (xAI bills the pair identically). - 'xai/grok-4-1-fast-non-reasoning': { input: 0.2, output: 0.5 }, // (probed) - 'xai/grok-4-fast-non-reasoning': { input: 0.2, output: 0.5 }, // (probed) - 'xai/grok-4-0709': { input: 3.0, output: 15.0 }, // (probed) gateway lists $3/$15 (was mispriced here at $0.2/$1.5) - 'xai/grok-3-mini': { input: 0.3, output: 0.5 }, // (probed) - 'xai/grok-2-vision': { input: 2.0, output: 10.0 }, - 'xai/grok-3': { input: 3.0, output: 15.0 }, // (probed) + // expensive, could never pick them over a priced rung. + 'xai/grok-4-1-fast-non-reasoning': { input: 1.25, output: 2.5 }, // served as grok-4.3 + 'xai/grok-4-fast-non-reasoning': { input: 1.25, output: 2.5 }, // served as grok-4.3 + 'xai/grok-4-0709': { input: 1.25, output: 2.5 }, // served as grok-4.3 (was $3/$15) + 'xai/grok-3-mini': { input: 1.25, output: 2.5 }, // served as grok-4.3 + 'xai/grok-2-vision': { input: 1.25, output: 2.5 }, // served as grok-4.3 + 'xai/grok-3': { input: 1.25, output: 2.5 }, // served as grok-4.3 (was $3/$15) 'minimax/minimax-m2.5': { input: 0.3, output: 1.2 }, // K2.x (probed): the bare `kimi`/`k2.*` shortcuts resolve to K3, the // explicit ids still answer. 'moonshot/kimi-k2.7': { input: 0.95, output: 4.0 }, 'moonshot/kimi-k2.6': { input: 0.95, output: 4.0 }, 'moonshot/kimi-k2.5': { input: 0.6, output: 3.0 }, - 'nvidia/kimi-k2.5': { input: 0.55, output: 2.5 }, + 'nvidia/kimi-k2.5': { input: 0.6, output: 3.0 }, // gateway list (falls back to moonshot/kimi-k2.5, same price) 'zai/glm-5.1-turbo': { input: 1.2, output: 4.0 }, // was a client alias for zai/glm-5-turbo }; diff --git a/src/ui/model-picker.ts b/src/ui/model-picker.ts index 57c57dd..08e0b39 100644 --- a/src/ui/model-picker.ts +++ b/src/ui/model-picker.ts @@ -60,6 +60,9 @@ export const MODEL_SHORTCUTS: Record = { 'gpt-6-astra': 'openai/gpt-6-astra', 'gpt-6-sol': 'openai/gpt-6-sol', 'gpt-6-luna': 'openai/gpt-6-luna', + // GPT-6.1 Sol (live /api/v1/models 2026-10-20, Base + Solana): same $2/$10 + // as GPT-6 Sol with a newer knowledge cutoff. Explicit pin only. + 'gpt-6.1-sol': 'openai/gpt-6.1-sol', 'gpt-5.1': 'openai/gpt-5.1', 'gpt-5.6-sol': 'openai/gpt-5.6-sol', 'gpt-5.6-terra': 'openai/gpt-5.6-terra', @@ -124,6 +127,10 @@ export const MODEL_SHORTCUTS: Record = { 'gemini-3.5-flash': 'google/gemini-3.5-flash', 'gemini-2.5-flash': 'google/gemini-2.5-flash', 'gemini-3-flash-preview': 'google/gemini-3-flash-preview', + // Gemini 3.8 Flash (live 2026-10-20, both chains) — same $0.75/$3.75 as 3.6. + // Explicit pins only for now; `flash` is NOT moved in a catalog sync. + 'gemini-3.8': 'google/gemini-3.8-flash', + 'gemini-3.8-flash': 'google/gemini-3.8-flash', // Flash Lite — thinking-mode Gemini for high-throughput work. `flash-lite` // follows the newest (3.5); 3.1 is cheaper still and stays explicit. 'flash-lite': 'google/gemini-3.5-flash-lite', @@ -144,14 +151,14 @@ export const MODEL_SHORTCUTS: Record = { 'grok-4.6': 'xai/grok-4.6', 'grok-4.3': 'xai/grok-4.3', 'grok-build': 'xai/grok-build-0.1', - // grok-3 / grok-4-0709 / the grok-4-1-fast pair are hidden from - // /v1/models but still served and charged (probed 2026-08-29) — explicit - // pins keep resolving to the real ids. Note grok-3 and grok-4-0709 bill at - // $3/$15; `grok` (4.5) is the cheaper flagship. - 'grok-3': 'xai/grok-3', - 'grok-4': 'xai/grok-4-0709', - 'grok-fast': 'xai/grok-4-1-fast-reasoning', - 'grok-4.1': 'xai/grok-4-1-fast-reasoning', + // grok-3 / grok-4-0709 / the grok-4-1-fast pair are retired at xAI. The + // gateway pins every one of them to grok-4.3 and bills grok-4.3's rate + // (blockrun model-maps.ts, "Retired -> grok-4.3"), so these pins name what + // actually answers. `grok-4` is the generation alias and follows `grok`. + 'grok-3': 'xai/grok-4.3', + 'grok-4': 'xai/grok-4.5', + 'grok-fast': 'xai/grok-4.3', + 'grok-4.1': 'xai/grok-4.3', // DeepSeek — paid SKUs route through deepseek/* (gateway aliases serve V4 // Flash modes upstream); free tier routes through nvidia/*. deepseek: 'deepseek/deepseek-chat', // V4 Flash Chat (paid, $0.20/$0.40) @@ -160,6 +167,11 @@ export const MODEL_SHORTCUTS: Record = { 'deepseek-v4-pro': 'deepseek/deepseek-v4-pro', 'dsv4-pro': 'deepseek/deepseek-v4-pro', 'v4-pro': 'deepseek/deepseek-v4-pro', + // V4 Flash Vision (experimental, live 2026-10-20): the paid DeepSeek SKU + // that takes images, $0.30/$1.20. Names match the shared catalog policy. + 'deepseek-v4-flash-vision-exp': 'deepseek/deepseek-v4-flash-vision-exp', + 'deepseek-vision': 'deepseek/deepseek-v4-flash-vision-exp', + 'v4-flash-vision': 'deepseek/deepseek-v4-flash-vision-exp', // The free nvidia/deepseek-v4-flash SKU was EOL'd by the gateway (410). // Point the deepseek-free aliases at the current free default so muscle // memory keeps working without handing back a dead id. @@ -198,10 +210,14 @@ export const MODEL_SHORTCUTS: Record = { omni: FREE_DEFAULT_MODEL, 'free-omni': FREE_DEFAULT_MODEL, 'nano-omni': FREE_DEFAULT_MODEL, - // Explicit pins to other live free ids (both chains, 2026-10-02) — they + // Explicit pins to other live free ids (both chains, 2026-10-20) — they // resolve to the id the user typed, never to the default. laguna: 'poolside/laguna-xs-2.1', 'ultra-550b': 'nvidia/nemotron-3-ultra-550b', + // Meta's Muse Glimmer 30B, free on NVIDIA (gateway 2026-10-02). A reasoning + // model: give it a real max_tokens budget or it answers blank. + 'muse-glimmer': 'nvidia/muse-glimmer-30b', + 'muse-glimmer-30b': 'nvidia/muse-glimmer-30b', // Maverick left the gateway catalog on/before 2026-07-14 and the free pool // silently substitutes for it, so pointing users at it would promise a model // they don't get. Resolve to the current free default instead. @@ -220,6 +236,13 @@ export const MODEL_SHORTCUTS: Record = { 'qwen-max': 'qwen/qwen3.7-max', 'qwen3.7-max': 'qwen/qwen3.7-max', 'qwen-3.7-max': 'qwen/qwen3.7-max', + // Qwen 3.8 (live 2026-10-20, both chains). Max is $2/$6, Flash $0.15/$0.47 + // and takes images. Explicit pins — `qwen-max` / `qwen-flash` stay on 3.7. + 'qwen3.8-max': 'qwen/qwen3.8-max', + 'qwen-3.8-max': 'qwen/qwen3.8-max', + 'qwen3.8-flash': 'qwen/qwen3.8-flash', + 'qwen-3.8-flash': 'qwen/qwen3.8-flash', + 'qwen-vision': 'qwen/qwen3.8-flash', // Plus and Flash round out the paid Qwen line — both 1M context with // reasoning. Flash at $0.03/$0.13 is the cheapest paid model on the gateway. 'qwen-plus': 'qwen/qwen3.7-plus', @@ -249,11 +272,18 @@ export const MODEL_SHORTCUTS: Record = { 'glm-turbo': 'zai/glm-5-turbo', 'glm5': 'zai/glm-5.2', // Tencent + Xiaomi joined the gateway in 2026-08 — cheap reasoning at long - // context, below the frontier tier. - hy3: 'tencent/hy3', - tencent: 'tencent/hy3', + // context, below the frontier tier. tencent/hy3 was retired 2026-09-24 (the + // gateway now redirects it to qwen3.7-flash); Hy4 Preview is Tencent's live + // model, so `hy3` / `tencent` follow it the way the K2.x pins follow K3. + hy3: 'tencent/hy4-preview', + tencent: 'tencent/hy4-preview', + hy4: 'tencent/hy4-preview', + 'hy4-preview': 'tencent/hy4-preview', mimo: 'xiaomi/mimo-v2.5-pro', 'mimo-v2.5-pro': 'xiaomi/mimo-v2.5-pro', + // MiMo V2.5 (non-Pro): cheaper and the vision-capable one ($0.14/$0.28). + 'mimo-v2.5': 'xiaomi/mimo-v2.5', + 'mimo-vision': 'xiaomi/mimo-v2.5', xiaomi: 'xiaomi/mimo-v2.5-pro', kimi: 'moonshot/kimi-k3', k3: 'moonshot/kimi-k3', @@ -264,6 +294,16 @@ export const MODEL_SHORTCUTS: Record = { 'k2.6': 'moonshot/kimi-k3', 'kimi-k2.5': 'moonshot/kimi-k3', 'k2.5': 'moonshot/kimi-k3', + // Mistral Large 4 (live 2026-10-20, both chains): $0.68/$2.09, reasoning + + // vision. Bare `mistral` is safe to take — `mistral-small` / `mistral-nemotron` + // are the free aliases and stay free. + mistral: 'mistral/mistral-large-4', + 'mistral-large': 'mistral/mistral-large-4', + 'mistral-large-4': 'mistral/mistral-large-4', + // Poolside Laguna S 2.1 — the PAID sibling of the free laguna-xs ($0.09/$0.18). + // `laguna` stays on the free XS model: a free alias never moves to a paid one. + 'laguna-s': 'poolside/laguna-s-2.1', + 'laguna-s-2.1': 'poolside/laguna-s-2.1', }; /** @@ -344,6 +384,9 @@ const PROVIDER_ORDER = [ 'deepseek', 'tencent', 'xiaomi', + 'mistral', + 'poolside', + 'cohere', 'nvidia', ]; @@ -359,6 +402,9 @@ const PROVIDER_LABELS: Record = { deepseek: 'DeepSeek', tencent: 'Tencent / Hunyuan', xiaomi: 'Xiaomi / MiMo', + mistral: 'Mistral', + poolside: 'Poolside / Laguna', + cohere: 'Cohere', nvidia: 'Free / NVIDIA', }; diff --git a/test/local.mjs b/test/local.mjs index a2e234f..4145d52 100644 --- a/test/local.mjs +++ b/test/local.mjs @@ -6066,12 +6066,13 @@ test('picker trim: shortcuts for hidden models still resolve (muscle-memory pres // flagship-promotion pattern as kimi → k2.7. Explicit pins still resolve. assert.equal(resolveModel('grok'), 'xai/grok-4.5'); assert.equal(resolveModel('grok-4.3'), 'xai/grok-4.3'); - // grok-3 / grok-4-0709 / grok-4-1-fast are hidden from /v1/models but - // still served (probed 2026-08-29) — explicit pins resolve to the real ids. - assert.equal(resolveModel('grok-3'), 'xai/grok-3'); - assert.equal(resolveModel('grok-4'), 'xai/grok-4-0709'); - assert.equal(resolveModel('grok-fast'), 'xai/grok-4-1-fast-reasoning'); - assert.equal(resolveModel('grok-4.1'), 'xai/grok-4-1-fast-reasoning'); + // grok-3 / grok-4-0709 / grok-4-1-fast are retired at xAI; the gateway + // serves them all as grok-4.3 at grok-4.3's rate, so the pins name what + // answers. `grok-4` is the generation alias and follows `grok`. + assert.equal(resolveModel('grok-3'), 'xai/grok-4.3'); + assert.equal(resolveModel('grok-4'), 'xai/grok-4.5'); + assert.equal(resolveModel('grok-fast'), 'xai/grok-4.3'); + assert.equal(resolveModel('grok-4.1'), 'xai/grok-4.3'); assert.equal(resolveModel('grok-build'), 'xai/grok-build-0.1'); }); @@ -9460,9 +9461,11 @@ test('catalog sync 2026-08-29: hidden-but-served ids stay priced, routable and p resetUnavailableModels(); const served = { 'anthropic/claude-opus-4.6': 'opus-4.6', - 'xai/grok-4-0709': 'grok-4', - 'xai/grok-3': 'grok-3', - 'xai/grok-4-1-fast-reasoning': 'grok-fast', + // The retired xAI ids keep a price and stay routable by full id (the + // gateway answers them as grok-4.3); their short pins moved to grok-4.3. + 'xai/grok-4-0709': 'xai/grok-4-0709', + 'xai/grok-3': 'xai/grok-3', + 'xai/grok-4-1-fast-reasoning': 'xai/grok-4-1-fast-reasoning', 'moonshot/kimi-k2.7': 'moonshot/kimi-k2.7', 'openai/gpt-5-nano': 'nano', }; @@ -9521,3 +9524,40 @@ test('2026-10 gateway models: explicit shortcuts, prices, context windows, visio assert.deepEqual(MODEL_PRICING['openai/gpt-5.6-sol'], { input: 4, output: 20 }); assert.deepEqual(MODEL_PRICING['openai/gpt-5.6-sol-pro'], { input: 4, output: 20 }); }); + +test('catalog sync 2026-10-20: every live gateway id is priced and pinnable', async () => { + // Ids that reached GET /api/v1/models (Base and Solana) after the 2026-10-02 + // sync, at the live list prices read 2026-10-20. MODEL_PRICING is what cost + // estimates read, and an unknown id estimates as $0. + const { MODEL_PRICING } = await import('../dist/pricing.js'); + const { resolveModel } = await import('../dist/ui/model-picker.js'); + const live = { + 'openai/gpt-6.1-sol': [2, 10, 'gpt-6.1-sol'], + 'google/gemini-3.8-flash': [0.75, 3.75, 'gemini-3.8-flash'], + 'deepseek/deepseek-v4-flash-vision-exp': [0.3, 1.2, 'deepseek-vision'], + 'qwen/qwen3.8-max': [2, 6, 'qwen3.8-max'], + 'qwen/qwen3.8-flash': [0.15, 0.47, 'qwen3.8-flash'], + 'tencent/hy4-preview': [0.834, 2.501, 'hy4'], + 'xiaomi/mimo-v2.5': [0.14, 0.28, 'mimo-v2.5'], + 'poolside/laguna-s-2.1': [0.09, 0.18, 'laguna-s'], + 'mistral/mistral-large-4': [0.68, 2.09, 'mistral'], + 'nvidia/muse-glimmer-30b': [0, 0, 'muse-glimmer'], + }; + for (const [id, [input, output, pin]] of Object.entries(live)) { + assert.deepEqual(MODEL_PRICING[id], { input, output }, `${id} price drifted from the live list`); + assert.equal(resolveModel(pin), id, `${pin} must resolve to ${id}`); + } + // tencent/hy3 was retired 2026-09-24; the Tencent aliases follow Hy4. + assert.equal(resolveModel('hy3'), 'tencent/hy4-preview'); + assert.equal(resolveModel('tencent'), 'tencent/hy4-preview'); + // Free aliases never move to a paid model. + assert.equal(resolveModel('laguna'), 'poolside/laguna-xs-2.1'); +}); + +test('retired Grok slugs are priced at grok-4.3, which the gateway serves and bills', async () => { + const { MODEL_PRICING } = await import('../dist/pricing.js'); + for (const id of ['xai/grok-3', 'xai/grok-4-0709', 'xai/grok-4-1-fast-reasoning', + 'xai/grok-4-1-fast-non-reasoning', 'xai/grok-4-fast-non-reasoning']) { + assert.deepEqual(MODEL_PRICING[id], MODEL_PRICING['xai/grok-4.3'], `${id} must price as grok-4.3`); + } +});