Skip to content

Commit 90708ee

Browse files
committed
fix(desktop): expose exact usage totals
Compact summaries stay readable while token breakdowns remain available on hover and keyboard focus. Signed-off-by: Jiawei Zhao <Phoenix500526@163.com> Generated-by: OpenAI Codex
1 parent 0e89bf8 commit 90708ee

3 files changed

Lines changed: 58 additions & 3 deletions

File tree

apps/desktop/src/renderer/locales/settings-usage-copy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type UsageSettingsCopy = {
2323
saveFailed: string; toolbarAria: string; rangeAria: string; ranges: readonly [string, string, string, string];
2424
refreshingAria: string; refreshAria: string; summaryAria: string; totalRequests: string; totalCost: string; costHelp: string;
2525
totalTokens: string; tokenDetail(input: string, output: string): string; cacheTokens: string; cacheDetail(miss: string, read: string, creation: string): string;
26+
tokenTooltip: { total: string; input: string; output: string; cached: string; new: string; hit: string; created: string };
2627
viewAria: string; tabs: readonly [string, string, string, string, string]; filtersAria: string; filterPlaceholder: string; filterAria: string;
2728
statusAria: string; statuses: readonly [string, string, string, string]; details: string; detailsAria: string; recordCount(count: number): string; clearFilters: string;
2829
summaryOnly: string; showDetails: string; filteredEmpty: string; filteredEmptyHelp: string; requestEmpty: string;
@@ -42,6 +43,7 @@ const SETTINGS_USAGE_COPY = {
4243
refreshingAria: '正在刷新使用统计', refreshAria: '刷新使用统计', summaryAria: '使用统计汇总指标', totalRequests: '模型调用', totalCost: '总费用', costHelp: '以模型供应商最终结算为准',
4344
totalTokens: '总 Token', tokenDetail: (input, output) => `输入 ${input} / 输出 ${output}`, cacheTokens: '缓存 Token',
4445
cacheDetail: (miss, read, creation) => `新 ${miss} / 命中 ${read} / 创建 ${creation}`, viewAria: '使用统计视图', tabs: ['活动记录', '供应商统计', '模型统计', '工具统计', '定价配置'],
46+
tokenTooltip: { total: '总计', input: '输入', output: '输出', cached: '缓存', new: '新', hit: '命中', created: '创建' },
4547
filtersAria: '活动记录筛选', filterPlaceholder: '按模型或工具筛选…', filterAria: '按模型或工具筛选活动记录', statusAria: '活动状态筛选',
4648
statuses: ['全部状态', '成功', '错误', '已中止'], details: '详情记录', detailsAria: '显示使用统计详情记录', recordCount: (count) => `共 ${count} 条记录`, clearFilters: '清除筛选',
4749
summaryOnly: '当前仅显示汇总指标。打开详情记录后,可以查看逐条模型调用和工具调用,按模型、工具或状态筛选,并用于排查费用与失败调用。',
@@ -64,6 +66,7 @@ const SETTINGS_USAGE_COPY = {
6466
refreshingAria: 'Refreshing usage', refreshAria: 'Refresh usage', summaryAria: 'Usage summary metrics', totalRequests: 'Model calls', totalCost: 'Total cost', costHelp: 'Final billing is determined by the model provider',
6567
totalTokens: 'Total tokens', tokenDetail: (input, output) => `Input ${input} / output ${output}`, cacheTokens: 'Cache tokens',
6668
cacheDetail: (miss, read, creation) => `New ${miss} / hit ${read} / created ${creation}`, viewAria: 'Usage view', tabs: ['Activity log', 'Providers', 'Models', 'Tools', 'Pricing'],
69+
tokenTooltip: { total: 'Total', input: 'Input', output: 'Output', cached: 'Cached', new: 'New', hit: 'Hit', created: 'Created' },
6770
filtersAria: 'Activity filters', filterPlaceholder: 'Filter by model or tool…', filterAria: 'Filter activity by model or tool', statusAria: 'Filter by activity status',
6871
statuses: ['All statuses', 'Success', 'Error', 'Aborted'], details: 'Detailed records', detailsAria: 'Show detailed usage records', recordCount: (count) => `${count} ${count === 1 ? 'record' : 'records'}`, clearFilters: 'Clear filters',
6972
summaryOnly: 'Only summary metrics are shown. Enable detailed records to inspect individual model calls and tool calls, filter by model, tool, or status, and investigate costs or failures.',

apps/desktop/src/renderer/settings/settings-metric-card.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
* under the License.
1818
*/
1919

20+
import type { ReactNode } from 'react';
2021
import { StatTile } from '@maka/ui';
2122

2223
/** Thin alias over the shared StatTile (convergence R4) — usage/bot call
2324
* sites keep their name; the recipe lives in the primitive. */
24-
export function MetricCard(props: { title: string; value: string; detail?: string }) {
25+
export function MetricCard(props: { title: string; value: ReactNode; detail?: string }) {
2526
return (
2627
/* Detail audit: was emphasis="filled" — gray-plate tiles while the
2728
Permission/Health summaries use the outlined StatTile. One tile

apps/desktop/src/renderer/settings/usage-settings-page.tsx

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ import { useOptimisticSettingsDraft } from './use-optimistic-settings-draft';
6464

6565
type UsageActiveTab = AppSettings['usage']['activeTab'];
6666

67+
function TokenTooltipContent(props: {
68+
rows: ReadonlyArray<readonly [label: string, value: string]>;
69+
}) {
70+
return (
71+
<div>
72+
{props.rows.map(([label, value]) => (
73+
<div key={label}>{label}: {value}</div>
74+
))}
75+
</div>
76+
);
77+
}
78+
6779
export function UsageSettingsPage(props: {
6880
settings: AppSettings;
6981
stats: UsageStats | null;
@@ -73,6 +85,10 @@ export function UsageSettingsPage(props: {
7385
}) {
7486
const locale = useUiLocale();
7587
const copy = getUsageSettingsCopy(locale);
88+
const exactTokenFormatter = useMemo(
89+
() => new Intl.NumberFormat(uiLocaleToIntlLocale(locale)),
90+
[locale],
91+
);
7692
const persistedUsage = props.settings.usage;
7793
const [refreshing, setRefreshing] = useState(false);
7894
const usageRefreshGuard = useActionGuard<'refresh'>();
@@ -200,8 +216,43 @@ export function UsageSettingsPage(props: {
200216
<div className="settingsUsageSummary" role="group" aria-label={copy.summaryAria}>
201217
<MetricCard title={copy.totalRequests} value={stats ? String(stats.summary.totalRequests) : '—'} />
202218
<MetricCard title={copy.totalCost} value={totalCostDisplay} detail={copy.costHelp} />
203-
<MetricCard title={copy.totalTokens} value={stats ? formatCompactTokenCount(stats.summary.totalTokens) : '—'} detail={stats ? copy.tokenDetail(formatCompactTokenCount(stats.summary.inputTokens), formatCompactTokenCount(stats.summary.outputTokens)) : undefined} />
204-
<MetricCard title={copy.cacheTokens} value={stats ? formatCompactTokenCount(stats.summary.cacheTokens) : '—'} detail={stats ? copy.cacheDetail(formatCompactTokenCount(stats.summary.cacheMiss), formatCompactTokenCount(stats.summary.cacheRead), formatCompactTokenCount(stats.summary.cacheCreation)) : undefined} />
219+
<MetricCard
220+
title={copy.totalTokens}
221+
value={stats ? (
222+
<Tooltip
223+
content={(
224+
<TokenTooltipContent rows={[
225+
[copy.tokenTooltip.total, exactTokenFormatter.format(stats.summary.totalTokens)],
226+
[copy.tokenTooltip.input, exactTokenFormatter.format(stats.summary.inputTokens)],
227+
[copy.tokenTooltip.output, exactTokenFormatter.format(stats.summary.outputTokens)],
228+
]} />
229+
)}
230+
hasHoverIndication={false}
231+
>
232+
{formatCompactTokenCount(stats.summary.totalTokens)}
233+
</Tooltip>
234+
) : '—'}
235+
detail={stats ? copy.tokenDetail(formatCompactTokenCount(stats.summary.inputTokens), formatCompactTokenCount(stats.summary.outputTokens)) : undefined}
236+
/>
237+
<MetricCard
238+
title={copy.cacheTokens}
239+
value={stats ? (
240+
<Tooltip
241+
content={(
242+
<TokenTooltipContent rows={[
243+
[copy.tokenTooltip.cached, exactTokenFormatter.format(stats.summary.cacheTokens)],
244+
[copy.tokenTooltip.new, exactTokenFormatter.format(stats.summary.cacheMiss)],
245+
[copy.tokenTooltip.hit, exactTokenFormatter.format(stats.summary.cacheRead)],
246+
[copy.tokenTooltip.created, exactTokenFormatter.format(stats.summary.cacheCreation)],
247+
]} />
248+
)}
249+
hasHoverIndication={false}
250+
>
251+
{formatCompactTokenCount(stats.summary.cacheTokens)}
252+
</Tooltip>
253+
) : '—'}
254+
detail={stats ? copy.cacheDetail(formatCompactTokenCount(stats.summary.cacheMiss), formatCompactTokenCount(stats.summary.cacheRead), formatCompactTokenCount(stats.summary.cacheCreation)) : undefined}
255+
/>
205256
</div>
206257
</div>
207258

0 commit comments

Comments
 (0)