Merge pull request #2682 from Xch13/fix-cache-hit-rate
fix(frontend): 修正 Cache Hit Rate 计算分母,包含全部 prompt tokens
This commit is contained in:
commit
e674a5628c
@ -109,8 +109,8 @@ const chartData = computed(() => {
|
||||
{
|
||||
label: 'Cache Hit Rate',
|
||||
data: props.trendData.map((d) => {
|
||||
const total = d.cache_read_tokens + d.cache_creation_tokens
|
||||
return total > 0 ? (d.cache_read_tokens / total) * 100 : 0
|
||||
const totalPromptTokens = d.input_tokens + d.cache_read_tokens + d.cache_creation_tokens
|
||||
return totalPromptTokens > 0 ? (d.cache_read_tokens / totalPromptTokens) * 100 : 0
|
||||
}),
|
||||
borderColor: chartColors.value.cacheHitRate,
|
||||
backgroundColor: `${chartColors.value.cacheHitRate}20`,
|
||||
|
||||
120
frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts
Normal file
120
frontend/src/components/charts/__tests__/TokenUsageTrend.spec.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
import TokenUsageTrend from '../TokenUsageTrend.vue'
|
||||
|
||||
const messages: Record<string, string> = {
|
||||
'admin.dashboard.tokenUsageTrend': 'Token Usage Trend',
|
||||
'admin.dashboard.noDataAvailable': 'No data available',
|
||||
}
|
||||
|
||||
vi.mock('vue-i18n', async () => {
|
||||
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
|
||||
return {
|
||||
...actual,
|
||||
useI18n: () => ({
|
||||
t: (key: string) => messages[key] ?? key,
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('vue-chartjs', () => ({
|
||||
Line: {
|
||||
props: ['data', 'options'],
|
||||
template: '<div class="chart-data">{{ JSON.stringify(data) }}</div>',
|
||||
},
|
||||
}))
|
||||
|
||||
describe('TokenUsageTrend', () => {
|
||||
it('calculates cache hit rate against all prompt tokens', () => {
|
||||
const wrapper = mount(TokenUsageTrend, {
|
||||
props: {
|
||||
trendData: [
|
||||
{
|
||||
date: '2026-05-08',
|
||||
requests: 1,
|
||||
input_tokens: 500,
|
||||
output_tokens: 100,
|
||||
cache_creation_tokens: 0,
|
||||
cache_read_tokens: 1500,
|
||||
cost: 0.01,
|
||||
actual_cost: 0.005,
|
||||
},
|
||||
],
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
LoadingSpinner: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const chartData = JSON.parse(wrapper.find('.chart-data').text())
|
||||
const hitRateDataset = chartData.datasets.find(
|
||||
(ds: any) => ds.label === 'Cache Hit Rate'
|
||||
)
|
||||
// Hit rate = 1500 / (500 + 1500 + 0) * 100 = 75%
|
||||
expect(hitRateDataset.data[0]).toBe(75)
|
||||
})
|
||||
|
||||
it('returns 0 hit rate when all prompt tokens are zero', () => {
|
||||
const wrapper = mount(TokenUsageTrend, {
|
||||
props: {
|
||||
trendData: [
|
||||
{
|
||||
date: '2026-05-08',
|
||||
requests: 0,
|
||||
input_tokens: 0,
|
||||
output_tokens: 0,
|
||||
cache_creation_tokens: 0,
|
||||
cache_read_tokens: 0,
|
||||
cost: 0,
|
||||
actual_cost: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
LoadingSpinner: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const chartData = JSON.parse(wrapper.find('.chart-data').text())
|
||||
const hitRateDataset = chartData.datasets.find(
|
||||
(ds: any) => ds.label === 'Cache Hit Rate'
|
||||
)
|
||||
expect(hitRateDataset.data[0]).toBe(0)
|
||||
})
|
||||
|
||||
it('includes cache_creation_tokens in denominator for Anthropic models', () => {
|
||||
const wrapper = mount(TokenUsageTrend, {
|
||||
props: {
|
||||
trendData: [
|
||||
{
|
||||
date: '2026-05-08',
|
||||
requests: 1,
|
||||
input_tokens: 200,
|
||||
output_tokens: 50,
|
||||
cache_creation_tokens: 300,
|
||||
cache_read_tokens: 500,
|
||||
cost: 0.02,
|
||||
actual_cost: 0.01,
|
||||
},
|
||||
],
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
LoadingSpinner: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const chartData = JSON.parse(wrapper.find('.chart-data').text())
|
||||
const hitRateDataset = chartData.datasets.find(
|
||||
(ds: any) => ds.label === 'Cache Hit Rate'
|
||||
)
|
||||
// Hit rate = 500 / (200 + 500 + 300) * 100 = 50%
|
||||
expect(hitRateDataset.data[0]).toBe(50)
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user