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
52 changes: 35 additions & 17 deletions src/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ export const MODEL_PRICING: Record<string, { input: number; output: number; perC
'nvidia/mistral-nemotron': { input: 0, output: 0 },
'nvidia/nemotron-nano-12b-v2-vl': { input: 0, output: 0 },
'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning': { input: 0, output: 0 },
// The rest of the live free pool (/api/v1/models, 2026-10-02).
'nvidia/step-3.7-flash': { input: 0, output: 0 },
// The rest of the live free pool (/api/v1/models, 2026-10-20 — identical on
// Base and Solana).
'nvidia/nemotron-3-ultra-550b': { input: 0, output: 0 },
'nvidia/nemotron-3.5-lightning': { input: 0, output: 0 },
'nvidia/llama-3.2-11b-vision': { input: 0, output: 0 },
'poolside/laguna-xs-2.1': { input: 0, output: 0 },
'cohere/north-mini-code': { input: 0, output: 0 },
'nvidia/step-3.7-flash': { input: 0, output: 0 },
'nvidia/muse-glimmer-30b': { input: 0, output: 0 }, // Meta Muse Glimmer, free on NVIDIA since 2026-10-02
// Retired free models (kept at 0 for legacy session-cost records; gateway no
// longer serves these — do NOT route to them).
'nvidia/qwen3-next-80b-a3b-instruct': { input: 0, output: 0 }, // NVIDIA EOL 410, 2026-07-27
Expand Down Expand Up @@ -69,6 +71,7 @@ export const MODEL_PRICING: Record<string, { input: number; output: number; perC
// per-1M table cannot express.
'openai/gpt-6-astra': { input: 10.0, output: 50.0 },
'openai/gpt-6-sol': { input: 2.0, output: 10.0 },
'openai/gpt-6.1-sol': { input: 2.0, output: 10.0 }, // refreshed Sol, live 2026-10-20 (same base rate as 6 Sol)
'openai/gpt-6-luna': { input: 0.1, output: 0.5 },
'openai/gpt-5.1': { input: 1.25, output: 10.0 },
'openai/gpt-4.1-nano': { input: 0.1, output: 0.4 },
Expand Down Expand Up @@ -112,6 +115,7 @@ export const MODEL_PRICING: Record<string, { input: number; output: number; perC
'google/gemini-3.5-flash': { input: 1.5, output: 9.0 }, // latest Flash w/ thinking, 1M ctx
'google/gemini-3.5-flash-lite': { input: 0.3, output: 2.5 },
'google/gemini-3.6-flash': { input: 0.75, output: 3.75 }, // added upstream 2026-08-03 (base #329); live promo rate 2026-10-02
'google/gemini-3.8-flash': { input: 0.75, output: 3.75 }, // live 2026-10-20
'google/gemini-2.5-pro': { input: 1.25, output: 10.0 },
'google/gemini-3.1-pro': { input: 2.0, output: 12.0 },
// xAI
Expand All @@ -127,16 +131,26 @@ export const MODEL_PRICING: Record<string, { input: number; output: number; perC
// 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 },
// V4 Flash Vision (experimental): the image-capable DeepSeek SKU, live 2026-10-20.
'deepseek/deepseek-v4-flash-vision-exp': { input: 0.3, output: 1.2 },
// Minimax
'minimax/minimax-m3': { input: 0.3, output: 1.2 },
'minimax/minimax-m2.7': { input: 0.3, output: 1.2 },
// Qwen — Plus/Flash tiers added upstream 2026-08-03 (base #329).
'qwen/qwen3.7-max': { input: 1.475, output: 4.425 },
'qwen/qwen3.7-plus': { input: 0.32, output: 1.28 },
'qwen/qwen3.7-flash': { input: 0.03, output: 0.13 },
// Qwen 3.8 — live 2026-10-20. Flash is the vision-capable one.
'qwen/qwen3.8-max': { input: 2.0, output: 6.0 },
'qwen/qwen3.8-flash': { input: 0.15, output: 0.47 },
// New providers added upstream 2026-08 — priced from the live catalog.
'tencent/hy3': { input: 0.132, output: 0.528 }, // retired upstream (hidden) — kept for session-cost records
'tencent/hy3': { input: 0.132, output: 0.528 }, // retired 2026-09-24 (gateway redirects to qwen3.7-flash) — kept for session-cost records
'tencent/hy4-preview': { input: 0.834, output: 2.501 }, // live 2026-10-20 — Tencent's current model
'xiaomi/mimo-v2.5-pro': { input: 0.435, output: 0.87 },
'xiaomi/mimo-v2.5': { input: 0.14, output: 0.28 }, // live 2026-10-20 — vision-capable, cheaper than Pro
// Mistral + Poolside paid tiers — live 2026-10-20.
'mistral/mistral-large-4': { input: 0.68, output: 2.09 },
'poolside/laguna-s-2.1': { input: 0.09, output: 0.18 }, // paid sibling of the free laguna-xs-2.1
// Moonshot — K3 is the gateway flagship (2026-07): 2.8T open MoE, 1M
// context, multimodal (image+text), returns reasoning_content. Pricier
// than the K2.x line it replaced ($3/$15 vs K2.7's $0.95/$4).
Expand Down Expand Up @@ -165,28 +179,32 @@ export const MODEL_PRICING: Record<string, { input: number; output: number; perC
'openai/gpt-5-nano': { input: 0.05, output: 0.4 }, // (probed)
'openai/o1-mini': { input: 1.1, output: 4.4 },
'google/gemini-3-pro-preview': { input: 2.0, output: 12.0 }, // GA'd as gemini-3.1-pro
'xai/grok-4-fast': { input: 0.2, output: 0.5 },
'xai/grok-4-fast-reasoning': { input: 0.2, output: 0.5 }, // (probed)
'xai/grok-4-1-fast': { input: 0.2, output: 0.5 },
'xai/grok-4-1-fast-reasoning': { input: 0.2, output: 0.5 }, // (probed)
// Retired at xAI. The gateway pins every retired Grok slug to grok-4.3 and
// bills grok-4.3's $1.25/$2.50 (blockrun models.ts + model-maps.ts
// "Retired -> 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
};

Expand Down
70 changes: 58 additions & 12 deletions src/ui/model-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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',
Expand Down Expand Up @@ -124,6 +127,10 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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',
Expand All @@ -144,14 +151,14 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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)
Expand All @@ -160,6 +167,11 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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.
Expand Down Expand Up @@ -198,10 +210,14 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
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.
Expand All @@ -220,6 +236,13 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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',
Expand Down Expand Up @@ -249,11 +272,18 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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',
Expand All @@ -264,6 +294,16 @@ export const MODEL_SHORTCUTS: Record<string, string> = {
'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',
};

/**
Expand Down Expand Up @@ -344,6 +384,9 @@ const PROVIDER_ORDER = [
'deepseek',
'tencent',
'xiaomi',
'mistral',
'poolside',
'cohere',
'nvidia',
];

Expand All @@ -359,6 +402,9 @@ const PROVIDER_LABELS: Record<string, string> = {
deepseek: 'DeepSeek',
tencent: 'Tencent / Hunyuan',
xiaomi: 'Xiaomi / MiMo',
mistral: 'Mistral',
poolside: 'Poolside / Laguna',
cohere: 'Cohere',
nvidia: 'Free / NVIDIA',
};

Expand Down
58 changes: 49 additions & 9 deletions test/local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});

Expand Down Expand Up @@ -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',
};
Expand Down Expand Up @@ -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`);
}
});
Loading