Merge pull request #6283 from okbexx/feat/usage-compaction-kind

feat(usage): track native compaction requests
This commit is contained in:
Wesley Liddick
2026-08-31 11:19:37 +08:00
committed by GitHub
44 changed files with 806 additions and 77 deletions
@@ -127,6 +127,12 @@
<Select v-model="filters.request_type" :options="requestTypeOptions" @change="emitChange" />
</div>
<!-- Native compaction is independent of the transport request type. -->
<div v-if="mode !== 'errors'" class="w-full sm:w-auto sm:min-w-[180px]">
<label class="input-label">{{ t('usage.compactionFilter') }}</label>
<Select v-model="filters.native_compaction_v2" :options="compactionOptions" @change="emitChange" />
</div>
<!-- Billing Type Filter (usage only) -->
<div v-if="mode !== 'errors'" class="w-full sm:w-auto sm:min-w-[200px]">
<label class="input-label">{{ t('admin.usage.billingType') }}</label>
@@ -274,6 +280,11 @@ const requestTypeOptions = ref<SelectOption[]>([
{ value: 'cyber', label: t('usage.cyber') }
])
const compactionOptions = ref<SelectOption[]>([
{ value: null, label: t('usage.allCompactionTypes') },
{ value: true, label: t('usage.compactionOnly') }
])
const billingTypeOptions = ref<SelectOption[]>([
{ value: null, label: t('admin.usage.allBillingTypes') },
{ value: 0, label: t('admin.usage.billingTypeBalance') },
@@ -125,9 +125,18 @@
</template>
<template #cell-stream="{ row }">
<span class="inline-flex items-center rounded px-2 py-0.5 text-xs font-medium" :class="getRequestTypeBadgeClass(row)">
{{ getRequestTypeLabel(row) }}
</span>
<div class="flex flex-wrap items-center gap-1">
<span data-testid="request-type-badge" class="inline-flex items-center rounded px-2 py-0.5 text-xs font-medium" :class="getRequestTypeBadgeClass(row)">
{{ getRequestTypeLabel(row) }}
</span>
<span
v-if="row.native_compaction_v2"
data-testid="native-compaction-badge"
class="inline-flex items-center rounded bg-teal-100 px-2 py-0.5 text-xs font-medium text-teal-800 dark:bg-teal-900 dark:text-teal-200"
>
{{ t('usage.nativeCompactionV2') }}
</span>
</div>
</template>
<template #cell-billing_mode="{ row }">
@@ -19,6 +19,9 @@ const messages: Record<string, string> = {
'usage.ws': 'WS',
'usage.stream': 'Stream',
'usage.sync': 'Sync',
'usage.compactionFilter': 'Request Kind',
'usage.allCompactionTypes': 'All Requests',
'usage.compactionOnly': 'Compaction Only',
'admin.usage.billingType': 'Billing Type',
'admin.usage.allBillingTypes': 'All Billing Types',
'admin.usage.billingTypeBalance': 'Balance',
@@ -77,6 +80,7 @@ const defaultFilters = () => ({
account_id: undefined,
model: null,
request_type: null,
native_compaction_v2: null,
billing_type: null,
billing_mode: null,
upstream_model_mismatch: null,
@@ -259,3 +263,45 @@ describe('UsageFilters — model options come from prop (no dup request)', () =>
expect(opts.map((o) => o.value)).toEqual([null, 'claude-3', 'gpt-4o'])
})
})
describe('UsageFilters — native compaction filter', () => {
it('offers only All/Compaction and emits the independent boolean filter', async () => {
const SelectStub = {
name: 'Select',
props: ['modelValue', 'options'],
emits: ['update:modelValue', 'change'],
template: '<div />',
}
const filters = defaultFilters()
const wrapper = mount(UsageFilters, {
props: {
modelValue: filters,
exporting: false,
startDate: '2026-05-01',
endDate: '2026-05-28',
showActions: false,
modelOptions: [],
},
global: { stubs: { Select: SelectStub, Teleport: true } },
})
const compactionSelect = wrapper.findAllComponents(SelectStub).find((select: any) =>
(select.props('options') as Array<{ value: unknown }>).some((option) => option.value === true)
)
expect(compactionSelect).toBeDefined()
expect(compactionSelect!.props('options')).toEqual([
{ value: null, label: 'All Requests' },
{ value: true, label: 'Compaction Only' },
])
expect(compactionSelect!.props('options')).not.toEqual(
expect.arrayContaining([expect.objectContaining({ value: false })])
)
compactionSelect!.vm.$emit('update:modelValue', true)
compactionSelect!.vm.$emit('change')
await wrapper.vm.$nextTick()
expect(filters.native_compaction_v2).toBe(true)
expect(wrapper.emitted('change')).toBeTruthy()
})
})
@@ -54,6 +54,9 @@ const messages: Record<string, string> = {
'usage.imageSizeUnknown': 'unknown',
'usage.imageUnitPrice': 'Per-image price',
'usage.imageTotalPrice': 'Image total price',
'usage.stream': 'Stream',
'usage.sync': 'Sync',
'usage.nativeCompactionV2': 'Compaction',
'admin.usage.billingModeToken': 'Token',
'admin.usage.billingModePerRequest': 'Per request',
'admin.usage.billingModeImage': 'Image',
@@ -169,6 +172,56 @@ describe('admin UsageTable tooltip', () => {
expect(wrapper.get('[data-testid="long-context-billing-marker"]').text()).toBe('x2')
})
it('keeps the request type badge and adds a separate badge only for native compaction rows', () => {
const DataTableStreamStub = {
props: ['data'],
template: `
<div>
<div v-for="row in data" :key="row.request_id">
<slot name="cell-stream" :row="row" />
</div>
</div>
`,
}
const wrapper = mount(UsageTable, {
props: {
data: [
{
...baseImageRow,
request_id: 'req-compaction-stream',
request_type: 'stream',
stream: true,
native_compaction_v2: true,
},
{
...baseImageRow,
request_id: 'req-historical-sync',
request_type: 'sync',
stream: false,
native_compaction_v2: false,
},
],
loading: false,
columns: [],
},
global: {
stubs: {
DataTable: DataTableStreamStub,
EmptyState: true,
Icon: true,
Teleport: true,
},
},
})
const requestBadges = wrapper.findAll('[data-testid="request-type-badge"]')
expect(requestBadges).toHaveLength(2)
expect(requestBadges[0].text()).toBe('Stream')
expect(requestBadges[1].text()).toBe('Sync')
expect(wrapper.findAll('[data-testid="native-compaction-badge"]')).toHaveLength(1)
expect(wrapper.get('[data-testid="native-compaction-badge"]').text()).toBe('Compaction')
})
it('shows service tier and billing breakdown in cost tooltip', async () => {
const row = {
request_id: 'req-admin-1',