@@ -257,19 +257,47 @@ export function recordUsage(
257257export function getStatsSummary ( ) : {
258258 stats : Stats ;
259259 opusCost : number ;
260+ /** All chat / token-billed model spend (excludes image / video / music). */
261+ chatOnlyCost : number ;
262+ /** Per-image / per-second / per-track media generation spend. */
263+ mediaCost : number ;
260264 saved : number ;
261265 savedPct : number ;
262266 avgCostPerRequest : number ;
263267 period : string ;
264268} {
265269 const stats = loadStats ( ) ;
266270
267- // Calculate what it would cost with the Opus-tier baseline
268- const opusCost =
271+ // Hypothetical "if you'd used Opus for everything" baseline. Opus is a
272+ // chat model — it can't replace ImageGen / VideoGen / Music (per_image,
273+ // per_second, per_track billing), so for those rows the Opus-equivalent
274+ // cost IS just the actual cost (no alternative). For chat rows, the
275+ // baseline is the same tokens repriced at Opus rates.
276+ //
277+ // Walk byModel: rows with zero tokens are media (recordUsage stores
278+ // image/video calls with inputTokens=0 outputTokens=0). Those count
279+ // towards both sides equally; chat rows count at actual price on the
280+ // "actual" side and at Opus rates on the "baseline" side. Keeping them
281+ // on both sides means the displayed totals match the user's real
282+ // spend rather than an unfamiliar chat-only subset.
283+ let chatOnlyCost = 0 ;
284+ let mediaCost = 0 ;
285+ for ( const m of Object . values ( stats . byModel ) ) {
286+ if ( ( m . inputTokens + m . outputTokens ) > 0 ) chatOnlyCost += m . costUsd ;
287+ else mediaCost += m . costUsd ;
288+ }
289+ const opusChatCost =
269290 ( stats . totalInputTokens / 1_000_000 ) * OPUS_PRICING . input +
270291 ( stats . totalOutputTokens / 1_000_000 ) * OPUS_PRICING . output ;
271-
272- const saved = opusCost - stats . totalCostUsd ;
292+ // Display-side baseline: include media on both sides so "you spent X
293+ // instead of Y" shows real, comparable totals.
294+ const opusCost = opusChatCost + mediaCost ;
295+
296+ // Saved is the chat-side delta only — media nets to zero. Clamp to 0
297+ // so a session where the user paid more than Opus-equivalent for chat
298+ // (e.g. Sonnet 4.6 with extended thinking enabled) doesn't show a
299+ // negative "savings" number; we just say zero saved.
300+ const saved = Math . max ( 0 , opusChatCost - chatOnlyCost ) ;
273301 const savedPct = opusCost > 0 ? ( saved / opusCost ) * 100 : 0 ;
274302 const avgCostPerRequest =
275303 stats . totalRequests > 0 ? stats . totalCostUsd / stats . totalRequests : 0 ;
@@ -285,5 +313,5 @@ export function getStatsSummary(): {
285313 else period = `${ days } days` ;
286314 }
287315
288- return { stats, opusCost, saved, savedPct, avgCostPerRequest, period } ;
316+ return { stats, opusCost, chatOnlyCost , mediaCost , saved, savedPct, avgCostPerRequest, period } ;
289317}
0 commit comments