Skip to content
Open
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
7 changes: 5 additions & 2 deletions apps/ccusage/src/commands/daily.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { UsageReportConfig } from '@ccusage/terminal/table';
import process from 'node:process';
import {
addEmptySeparatorRow,
calculateCacheHitRate,
createUsageReportTable,
formatTotalsRow,
formatUsageDataRow,
Expand Down Expand Up @@ -113,6 +114,7 @@ export const dailyCommand = define({
outputTokens: data.outputTokens,
cacheCreationTokens: data.cacheCreationTokens,
cacheReadTokens: data.cacheReadTokens,
cacheHitRate: calculateCacheHitRate(data),
totalTokens: getTotalTokens(data),
totalCost: data.totalCost,
modelsUsed: data.modelsUsed,
Expand Down Expand Up @@ -156,7 +158,7 @@ export const dailyCommand = define({
// Add project section header
if (!isFirstProject) {
// Add empty row for visual separation between projects
table.push(['', '', '', '', '', '', '', '']);
table.push(['', '', '', '', '', '', '', '', '']);
}

// Add project header row
Expand All @@ -169,6 +171,7 @@ export const dailyCommand = define({
'',
'',
'',
'',
]);

// Add data rows for this project
Expand Down Expand Up @@ -213,7 +216,7 @@ export const dailyCommand = define({
}

// Add empty row for visual separation before totals
addEmptySeparatorRow(table, 8);
addEmptySeparatorRow(table, 9);

// Add totals
const totalsRow = formatTotalsRow({
Expand Down
4 changes: 3 additions & 1 deletion apps/ccusage/src/commands/monthly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { UsageReportConfig } from '@ccusage/terminal/table';
import process from 'node:process';
import {
addEmptySeparatorRow,
calculateCacheHitRate,
createUsageReportTable,
formatTotalsRow,
formatUsageDataRow,
Expand Down Expand Up @@ -74,6 +75,7 @@ export const monthlyCommand = define({
outputTokens: data.outputTokens,
cacheCreationTokens: data.cacheCreationTokens,
cacheReadTokens: data.cacheReadTokens,
cacheHitRate: calculateCacheHitRate(data),
totalTokens: getTotalTokens(data),
totalCost: data.totalCost,
modelsUsed: data.modelsUsed,
Expand Down Expand Up @@ -130,7 +132,7 @@ export const monthlyCommand = define({
}

// Add empty row for visual separation before totals
addEmptySeparatorRow(table, 8);
addEmptySeparatorRow(table, 9);

// Add totals
const totalsRow = formatTotalsRow({
Expand Down
4 changes: 3 additions & 1 deletion apps/ccusage/src/commands/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { UsageReportConfig } from '@ccusage/terminal/table';
import process from 'node:process';
import {
addEmptySeparatorRow,
calculateCacheHitRate,
createUsageReportTable,
formatTotalsRow,
formatUsageDataRow,
Expand Down Expand Up @@ -101,6 +102,7 @@ export const sessionCommand = define({
outputTokens: data.outputTokens,
cacheCreationTokens: data.cacheCreationTokens,
cacheReadTokens: data.cacheReadTokens,
cacheHitRate: calculateCacheHitRate(data),
totalTokens: getTotalTokens(data),
totalCost: data.totalCost,
lastActivity: data.lastActivity,
Expand Down Expand Up @@ -166,7 +168,7 @@ export const sessionCommand = define({
}

// Add empty row for visual separation before totals
addEmptySeparatorRow(table, 9);
addEmptySeparatorRow(table, 10);

// Add totals
const totalsRow = formatTotalsRow(
Expand Down
4 changes: 3 additions & 1 deletion apps/ccusage/src/commands/weekly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { UsageReportConfig } from '@ccusage/terminal/table';
import process from 'node:process';
import {
addEmptySeparatorRow,
calculateCacheHitRate,
createUsageReportTable,
formatTotalsRow,
formatUsageDataRow,
Expand Down Expand Up @@ -84,6 +85,7 @@ export const weeklyCommand = define({
outputTokens: data.outputTokens,
cacheCreationTokens: data.cacheCreationTokens,
cacheReadTokens: data.cacheReadTokens,
cacheHitRate: calculateCacheHitRate(data),
totalTokens: getTotalTokens(data),
totalCost: data.totalCost,
modelsUsed: data.modelsUsed,
Expand Down Expand Up @@ -136,7 +138,7 @@ export const weeklyCommand = define({
}

// Add empty row for visual separation before totals
addEmptySeparatorRow(table, 8);
addEmptySeparatorRow(table, 9);

// Add totals
const totalsRow = formatTotalsRow({
Expand Down
52 changes: 50 additions & 2 deletions packages/terminal/src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ export function pushBreakdownRows(
pc.gray(formatNumber(breakdown.outputTokens)),
pc.gray(formatNumber(breakdown.cacheCreationTokens)),
pc.gray(formatNumber(breakdown.cacheReadTokens)),
pc.gray(formatCacheHitRate(breakdown)),
pc.gray(formatNumber(totalTokens)),
pc.gray(formatCurrency(breakdown.cost)),
);
Expand Down Expand Up @@ -514,6 +515,42 @@ export type UsageData = {
modelsUsed?: string[];
};

/**
* Calculates and formats cache hit rate with color coding
* @param data - Usage data containing token counts
* @returns Color-coded percentage string
*/
export function formatCacheHitRate(data: {
inputTokens: number;
cacheCreationTokens: number;
cacheReadTokens: number;
}): string {
const totalInput = data.inputTokens + data.cacheCreationTokens + data.cacheReadTokens;
const rate = totalInput > 0 ? data.cacheReadTokens / totalInput : 0;
const pct = `${(rate * 100).toFixed(1)}%`;
if (rate >= 0.7) {
return pc.green(pct);
}
if (rate >= 0.4) {
return pc.yellow(pct);
}
return pc.red(pct);
}

/**
* Calculates cache hit rate as a number
* @param data - Usage data containing token counts
* @returns Cache hit rate between 0 and 1
*/
export function calculateCacheHitRate(data: {
inputTokens: number;
cacheCreationTokens: number;
cacheReadTokens: number;
}): number {
const totalInput = data.inputTokens + data.cacheCreationTokens + data.cacheReadTokens;
return totalInput > 0 ? data.cacheReadTokens / totalInput : 0;
}

/**
* Creates a standard usage report table with consistent styling and layout
* @param config - Configuration options for the table
Expand All @@ -527,6 +564,7 @@ export function createUsageReportTable(config: UsageReportConfig): ResponsiveTab
'Output',
'Cache Create',
'Cache Read',
'Hit Rate',
'Total Tokens',
'Cost (USD)',
];
Expand All @@ -540,11 +578,19 @@ export function createUsageReportTable(config: UsageReportConfig): ResponsiveTab
'right',
'right',
'right',
'right',
];

const compactHeaders = [config.firstColumnName, 'Models', 'Input', 'Output', 'Cost (USD)'];
const compactHeaders = [
config.firstColumnName,
'Models',
'Input',
'Output',
'Hit Rate',
'Cost (USD)',
];

const compactAligns: TableCellAlign[] = ['left', 'left', 'right', 'right', 'right'];
const compactAligns: TableCellAlign[] = ['left', 'left', 'right', 'right', 'right', 'right'];

// Add Last Activity column for session reports
if (config.includeLastActivity ?? false) {
Expand Down Expand Up @@ -588,6 +634,7 @@ export function formatUsageDataRow(
formatNumber(data.outputTokens),
formatNumber(data.cacheCreationTokens),
formatNumber(data.cacheReadTokens),
formatCacheHitRate(data),
formatNumber(totalTokens),
formatCurrency(data.totalCost),
];
Expand Down Expand Up @@ -619,6 +666,7 @@ export function formatTotalsRow(
pc.yellow(formatNumber(totals.outputTokens)),
pc.yellow(formatNumber(totals.cacheCreationTokens)),
pc.yellow(formatNumber(totals.cacheReadTokens)),
pc.yellow(formatCacheHitRate(totals)),
pc.yellow(formatNumber(totalTokens)),
pc.yellow(formatCurrency(totals.totalCost)),
];
Expand Down