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',
|
label: 'Cache Hit Rate',
|
||||||
data: props.trendData.map((d) => {
|
data: props.trendData.map((d) => {
|
||||||
const total = d.cache_read_tokens + d.cache_creation_tokens
|
const totalPromptTokens = d.input_tokens + d.cache_read_tokens + d.cache_creation_tokens
|
||||||
return total > 0 ? (d.cache_read_tokens / total) * 100 : 0
|
return totalPromptTokens > 0 ? (d.cache_read_tokens / totalPromptTokens) * 100 : 0
|
||||||
}),
|
}),
|
||||||
borderColor: chartColors.value.cacheHitRate,
|
borderColor: chartColors.value.cacheHitRate,
|
||||||
backgroundColor: `${chartColors.value.cacheHitRate}20`,
|
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