Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ const LEGACY_PRICING: Record<string, { input: number; output: number; perCall?:
// again upstream 2026-08-07 to $0.14/$0.28 — mirrored here 2026-08-12).
'deepseek/deepseek-chat': { input: 0.14, output: 0.28 },
'deepseek/deepseek-reasoner': { input: 0.14, output: 0.28 },
// V4.1 Flash — live on Base 2026-10-20; the same model and price as deepseek-chat.
'deepseek/deepseek-v4.1-flash': { input: 0.14, output: 0.28 },
// V4 Pro (1.6T MoE / 49B active, 1M ctx, 65K out). Live list 2026-10-02;
// the old $0.435/$0.87 promo figure no longer matches the gateway.
'deepseek/deepseek-v4-pro': { input: 1.32, output: 3.96 },
Expand Down
7 changes: 7 additions & 0 deletions src/router/vision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ const VISION_MODELS = new Set<string>([
'openai/gpt-6-sol',
'openai/gpt-6-luna',
'openai/gpt-5.1',
// 2026-10-20 catalog sync — every new id the live catalog tags vision.
'openai/gpt-6.1-sol',
'google/gemini-3.8-flash',
'deepseek/deepseek-v4-flash-vision-exp',
'qwen/qwen3.8-flash',
'xiaomi/mimo-v2.5',
'mistral/mistral-large-4',
// Google — vision baked into every Gemini SKU we surface (flash-lite excepted)
'google/gemini-3.1-pro',
'google/gemini-3.6-flash',
Expand Down
42 changes: 25 additions & 17 deletions src/ui/model-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,19 @@ const LEGACY_SHORTCUTS: Record<string, string> = {
gemini: 'google/gemini-3.1-pro',
'gemini-2.5': 'google/gemini-2.5-pro',
'gemini-2.5-pro': 'google/gemini-2.5-pro',
// `flash` follows the newest Flash generation (3.6 since 2026-08). 3.5 stays
// reachable by its explicit id — it is dearer on output ($9 vs $7.5) with no
// capability edge, so nothing should be pinned to it deliberately.
flash: 'google/gemini-3.6-flash',
'gemini-flash': 'google/gemini-3.6-flash',
// `flash` follows the newest Flash generation — 3.8 since 2026-10-20, at
// the same $0.75/$3.75 as 3.6 and live on both chains. 3.6 stays reachable
// as `gemini-3.6`. 3.5 stays reachable by its explicit id — it is dearer
// ($1.5/$9) with no capability edge, so nothing should be pinned to it
// deliberately.
flash: 'google/gemini-3.8-flash',
'gemini-flash': 'google/gemini-3.8-flash',
'gemini-3.6': 'google/gemini-3.6-flash',
'gemini-3.6-flash': 'google/gemini-3.6-flash',
'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`
Expand Down Expand Up @@ -160,6 +161,16 @@ const LEGACY_SHORTCUTS: Record<string, string> = {
'deepseek-v4-pro': 'deepseek/deepseek-v4-pro',
'dsv4-pro': 'deepseek/deepseek-v4-pro',
'v4-pro': 'deepseek/deepseek-v4-pro',
// V4.1 Flash (live on Base 2026-10-20, $0.14/$0.28): the gateway's own name
// for the model deepseek-chat already serves — same upstream, same price,
// same non-thinking default. `deepseek` deliberately stays on deepseek-chat:
// sol.blockrun.ai does not list v4.1-flash yet, and a bare alias has to
// resolve on both chains (the `glm` rule). Explicit pins only until Solana
// lists it. NOTE `deepseek-v4-flash` / `dsv4` are FREE aliases (below) and
// must not move to this paid id.
'deepseek-v4.1-flash': 'deepseek/deepseek-v4.1-flash',
'deepseek-v4.1': 'deepseek/deepseek-v4.1-flash',
'v4.1-flash': 'deepseek/deepseek-v4.1-flash',
// 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',
Expand Down Expand Up @@ -250,16 +261,13 @@ const LEGACY_SHORTCUTS: Record<string, string> = {
'qwen-flash': 'qwen/qwen3.7-flash',
'qwen3.7-flash': 'qwen/qwen3.7-flash',
// GLM-5.3 (2026-08) is Z.AI's flagship — same $1.4/$4.4 as 5.2 with 1M
// context and always-on reasoning — but it ships on the BASE gateway ONLY
// (sol.blockrun.ai lists 92 of Base's 93 models, and 5.3 is the one it's
// missing; verified 2026-08-20).
//
// So `glm` deliberately does NOT follow the flagship here. A bare alias is
// what people type from muscle memory, and it has to resolve on BOTH chains
// — pointing it at a Base-only id would hand every Solana user an HTTP 400
// on `/model glm`. 5.2 is on both, identically priced, one generation back.
// Promote this line the day 5.3 lands on Solana.
glm: 'zai/glm-5.2',
// context and always-on reasoning. It shipped Base-only, so `glm` held at 5.2
// (a bare alias has to resolve on BOTH chains, or every Solana user gets an
// HTTP 400 on `/model glm`) with a note to promote it the day 5.3 reached
// Solana. It has: sol.blockrun.ai lists zai/glm-5.3 at $1.4/$4.4 (verified
// 2026-10-20), so `glm` / `glm5` now follow the flagship like `gpt`, `grok`
// and `gemini`. 5.2 stays reachable as `glm-5.2`.
glm: 'zai/glm-5.3',
'glm-5': 'zai/glm-5',
'glm-5.3': 'zai/glm-5.3',
// GLM-5.3 Flash (2026-08-29 catalog sync): 1M ctx, vision, $0.15/$0.5.
Expand All @@ -270,7 +278,7 @@ const LEGACY_SHORTCUTS: Record<string, string> = {
// routes for anyone who wants the 200K-context build explicitly.
'glm-5.1': 'zai/glm-5.1',
'glm-turbo': 'zai/glm-5-turbo',
'glm5': 'zai/glm-5.2',
'glm5': 'zai/glm-5.3',
// Tencent + Xiaomi joined the gateway in 2026-08 — cheap reasoning at long
// 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
Expand Down
49 changes: 49 additions & 0 deletions test/local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12517,3 +12517,52 @@ test('catalog sync 2026-10-20: every live gateway id is priced and pinnable', as
assert.equal(isFreeModelId('nvidia/nemotron-3-nano-30b'), true,
'a delisted free id still prices old sessions at $0');
});

test('2026-10-20: bare flash / glm follow their flagships, price-neutral and on both chains', async () => {
const { resolveModel } = await import('../dist/ui/model-picker.js');
const { MODEL_PRICING } = await import('../dist/pricing.js');
const { isVisionModel } = await import('../dist/router/vision.js');
assert.equal(resolveModel('flash'), 'google/gemini-3.8-flash');
assert.equal(resolveModel('gemini-flash'), 'google/gemini-3.8-flash');
assert.equal(resolveModel('gemini-3.6'), 'google/gemini-3.6-flash', '3.6 stays pinnable');
assert.deepEqual(MODEL_PRICING['google/gemini-3.8-flash'], MODEL_PRICING['google/gemini-3.6-flash']);
assert.equal(resolveModel('glm'), 'zai/glm-5.3');
assert.equal(resolveModel('glm5'), 'zai/glm-5.3');
assert.equal(resolveModel('glm-5.2'), 'zai/glm-5.2', '5.2 stays pinnable');
assert.deepEqual(MODEL_PRICING['zai/glm-5.3'], MODEL_PRICING['zai/glm-5.2']);
// `qwen-max` deliberately stays on 3.7: 3.8 Max is dearer ($2/$6 vs $1.475/$4.425).
assert.equal(resolveModel('qwen-max'), 'qwen/qwen3.7-max');
// A vision-tagged new id must be recognised as vision, or an image turn on
// `/model flash` would be swapped to a "vision sibling" the user never chose.
for (const id of ['openai/gpt-6.1-sol', 'google/gemini-3.8-flash', 'deepseek/deepseek-v4-flash-vision-exp',
'qwen/qwen3.8-flash', 'xiaomi/mimo-v2.5', 'mistral/mistral-large-4']) {
assert.equal(isVisionModel(id), true, `${id} is vision-tagged in the live catalog`);
}
for (const id of ['qwen/qwen3.8-max', 'tencent/hy4-preview', 'poolside/laguna-s-2.1']) {
assert.equal(isVisionModel(id), false, `${id} is text-only`);
}
});

test('2026-10-20: deepseek-v4.1-flash is priced and pinnable; the dead nvidia DeepSeek SKUs are not free', async () => {
const { resolveModel } = await import('../dist/ui/model-picker.js');
const { MODEL_PRICING } = await import('../dist/pricing.js');
assert.deepEqual(MODEL_PRICING['deepseek/deepseek-v4.1-flash'], { input: 0.14, output: 0.28 });
assert.equal(resolveModel('deepseek-v4.1-flash'), 'deepseek/deepseek-v4.1-flash');
assert.equal(resolveModel('v4.1-flash'), 'deepseek/deepseek-v4.1-flash');
// Base-only for now, so the bare alias stays on the both-chains twin.
assert.equal(resolveModel('deepseek'), 'deepseek/deepseek-chat');
// Free aliases never move to a paid model.
assert.notEqual(resolveModel('deepseek-v4-flash'), 'deepseek/deepseek-v4.1-flash');
assert.equal(MODEL_PRICING[resolveModel('deepseek-v4-flash')].input, 0);
});

test('the gateway-400 nvidia DeepSeek SKUs are in no free set or routing order', async () => {
const { FREE_PREFERENCE_ORDER, FREE_MODEL_CONTEXT_WINDOWS, LEGACY_FREE_MODEL_IDS, isFreeModelId } =
await import('../dist/free-models.js');
for (const id of ['nvidia/deepseek-v4-flash', 'nvidia/deepseek-v4-pro']) {
assert.ok(!FREE_PREFERENCE_ORDER.includes(id), `${id} must not be routed`);
assert.ok(!(id in FREE_MODEL_CONTEXT_WINDOWS), `${id} is not a live free id`);
assert.ok(!LEGACY_FREE_MODEL_IDS.includes(id), `${id} must not be treated as free`);
assert.equal(isFreeModelId(id), false);
}
});
Loading