mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-01 12:25:31 +00:00
* Resolve Fireworks-hosted models to friendly display names Fireworks fleet models arrive as `accounts/fireworks/models/<slug>` and were leaking the raw path/slug in reports and the dashboard (e.g. `fireworks/mode…`, `glm-5p2`). getShortModelName's path fallback now takes the last path segment and re-resolves it, so a known slug earns a friendly name (GLM-5.2, Qwen 3.7 Plus, Kimi K2.7 Code, DeepSeek v4 Pro/Flash) while an unmapped slug still falls through to the raw segment. Display-only: getModelCosts prices off the full id, so dollar amounts are unchanged. The By Model dashboard panel now renders through getShortModelName too, so already-cached raw keys normalize without a cache rebuild. * Merge By Model rows that resolve to the same display name Once path-style ids normalize, a mixed-vintage cache can hold more than one raw key for the same model (the full accounts/fireworks/models/<slug> path from an older build and the bare slug/friendly name from a newer one). The dashboard By Model panel aggregated by raw id, so those rendered as duplicate rows once both mapped to the same display name. Extract the aggregation into a pure, tested aggregateModelTotals() keyed by the resolved display name so the rows merge into one. This also aligns the panel with modelEfficiency, which is already keyed by getShortModelName.
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { aggregateModelTotals } from '../src/model-breakdown.js'
|
|
import type { ProjectSummary, TokenUsage } from '../src/types.js'
|
|
|
|
function tokens(input: number, cacheRead: number, cacheWrite: number): TokenUsage {
|
|
return {
|
|
inputTokens: input,
|
|
outputTokens: 0,
|
|
cacheCreationInputTokens: cacheWrite,
|
|
cacheReadInputTokens: cacheRead,
|
|
cachedInputTokens: 0,
|
|
reasoningTokens: 0,
|
|
webSearchRequests: 0,
|
|
}
|
|
}
|
|
|
|
// Minimal ProjectSummary carrying only what aggregateModelTotals reads.
|
|
function project(modelBreakdown: Record<string, { calls: number; costUSD: number; tokens: TokenUsage }>): ProjectSummary {
|
|
return { sessions: [{ modelBreakdown }] } as unknown as ProjectSummary
|
|
}
|
|
|
|
describe('aggregateModelTotals', () => {
|
|
it('merges raw and normalized keys that resolve to the same model into one row', () => {
|
|
// A mixed-vintage cache: the full Fireworks path from an older build and the
|
|
// bare slug from a newer one — both resolve to `GLM-5.2`.
|
|
const totals = aggregateModelTotals([
|
|
project({ 'accounts/fireworks/models/glm-5p2': { calls: 2, costUSD: 3.0, tokens: tokens(100, 1000, 0) } }),
|
|
project({ 'glm-5p2': { calls: 5, costUSD: 2.18, tokens: tokens(50, 500, 0) } }),
|
|
])
|
|
|
|
expect(Object.keys(totals)).toEqual(['GLM-5.2'])
|
|
expect(totals['GLM-5.2']).toMatchObject({ calls: 7, costUSD: 5.18, freshInput: 150, cacheRead: 1500 })
|
|
})
|
|
|
|
it('keeps distinct models on separate rows', () => {
|
|
const totals = aggregateModelTotals([
|
|
project({
|
|
'accounts/fireworks/models/qwen3p7-plus': { calls: 1, costUSD: 0.5, tokens: tokens(10, 0, 0) },
|
|
'accounts/fireworks/models/kimi-k2p7-code': { calls: 1, costUSD: 0.6, tokens: tokens(20, 0, 0) },
|
|
}),
|
|
])
|
|
expect(new Set(Object.keys(totals))).toEqual(new Set(['Qwen 3.7 Plus', 'Kimi K2.7 Code']))
|
|
})
|
|
})
|