From a42b32329f6158c16ad0471eba429435f6afada9 Mon Sep 17 00:00:00 2001 From: Pedro Filho Date: Wed, 23 Sep 2026 11:41:53 -0300 Subject: [PATCH] price fable 5.1, opus 5.5, sonnet 5, gpt-6 and gpt-5.6 at current rates --- CHANGELOG.md | 1 + package.json | 2 +- src/claude.ts | 4 ---- src/config-install.ts | 7 +++---- src/pricing.test.ts | 34 +++++++++++++++++++++++++------ src/pricing.ts | 44 ++++++++++++++++++++++++++++++++++++++++ src/tui/fixtures.ts | 8 ++++---- src/tui/usage-windows.ts | 3 +-- 8 files changed, 82 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 246de57..e90ca0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [2026-09-23] price fable 5.1, opus 5.5, sonnet 5, gpt-6 and gpt-5.6 at current rates - [2026-08-21] codex keeps its shell and apply_patch tools on the gpt-5.6 models - [2026-08-18] meter clients that hang up early - [2026-08-18] reclaim bare provider tables diff --git a/package.json b/package.json index 7ec8ed3..ac9da2d 100644 --- a/package.json +++ b/package.json @@ -59,5 +59,5 @@ "post-commit": "bun x @rubriclab/package post-commit" }, "type": "module", - "version": "0.0.66" + "version": "0.0.67" } diff --git a/src/claude.ts b/src/claude.ts index 4fec5bd..83d855a 100644 --- a/src/claude.ts +++ b/src/claude.ts @@ -533,8 +533,6 @@ const UsageResponseSchema = z limits: z.array(LimitSchema).nullish(), seven_day: UsageWindowResponseSchema.nullish(), seven_day_oauth_apps: UsageWindowResponseSchema.nullish(), - seven_day_opus: UsageWindowResponseSchema.nullish(), - seven_day_sonnet: UsageWindowResponseSchema.nullish(), spend: SpendResponseSchema.nullish() }) .passthrough() @@ -661,8 +659,6 @@ async function fetchClaudeUsage(input: { const definitions = [ ['five_hour', '5 hour', 'session', body.five_hour], ['seven_day', '7 day', 'weekly_all', body.seven_day], - ['seven_day_opus', '7 day · Opus', null, body.seven_day_opus], - ['seven_day_sonnet', '7 day · Sonnet', null, body.seven_day_sonnet], ['seven_day_oauth_apps', '7 day · OAuth apps', null, body.seven_day_oauth_apps] ] as const for (const [id, label, limitEquivalent, window] of definitions) { diff --git a/src/config-install.ts b/src/config-install.ts index fb10149..c29c29d 100644 --- a/src/config-install.ts +++ b/src/config-install.ts @@ -249,10 +249,9 @@ function piModelsPath(): string { const piProviderKeys = ['tokenmaxx-anthropic', 'tokenmaxx-openai'] // The anthropic ids pair with an API-key account (subscription auth is not for -// third-party harnesses); gpt-5.6-sol is the one id the ChatGPT codex backend -// accepts for subscription accounts. -const piAnthropicModelIds = ['claude-opus-4-8', 'claude-sonnet-4-6'] -const piOpenaiModelIds = ['gpt-5.6-sol'] +// third-party harnesses). +const piAnthropicModelIds = ['claude-fable-5-1', 'claude-opus-5-5', 'claude-sonnet-5'] +const piOpenaiModelIds = ['gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna'] function piProviders(paths: ApplicationPaths): Record { const models = (ids: readonly string[]) => ids.map(id => ({ id, reasoning: true })) diff --git a/src/pricing.test.ts b/src/pricing.test.ts index 9957e4d..c18b57f 100644 --- a/src/pricing.test.ts +++ b/src/pricing.test.ts @@ -3,23 +3,45 @@ import { costUsd, priceFor } from './pricing.ts' describe('priceFor', () => { test('current model generations use current list prices', () => { - expect(priceFor('claude-fable-5')).toMatchObject({ inputPerMTok: 10, outputPerMTok: 50 }) - expect(priceFor('claude-opus-4-8')).toMatchObject({ inputPerMTok: 5, outputPerMTok: 25 }) - expect(priceFor('claude-sonnet-4-6')).toMatchObject({ inputPerMTok: 3, outputPerMTok: 15 }) + expect(priceFor('claude-fable-5-1')).toMatchObject({ inputPerMTok: 10, outputPerMTok: 50 }) + expect(priceFor('claude-opus-5-5')).toMatchObject({ inputPerMTok: 4, outputPerMTok: 20 }) + expect(priceFor('claude-opus-5')).toMatchObject({ inputPerMTok: 5, outputPerMTok: 25 }) + expect(priceFor('claude-sonnet-5')).toMatchObject({ inputPerMTok: 2, outputPerMTok: 10 }) expect(priceFor('claude-haiku-4-5-20251001')).toMatchObject({ inputPerMTok: 1, outputPerMTok: 5 }) - expect(priceFor('gpt-5.6-sol')).toMatchObject({ inputPerMTok: 1.25, outputPerMTok: 10 }) + expect(priceFor('gpt-6-astra')).toMatchObject({ inputPerMTok: 10, outputPerMTok: 50 }) + expect(priceFor('gpt-6-sol')).toMatchObject({ inputPerMTok: 2, outputPerMTok: 10 }) + expect(priceFor('gpt-6-luna')).toMatchObject({ inputPerMTok: 0.1, outputPerMTok: 0.5 }) + expect(priceFor('gpt-5.6-sol')).toMatchObject({ inputPerMTok: 4, outputPerMTok: 20 }) + expect(priceFor('gpt-5.6-terra')).toMatchObject({ inputPerMTok: 2, outputPerMTok: 12 }) + expect(priceFor('gpt-5.6-luna')).toMatchObject({ inputPerMTok: 0.2, outputPerMTok: 1.2 }) }) - test('legacy opus generations keep their historical 15/75 pricing', () => { + test('earlier generations keep their own prices', () => { + expect(priceFor('claude-fable-5')).toMatchObject({ cacheReadPerMTok: 1, inputPerMTok: 10 }) + expect(priceFor('claude-opus-4-8')).toMatchObject({ inputPerMTok: 5, outputPerMTok: 25 }) expect(priceFor('claude-opus-4-1')).toMatchObject({ inputPerMTok: 15, outputPerMTok: 75 }) expect(priceFor('claude-opus-4-20250514')).toMatchObject({ inputPerMTok: 15, outputPerMTok: 75 }) + expect(priceFor('claude-sonnet-4-6')).toMatchObject({ inputPerMTok: 3, outputPerMTok: 15 }) + expect(priceFor('gpt-5.5')).toMatchObject({ inputPerMTok: 5, outputPerMTok: 30 }) + expect(priceFor('gpt-5.3-codex')).toMatchObject({ inputPerMTok: 1.75, outputPerMTok: 14 }) + expect(priceFor('gpt-5')).toMatchObject({ inputPerMTok: 1.25, outputPerMTok: 10 }) }) test('cache reads are a tenth of input, anthropic cache writes 1.25x', () => { const opus = priceFor('claude-opus-4-8') expect(opus.cacheReadPerMTok).toBeCloseTo(opus.inputPerMTok * 0.1) expect(opus.cacheWritePerMTok).toBeCloseTo(opus.inputPerMTok * 1.25) - expect(priceFor('gpt-5.6-sol').cacheWritePerMTok).toBe(0) + }) + + test('fable 5.1 and opus 5.5 discount cache reads below a tenth of input', () => { + expect(priceFor('claude-fable-5-1').cacheReadPerMTok).toBe(0.25) + expect(priceFor('claude-opus-5-5').cacheReadPerMTok).toBe(0.2) + }) + + test('openai bills cache writes from gpt-5.6 on', () => { + expect(priceFor('gpt-6-sol').cacheWritePerMTok).toBe(2.5) + expect(priceFor('gpt-5.6-sol').cacheWritePerMTok).toBe(5) + expect(priceFor('gpt-5.5').cacheWritePerMTok).toBe(0) }) }) diff --git a/src/pricing.ts b/src/pricing.ts index b2858c8..82ed943 100644 --- a/src/pricing.ts +++ b/src/pricing.ts @@ -6,6 +6,10 @@ interface ModelPrice { } const PRICES: ReadonlyArray<{ match: string; price: ModelPrice }> = [ + { + match: 'claude-fable-5-1', + price: { cacheReadPerMTok: 0.25, cacheWritePerMTok: 12.5, inputPerMTok: 10, outputPerMTok: 50 } + }, { match: 'claude-fable', price: { cacheReadPerMTok: 1, cacheWritePerMTok: 12.5, inputPerMTok: 10, outputPerMTok: 50 } @@ -14,6 +18,10 @@ const PRICES: ReadonlyArray<{ match: string; price: ModelPrice }> = [ match: 'claude-mythos', price: { cacheReadPerMTok: 1, cacheWritePerMTok: 12.5, inputPerMTok: 10, outputPerMTok: 50 } }, + { + match: 'claude-opus-5-5', + price: { cacheReadPerMTok: 0.2, cacheWritePerMTok: 5, inputPerMTok: 4, outputPerMTok: 20 } + }, { match: 'claude-opus-4-1', price: { cacheReadPerMTok: 1.5, cacheWritePerMTok: 18.75, inputPerMTok: 15, outputPerMTok: 75 } @@ -26,6 +34,10 @@ const PRICES: ReadonlyArray<{ match: string; price: ModelPrice }> = [ match: 'claude-opus', price: { cacheReadPerMTok: 0.5, cacheWritePerMTok: 6.25, inputPerMTok: 5, outputPerMTok: 25 } }, + { + match: 'claude-sonnet-5', + price: { cacheReadPerMTok: 0.2, cacheWritePerMTok: 2.5, inputPerMTok: 2, outputPerMTok: 10 } + }, { match: 'claude-sonnet', price: { cacheReadPerMTok: 0.3, cacheWritePerMTok: 3.75, inputPerMTok: 3, outputPerMTok: 15 } @@ -34,6 +46,38 @@ const PRICES: ReadonlyArray<{ match: string; price: ModelPrice }> = [ match: 'claude-haiku', price: { cacheReadPerMTok: 0.1, cacheWritePerMTok: 1.25, inputPerMTok: 1, outputPerMTok: 5 } }, + { + match: 'gpt-6-astra', + price: { cacheReadPerMTok: 1, cacheWritePerMTok: 12.5, inputPerMTok: 10, outputPerMTok: 50 } + }, + { + match: 'gpt-6-sol', + price: { cacheReadPerMTok: 0.2, cacheWritePerMTok: 2.5, inputPerMTok: 2, outputPerMTok: 10 } + }, + { + match: 'gpt-6-luna', + price: { cacheReadPerMTok: 0.01, cacheWritePerMTok: 0.125, inputPerMTok: 0.1, outputPerMTok: 0.5 } + }, + { + match: 'gpt-5.6-sol', + price: { cacheReadPerMTok: 0.4, cacheWritePerMTok: 5, inputPerMTok: 4, outputPerMTok: 20 } + }, + { + match: 'gpt-5.6-terra', + price: { cacheReadPerMTok: 0.2, cacheWritePerMTok: 2.5, inputPerMTok: 2, outputPerMTok: 12 } + }, + { + match: 'gpt-5.6-luna', + price: { cacheReadPerMTok: 0.02, cacheWritePerMTok: 0.25, inputPerMTok: 0.2, outputPerMTok: 1.2 } + }, + { + match: 'gpt-5.5', + price: { cacheReadPerMTok: 0.5, cacheWritePerMTok: 0, inputPerMTok: 5, outputPerMTok: 30 } + }, + { + match: 'gpt-5.3-codex', + price: { cacheReadPerMTok: 0.175, cacheWritePerMTok: 0, inputPerMTok: 1.75, outputPerMTok: 14 } + }, { match: 'gpt-5', price: { cacheReadPerMTok: 0.125, cacheWritePerMTok: 0, inputPerMTok: 1.25, outputPerMTok: 10 } diff --git a/src/tui/fixtures.ts b/src/tui/fixtures.ts index c48e7de..8f0a164 100644 --- a/src/tui/fixtures.ts +++ b/src/tui/fixtures.ts @@ -31,10 +31,10 @@ function buildTokens(scale: number): TokenAnalytics { const buckets = raw.map(value => Math.round((value / rawSum) * target)) const totalTokens = buckets.reduce((sum, value) => sum + value, 0) const modelMix: { model: string; provider: ProviderId; share: number }[] = [ - { model: 'gpt-5.6-sol', provider: 'openai', share: 0.42 }, - { model: 'gpt-5.6-codex', provider: 'openai', share: 0.13 }, - { model: 'claude-opus-4-8', provider: 'anthropic', share: 0.3 }, - { model: 'claude-sonnet-4-6', provider: 'anthropic', share: 0.11 }, + { model: 'gpt-6-sol', provider: 'openai', share: 0.42 }, + { model: 'gpt-6-astra', provider: 'openai', share: 0.13 }, + { model: 'claude-opus-5-5', provider: 'anthropic', share: 0.3 }, + { model: 'claude-sonnet-5', provider: 'anthropic', share: 0.11 }, { model: 'claude-haiku-4-5', provider: 'anthropic', share: 0.04 } ] const models = modelMix diff --git a/src/tui/usage-windows.ts b/src/tui/usage-windows.ts index 3c1327b..1cb2c89 100644 --- a/src/tui/usage-windows.ts +++ b/src/tui/usage-windows.ts @@ -5,14 +5,13 @@ export function isFiveHourUsageWindow(window: UsageWindow): boolean { } function usageWindowPriority(window: UsageWindow): number { - const identity = `${window.id} ${window.label}` switch (true) { case isFiveHourUsageWindow(window): return 0 case /^(7 day(?: · all models)?)$/i.test(window.label): case /^(weekly_all|seven_day|weekly|codex:primary)$/.test(window.id): return 1 - case /scoped|fable|opus|sonnet|spark/i.test(identity): + case /^(weekly_scoped|codex_)/.test(window.id): return 2 default: return 3