import { useState } from 'react' import { CliErrorPanel } from '../components/CliErrorPanel' import { EmptyNote } from '../components/EmptyState' import { seriesColorForModel } from '../components/ListRow' import { Panel } from '../components/Panel' import { SectionSkeleton } from '../components/Skeleton' import { SegTabs } from '../components/SegTabs' import { StaleBanner } from '../components/StaleBanner' import type { Section } from '../components/Sidebar' import { usePolled } from '../hooks/usePolled' import { formatCompact, formatUsd } from '../lib/format' import { codeburn } from '../lib/ipc' import type { AuditRow, DateRange, ModelReportRow, Period } from '../lib/types' import type { SettingsPane } from './Settings' type ModelsLens = 'model' | 'task' | 'audit' const LENSES = [ { value: 'model', label: 'By model' }, { value: 'task', label: 'By task' }, { value: 'audit', label: 'Audit' }, ] function fmtInt(n: number): string { return n.toLocaleString('en-US') } // Muted secondary tag naming a row's provider, so the same model name coming // from different providers reads as distinct rows. const providerTagStyle = { color: 'var(--mut)', fontSize: 'var(--fs-label)', fontWeight: 450 } as const export function Models({ period, provider, range = null, refreshToken = 0, onNavigate, }: { period: Period provider: string range?: DateRange | null refreshToken?: number onNavigate?: (section: Section, pane?: SettingsPane) => void }) { const [lens, setLens] = useState('model') const onAddAlias = () => onNavigate?.('settings', 'aliases') return ( <>
setLens(value as ModelsLens)} /> {lens !== 'audit' && ( )}
{lens === 'audit' ? ( ) : ( )} ) } function ModelsUsage({ period, provider, range, byTask, refreshToken, onAddAlias, }: { period: Period provider: string range: DateRange | null byTask: boolean refreshToken: number onAddAlias: () => void }) { const report = usePolled( () => range ? codeburn.getModels(period, provider, byTask, range) : codeburn.getModels(period, provider, byTask), [period, provider, byTask, range?.from, range?.to, refreshToken], ) if (!report.data) { if (report.error) return return } return ( <> {report.error && } {report.data.length ? ( ) : ( No model usage in this range yet. )} ) } // A row's cost is "estimated" when it has no live pricing entry, or when the // attributed cost diverges from a straight rate x displayed-token recompute // (fast-mode multipliers or the 1-hour cache rate that calculateCost applies). function auditEstimated(row: AuditRow): boolean { if (!row.rates) return true return Math.abs(row.cost.recomputedTotalUSD - row.attributedCostUSD) > 0.005 } function AuditLens({ period, provider, range, refreshToken, }: { period: Period provider: string range: DateRange | null refreshToken: number }) { const report = usePolled( () => range ? codeburn.getAudit(period, provider, range) : codeburn.getAudit(period, provider), [period, provider, range?.from, range?.to, refreshToken], ) if (!report.data) { if (report.error) return return } return ( <> {report.error && } {report.data.length ? ( ) : ( No model usage to audit in this range yet. )} ) } function AuditTable({ rows }: { rows: AuditRow[] }) { return ( {rows.map((row, i) => ( ))}
Model Calls Input Output Reasoning Norm out Cache wr Cache rd Cost
) } function AuditTableRow({ row }: { row: AuditRow }) { const estimated = auditEstimated(row) return ( {row.modelDisplayName} {fmtInt(row.calls)} {formatCompact(row.raw.inputTokens)} {formatCompact(row.raw.outputTokens)} {formatCompact(row.raw.reasoningTokens)} {formatCompact(row.displayed.outputTokens)} {formatCompact(row.displayed.cacheWriteTokens)} {formatCompact(row.displayed.cacheReadTokens)} {formatUsd(row.attributedCostUSD)} {estimated ? est : null} ) } function ModelsTable({ rows, byTask, onAddAlias }: { rows: ModelReportRow[]; byTask: boolean; onAddAlias: () => void }) { if (byTask) return return ( {rows.map((row, i) => ( ))}
Model Calls Input Output Cache read Cost Saved
) } function ModelsByTaskTable({ rows, onAddAlias }: { rows: ModelReportRow[]; onAddAlias: () => void }) { const groups = groupTaskRows(rows) return ( {groups.map(group => ( {group.rows.map((row, i) => ( ))} ))}
Task Calls Input Output Cache read Cost Saved
) } function ModelTableRow({ row, onAddAlias }: { row: ModelReportRow; onAddAlias: () => void }) { const unpriced = row.costUSD === 0 && row.savingsUSD === 0 const cellClass = unpriced ? 'dim' : undefined const tokenValue = (value: number) => (unpriced ? '—' : formatCompact(value)) const dotStyle = { display: 'inline-block', background: seriesColorForModel(row.modelDisplayName || row.model), marginRight: 8, } return ( {row.modelDisplayName} {unpriced ? ( <> {' '} ) : null} {row.providerDisplayName} {fmtInt(row.calls)} {tokenValue(row.inputTokens)} {tokenValue(row.outputTokens)} {tokenValue(row.cacheReadTokens)} {unpriced ? '—' : formatUsd(row.costUSD)} 0 ? 'pos' : undefined}>{unpriced ? '—' : formatUsd(row.savingsUSD)} ) } function ModelGroupRow({ rows, onAddAlias }: { rows: ModelReportRow[]; onAddAlias: () => void }) { const model = rows[0] const calls = rows.reduce((sum, row) => sum + row.calls, 0) const costUSD = rows.reduce((sum, row) => sum + row.costUSD, 0) const savingsUSD = rows.reduce((sum, row) => sum + row.savingsUSD, 0) const unpriced = costUSD === 0 && savingsUSD === 0 return ( {model.modelDisplayName} {model.providerDisplayName} {unpriced ? : null} {fmtInt(calls)} {unpriced ? '—' : formatUsd(costUSD)} 0 ? 'pos' : undefined}>{unpriced ? '—' : formatUsd(savingsUSD)} ) } function ModelTaskRow({ row }: { row: ModelReportRow }) { const unpriced = row.costUSD === 0 && row.savingsUSD === 0 const cellClass = unpriced ? 'dim' : undefined const tokenValue = (value: number) => (unpriced ? '—' : formatCompact(value)) return ( {row.category ?? 'general'} {fmtInt(row.calls)} {tokenValue(row.inputTokens)} {tokenValue(row.outputTokens)} {tokenValue(row.cacheReadTokens)} {unpriced ? '—' : formatUsd(row.costUSD)} 0 ? 'pos' : undefined}>{unpriced ? '—' : formatUsd(row.savingsUSD)} ) } function groupTaskRows(rows: ModelReportRow[]) { const groups = new Map() for (const row of rows) { const key = `${row.provider}\u0000${row.model}` const group = groups.get(key) if (group) group.rows.push(row) else groups.set(key, { provider: row.provider, model: row.model, rows: [row] }) } return [...groups.values()] }