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
13 changes: 12 additions & 1 deletion lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@ export function formatTokens(n: number): string {

/**
* Format cost in dollars.
* null -> "$0.00*" (unknown model), 0.0073 -> "$0.0073", 1.5 -> "$1.50"
* null -> "$0.00*" (unknown model), 1.5 -> "$1.50", 100 -> "$100.00".
*
* Auto-promotes to 4 decimals when a positive fractional amount would otherwise
* round to "$0.00" at the default 2-decimal precision. Keeps displayed cost
* consistent across surfaces: a $0.0029 session reads as $0.0029 in both the
* overlay and the side panel, never $0.00 on one and $0.0029 on the other.
* Only activates when the caller accepts the default precision; explicit
* decimals arguments (e.g. decimals: 6) are respected as-is.
*
* @param decimals - number of decimal places (default 2 for dashboard, use 4 for per-request overlay)
*/
export function formatCost(cost: number | null, decimals: number = 2): string {
if (cost === null) return '$0.00*';
if (decimals === 2 && cost > 0 && cost < 0.01) {
return `$${cost.toFixed(4)}`;
}
return `$${cost.toFixed(decimals)}`;
}

Expand Down
13 changes: 13 additions & 0 deletions tests/audit/format-audit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ describe('formatCost', () => {
expect(formatCost(0.0073, 4)).toBe('$0.0073');
});

test('fractional cost under $0.01 auto-promotes to 4 decimals at default', () => {
expect(formatCost(0.0029)).toBe('$0.0029');
expect(formatCost(0.009)).toBe('$0.0090');
});

test('fractional cost at $0.01 boundary stays 2 decimals', () => {
expect(formatCost(0.01)).toBe('$0.01');
});

test('explicit decimals override wins over auto-promotion', () => {
expect(formatCost(0.0029, 4)).toBe('$0.0029');
});

test('large cost', () => {
expect(formatCost(100.5)).toBe('$100.50');
});
Expand Down
2 changes: 1 addition & 1 deletion ui/overlay-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export const OVERLAY_CSS = `
.lco-health-label--critical { color: #ef4444; }

.lco-coaching {
font-size: 9px;
font-size: 10px;
line-height: 1.4;
color: var(--lco-muted);
margin: 2px 0 3px;
Expand Down
3 changes: 2 additions & 1 deletion ui/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,9 @@ export function createOverlay(): OverlayHandle {
if (elSession && sessionVisible) {
const { requestCount, totalInputTokens, totalOutputTokens, totalCost } = state.session;
const total = totalInputTokens + totalOutputTokens;
const turnLabel = requestCount === 1 ? 'turn' : 'turns';
elSession.textContent =
`${requestCount} req · ~${fmt(total)} tok · ${fmtCost(totalCost)}`;
`${requestCount} ${turnLabel} · ~${fmt(total)} tok · ${fmtCost(totalCost)}`;
}

if (elHealth) {
Expand Down
Loading