From 51d722906cddf63ebf251a4c8a7b5fd750372d55 Mon Sep 17 00:00:00 2001
From: SavitarC <71271200+SavitarC@users.noreply.github.com>
Date: Thu, 18 Jun 2026 14:20:20 +0800
Subject: [PATCH] =?UTF-8?q?fix(usage):=20=E6=98=BE=E7=A4=BA=E7=BC=93?=
=?UTF-8?q?=E5=AD=98=20Token=20=E6=98=8E=E7=BB=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
frontend/src/api/admin/usage.ts | 2 +
.../admin/usage/UsageStatsCards.vue | 46 ++++++++++++-
.../usage/__tests__/UsageStatsCards.spec.ts | 67 +++++++++++++++++++
frontend/src/i18n/locales/en.ts | 4 ++
frontend/src/i18n/locales/zh.ts | 4 ++
5 files changed, 120 insertions(+), 3 deletions(-)
create mode 100644 frontend/src/components/admin/usage/__tests__/UsageStatsCards.spec.ts
diff --git a/frontend/src/api/admin/usage.ts b/frontend/src/api/admin/usage.ts
index d933ac6351..b37996d61a 100644
--- a/frontend/src/api/admin/usage.ts
+++ b/frontend/src/api/admin/usage.ts
@@ -14,6 +14,8 @@ export interface AdminUsageStatsResponse {
total_input_tokens: number
total_output_tokens: number
total_cache_tokens: number
+ total_cache_creation_tokens: number
+ total_cache_read_tokens: number
total_tokens: number
total_cost: number
total_actual_cost: number
diff --git a/frontend/src/components/admin/usage/UsageStatsCards.vue b/frontend/src/components/admin/usage/UsageStatsCards.vue
index 905ece61a5..415c7cc186 100644
--- a/frontend/src/components/admin/usage/UsageStatsCards.vue
+++ b/frontend/src/components/admin/usage/UsageStatsCards.vue
@@ -15,9 +15,46 @@
{{ t('usage.totalTokens') }}
{{ formatTokens(stats?.total_tokens || 0) }}
-
- {{ t('usage.in') }}: {{ formatTokens(stats?.total_input_tokens || 0) }} /
- {{ t('usage.out') }}: {{ formatTokens(stats?.total_output_tokens || 0) }}
+
+ {{ t('usage.in') }}: {{ formatTokens(stats?.total_input_tokens || 0) }}
+ /
+ {{ t('usage.out') }}: {{ formatTokens(stats?.total_output_tokens || 0) }}
+ /
+
+ {{ cacheLabel() }}: {{ formatTokens(stats?.total_cache_tokens || 0) }}
+
+
+
+ {{ cacheDetailLabel() }}
+
+
+ {{ t('usage.cacheCreationTokensLabel') }}
+
+ {{ formatTokens(stats?.total_cache_creation_tokens || 0) }}
+
+
+
+ {{ t('usage.cacheReadTokensLabel') }}
+
+ {{ formatTokens(stats?.total_cache_read_tokens || 0) }}
+
+
+
+
@@ -64,4 +101,7 @@ const formatTokens = (value: number) => {
if (value >= 1e3) return (value / 1e3).toFixed(2) + 'K'
return value.toLocaleString()
}
+
+const cacheLabel = () => t('usage.cacheTotal')
+const cacheDetailLabel = () => t('usage.cacheBreakdown')
diff --git a/frontend/src/components/admin/usage/__tests__/UsageStatsCards.spec.ts b/frontend/src/components/admin/usage/__tests__/UsageStatsCards.spec.ts
new file mode 100644
index 0000000000..c73dec0b13
--- /dev/null
+++ b/frontend/src/components/admin/usage/__tests__/UsageStatsCards.spec.ts
@@ -0,0 +1,67 @@
+import { describe, expect, it, vi } from 'vitest'
+import { mount } from '@vue/test-utils'
+
+import UsageStatsCards from '../UsageStatsCards.vue'
+
+const messages: Record = {
+ 'usage.totalRequests': 'Total Requests',
+ 'usage.inSelectedRange': 'in selected range',
+ 'usage.totalTokens': 'Total Tokens',
+ 'usage.in': 'In',
+ 'usage.out': 'Out',
+ 'usage.cacheTotal': 'Cache',
+ 'usage.cacheBreakdown': 'Cache Token Breakdown',
+ 'usage.cacheCreationTokensLabel': 'Cache Creation',
+ 'usage.cacheReadTokensLabel': 'Cache Read',
+ 'usage.totalCost': 'Total Cost',
+ 'usage.accountCost': 'Cost',
+ 'usage.standardCost': 'Standard',
+ 'usage.avgDuration': 'Avg Duration',
+}
+
+vi.mock('vue-i18n', async () => {
+ const actual = await vi.importActual('vue-i18n')
+ return {
+ ...actual,
+ useI18n: () => ({
+ t: (key: string) => messages[key] ?? key,
+ }),
+ }
+})
+
+const stats = {
+ total_requests: 1,
+ total_input_tokens: 100,
+ total_output_tokens: 50,
+ total_cache_tokens: 34,
+ total_cache_creation_tokens: 12,
+ total_cache_read_tokens: 22,
+ total_tokens: 184,
+ total_cost: 0.001,
+ total_actual_cost: 0.001,
+ total_account_cost: 0.001,
+ average_duration_ms: 250,
+}
+
+describe('UsageStatsCards', () => {
+ it('shows cache token breakdown values', () => {
+ const wrapper = mount(UsageStatsCards, {
+ props: {
+ stats,
+ },
+ global: {
+ stubs: {
+ Icon: true,
+ },
+ },
+ })
+
+ const text = wrapper.text()
+ expect(text).toContain('Cache: 34')
+ expect(text).toContain('Cache Token Breakdown')
+ expect(text).toContain('Cache Creation')
+ expect(text).toContain('12')
+ expect(text).toContain('Cache Read')
+ expect(text).toContain('22')
+ })
+})
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index c26d4d063c..0ac93a9811 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -878,6 +878,10 @@ export default {
cacheTtlOverridden1h: 'Billed as 1h',
totalRequests: 'Total Requests',
totalTokens: 'Total Tokens',
+ cacheTotal: 'Cache',
+ cacheBreakdown: 'Cache Token Breakdown',
+ cacheCreationTokensLabel: 'Cache Creation',
+ cacheReadTokensLabel: 'Cache Read',
totalCost: 'Total Cost',
standardCost: 'Standard',
actualCost: 'Actual',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 0ce766f9f7..ec9c51a171 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -882,6 +882,10 @@ export default {
cacheTtlOverridden1h: '按 1h 计费',
totalRequests: '总请求数',
totalTokens: '总 Token',
+ cacheTotal: '缓存',
+ cacheBreakdown: '缓存 Token 明细',
+ cacheCreationTokensLabel: '缓存创建',
+ cacheReadTokensLabel: '缓存读取',
totalCost: '总消费',
standardCost: '标准',
actualCost: '实际',