From c00daa96d3b9099ac69efa801ccb546b096af830 Mon Sep 17 00:00:00 2001 From: seakee Date: Mon, 13 Jul 2026 12:27:10 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(monitoring):=20clarify=20acc?= =?UTF-8?q?ount=20cache=20metrics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render the existing account cache slot as C, CR, and CW based on the available token buckets. Apply the same compact presentation to desktop token structure, mobile cards, and model details without changing responsive layout. Preserve legacy cached-token display and cover fine-grained, mixed, and empty cache states. --- .../monitoring/MonitoringCenterPage.test.tsx | 42 ++++++++++-- .../components/AccountOverviewCard.tsx | 60 ++++++++-------- .../components/accountOverviewPresentation.ts | 68 +++++++++++++++++-- 3 files changed, 133 insertions(+), 37 deletions(-) diff --git a/apps/web/src/features/monitoring/MonitoringCenterPage.test.tsx b/apps/web/src/features/monitoring/MonitoringCenterPage.test.tsx index 26e6d512..b27fde64 100644 --- a/apps/web/src/features/monitoring/MonitoringCenterPage.test.tsx +++ b/apps/web/src/features/monitoring/MonitoringCenterPage.test.tsx @@ -13,6 +13,7 @@ import { type MonitoringAccountAuthState, } from '@/features/monitoring/accountOverviewState'; import { buildRealtimeSourceDisplay } from '@/features/monitoring/realtimeSourceDisplay'; +import { buildCacheTokenPresentation } from '@/features/monitoring/components/accountOverviewPresentation'; const t = ((key: string, options?: Record) => { const copy: Record = { @@ -269,6 +270,33 @@ describe('MonitoringCenterPage summary cards', () => { }); describe('MonitoringCenterPage account card', () => { + it('formats legacy and fine-grained account cache metrics without changing the token slot', () => { + expect( + buildCacheTokenPresentation( + { cachedTokens: 12_500, cacheReadTokens: 0, cacheCreationTokens: 0 }, + t + ) + ).toMatchObject({ label: 'Cached Tokens', value: '12.5K' }); + expect( + buildCacheTokenPresentation( + { cachedTokens: 0, cacheReadTokens: 1_200, cacheCreationTokens: 300 }, + t + ) + ).toMatchObject({ label: 'CR / CW', value: '1.2K / 300' }); + expect( + buildCacheTokenPresentation( + { cachedTokens: 40, cacheReadTokens: 1_200, cacheCreationTokens: 300 }, + t + ) + ).toMatchObject({ label: 'C / CR / CW', value: '40 / 1.2K / 300' }); + expect( + buildCacheTokenPresentation( + { cachedTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 }, + t + ) + ).toMatchObject({ label: 'Cached Tokens', value: '0' }); + }); + it('prefers readable channel names in realtime source cells', () => { const display = buildRealtimeSourceDisplay( { @@ -688,7 +716,13 @@ describe('MonitoringCenterPage account card', () => { { key: 'total-tokens', label: 'Total Tokens', value: '35.1M' }, { key: 'input-tokens', label: 'Input Tokens', value: '35.0M' }, { key: 'output-tokens', label: 'Output Tokens', value: '68.5K' }, - { key: 'cached-tokens', label: 'Cached Tokens', value: '33.9M' }, + { + key: 'cached-tokens', + label: 'C / CR / CW', + fullLabel: + 'Cached Tokens: 33.9M · Cache Read Tokens: 1.2M · Cache Creation Tokens: 340.0K', + value: '33.9M / 1.2M / 340.0K', + }, ]} onRefreshQuota={() => {}} variant="table" @@ -698,9 +732,8 @@ describe('MonitoringCenterPage account card', () => { expect(html).toContain('Token Structure'); expect(html).toContain('Input Tokens'); expect(html).toContain('Output Tokens'); - expect(html).toContain('Cached Tokens'); - expect(html).not.toContain('Cache Read Tokens'); - expect(html).not.toContain('Cache Creation Tokens'); + expect(html).toContain('C / CR / CW'); + expect(html).toContain('33.9M / 1.2M / 340.0K'); expect(html).toContain('Model Usage Top 2'); expect(html).toContain('View All'); expect(html).not.toContain('Read'); @@ -709,6 +742,7 @@ describe('MonitoringCenterPage account card', () => { expect(html).toContain('Latest request'); expect(html).toContain('gpt-5.5'); expect(html).toContain('codex-auto-review'); + expect(html).toContain('C / CR / CW 32.5M / 1.1M / 300.0K'); expect(html).not.toContain('long-tail-model'); }); diff --git a/apps/web/src/features/monitoring/components/AccountOverviewCard.tsx b/apps/web/src/features/monitoring/components/AccountOverviewCard.tsx index 2a5ca0c4..2bafaf27 100644 --- a/apps/web/src/features/monitoring/components/AccountOverviewCard.tsx +++ b/apps/web/src/features/monitoring/components/AccountOverviewCard.tsx @@ -29,6 +29,7 @@ import { MonitoringHealthStatusBar } from './MonitoringHealthStatusBar'; import { buildAccountSecondaryText, buildAccountSummaryMetrics, + buildCacheTokenPresentation, formatPercent, getAccountStatusDotClassName, getAccountStatusLabel, @@ -595,6 +596,7 @@ function AccountModelUsageList({ {visibleModels.map((model) => { const modelKey = `${row.id}-${model.model}`; const isModelExpanded = Boolean(expandedModels[modelKey]); + const cacheMetric = buildCacheTokenPresentation(model, t); return (
- - {shortLabel( - t, - 'monitoring.cached_tokens_short', - 'monitoring.cached_tokens' - )} - - {formatCompactNumber(model.cachedTokens)} + {cacheMetric.label} + {cacheMetric.value}
@@ -743,25 +739,35 @@ export function AccountModelUsageTable({ - {visibleModels.map((model) => ( - - - - {model.model} - - - {formatCompactNumber(model.totalCalls)} - - {formatPercent(model.successRate)} - - {formatCompactNumber(model.inputTokens)} - {formatCompactNumber(model.outputTokens)} - {formatCompactNumber(model.cachedTokens)} - {formatCompactNumber(model.totalTokens)} - {hasPrices ? formatUsd(model.totalCost) : '--'} - {new Date(model.lastSeenAt).toLocaleString(locale)} - - ))} + {visibleModels.map((model) => { + const cacheMetric = buildCacheTokenPresentation(model, t); + return ( + + + + {model.model} + + + {formatCompactNumber(model.totalCalls)} + + {formatPercent(model.successRate)} + + {formatCompactNumber(model.inputTokens)} + {formatCompactNumber(model.outputTokens)} + + + {cacheMetric.label === + shortLabel(t, 'monitoring.cached_tokens_short', 'monitoring.cached_tokens') + ? cacheMetric.value + : `${cacheMetric.label} ${cacheMetric.value}`} + + + {formatCompactNumber(model.totalTokens)} + {hasPrices ? formatUsd(model.totalCost) : '--'} + {new Date(model.lastSeenAt).toLocaleString(locale)} + + ); + })} ) : ( diff --git a/apps/web/src/features/monitoring/components/accountOverviewPresentation.ts b/apps/web/src/features/monitoring/components/accountOverviewPresentation.ts index 92b77cab..44b4f4f6 100644 --- a/apps/web/src/features/monitoring/components/accountOverviewPresentation.ts +++ b/apps/web/src/features/monitoring/components/accountOverviewPresentation.ts @@ -50,6 +50,12 @@ export type AccountSummaryMetric = { valueClassName?: string; }; +export type CacheTokenPresentation = { + label: string; + fullLabel: string; + value: string; +}; + export const formatPercent = (value: number) => `${(value * 100).toFixed(1)}%`; const joinShort = (values: string[], limit = 2) => { @@ -65,6 +71,61 @@ const shortLabel = (t: TFunction, shortKey: string, fallbackKey: string) => { return label === shortKey ? fallback : label; }; +export const buildCacheTokenPresentation = ( + tokens: Pick, + t: TFunction +): CacheTokenPresentation => { + const cachedTokens = Math.max(tokens.cachedTokens || 0, 0); + const cacheReadTokens = Math.max(tokens.cacheReadTokens || 0, 0); + const cacheCreationTokens = Math.max(tokens.cacheCreationTokens || 0, 0); + const hasFineGrainedCache = cacheReadTokens > 0 || cacheCreationTokens > 0; + + if (!hasFineGrainedCache) { + return { + label: shortLabel(t, 'monitoring.cached_tokens_short', 'monitoring.cached_tokens'), + fullLabel: t('monitoring.cached_tokens'), + value: formatCompactNumber(cachedTokens), + }; + } + + const parts = [ + cachedTokens > 0 + ? { code: 'C', fullLabel: t('monitoring.cached_tokens'), value: cachedTokens } + : null, + cacheReadTokens > 0 + ? { code: 'CR', fullLabel: t('monitoring.cache_read_tokens'), value: cacheReadTokens } + : null, + cacheCreationTokens > 0 + ? { + code: 'CW', + fullLabel: t('monitoring.cache_creation_tokens'), + value: cacheCreationTokens, + } + : null, + ].filter((part): part is { code: string; fullLabel: string; value: number } => part !== null); + + return { + label: parts.map((part) => part.code).join(' / '), + fullLabel: parts + .map((part) => `${part.fullLabel}: ${formatCompactNumber(part.value)}`) + .join(' · '), + value: parts.map((part) => formatCompactNumber(part.value)).join(' / '), + }; +}; + +const buildAccountCacheSummaryMetric = ( + row: MonitoringAccountRow, + t: TFunction +): AccountSummaryMetric => { + const cacheMetric = buildCacheTokenPresentation(row, t); + return { + key: 'cached-tokens', + label: cacheMetric.label, + fullLabel: cacheMetric.fullLabel, + value: cacheMetric.value, + }; +}; + export const getCodexPlanLabel = ( planType: string | null | undefined, t: TFunction @@ -144,12 +205,7 @@ export const buildAccountSummaryMetrics = ( fullLabel: t('monitoring.output_tokens'), value: formatCompactNumber(row.outputTokens), }, - { - key: 'cached-tokens', - label: shortLabel(t, 'monitoring.cached_tokens_short', 'monitoring.cached_tokens'), - fullLabel: t('monitoring.cached_tokens'), - value: formatCompactNumber(row.cachedTokens), - }, + buildAccountCacheSummaryMetric(row, t), { key: 'cache-creation-tokens', label: shortLabel(