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
20 changes: 12 additions & 8 deletions src/daily-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { homedir } from 'os'
import { join } from 'path'
import type { DateRange, ProjectSummary } from './types.js'

// Bumped to 8: local-model savings accounting is now part of the daily rollup
// (savingsUSD per day / per model / per category / per provider). Stale entries
// computed by older binaries lack those fields, so MIN_SUPPORTED_VERSION is
// also raised to 8 to force a full re-hydration. The `savingsConfigHash` field
// is invalidated separately when the user changes their `localModelSavings`
// mapping so historical "saved" totals stay in sync with the active baseline.
export const DAILY_CACHE_VERSION = 8
const MIN_SUPPORTED_VERSION = 8
// Bumped to 9: providers added since the v8 rollup (Grok, Hermes, ZCode) parse
// usage that older binaries skipped, so days cached at v8 omit them and report
// $0 for those providers across history. Raising MIN_SUPPORTED_VERSION to 9 too
// forces a one-time full re-hydration so newly supported providers backfill
// without a manual cache clear.
//
// v8 added local-model savings to the daily rollup (savingsUSD per day / model /
// category / provider). The `savingsConfigHash` field is invalidated separately
// when the user changes their `localModelSavings` mapping so historical "saved"
// totals stay in sync with the active baseline.
export const DAILY_CACHE_VERSION = 9
const MIN_SUPPORTED_VERSION = 9
const DAILY_CACHE_FILENAME = 'daily-cache.json'

export type DailyEntry = {
Expand Down
8 changes: 8 additions & 0 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,14 @@ const SHORT_NAMES: Record<string, string> = {
'o3': 'o3',
'MiniMax-M2.7-highspeed': 'MiniMax M2.7 Highspeed',
'MiniMax-M2.7': 'MiniMax M2.7',
// Grok (xAI) and GLM ids that otherwise surface raw or as a pricing key in
// reports. grok-build and GLM-5.2 price via sibling aliases, so
// getShortModelName resolves to the pricing key before this lookup; map each
// back to the real model name. grok-composer has no alias, it just lacked an
// entry.
'glm-5p1': 'GLM-5.2', // ZCode/Hermes run GLM-5.2 (priced as the GLM-5.1 sibling)
'grok-build-0.1': 'Grok Build', // Grok Build prices through the 0.1 sibling
'grok-composer-2.5-fast': 'Grok Composer 2.5 Fast',
}

// Sorted longest-first so more-specific prefixes match before shorter ones.
Expand Down
13 changes: 13 additions & 0 deletions tests/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ describe('getShortModelName', () => {
expect(getShortModelName('claude-haiku-5')).toBe('Haiku 5')
expect(getShortModelName('claude-opus-9-9-20300101')).toBe('Opus 9.9')
})

it('shows the real model name for pricing-sibling aliases, not the internal key', () => {
// GLM-5.2 (and its lowercase Hermes spelling) price via the glm-5p1 sibling;
// reports must show GLM-5.2, not the pricing key.
expect(getShortModelName('GLM-5.2')).toBe('GLM-5.2')
expect(getShortModelName('glm-5.2')).toBe('GLM-5.2')
expect(getShortModelName('glm-5p1')).toBe('GLM-5.2')
// Grok Build prices via the grok-build-0.1 sibling.
expect(getShortModelName('grok-build')).toBe('Grok Build')
expect(getShortModelName('grok-build-0.1')).toBe('Grok Build')
// grok-composer has no alias, just a missing display entry.
expect(getShortModelName('grok-composer-2.5-fast')).toBe('Grok Composer 2.5 Fast')
})
})

describe('claude-fable-5 pricing + name', () => {
Expand Down