Fix image billing size normalization

This commit is contained in:
2ue committed 2026-05-12 15:21:31 +08:00
1 parent 62ccd0ff39
commit bb4c1abe28
45 files changed
+3270 -313

No files matched your search

@@ -22,6 +22,26 @@ const messages: Record<string, string> = {
'usage.original': 'Original',
'usage.userBilled': 'User billed',
'usage.accountBilled': 'Account billed',
'usage.imageUnit': ' images',
'usage.imageCount': 'Image count',
'usage.imageBillingSize': 'Billing size',
'usage.imageInputSize': 'Input size',
'usage.imageOutputSize': 'Output size',
'usage.imageSizeSource': 'Size source',
'usage.imageSizeBreakdown': 'Size breakdown',
'usage.imageSizeSourceOutput': 'Upstream output',
'usage.imageSizeSourceInput': 'Request input',
'usage.imageSizeSourceDefault': 'Default billing tier',
'usage.imageSizeSourceLegacy': 'Legacy record',
'usage.imageSizeSourceMissing': 'Not recorded',
'usage.imageSizeNotRecorded': 'not recorded',
'usage.imageSizeLegacyUnstandardized': 'legacy unstandardized',
'usage.imageSizeUnknown': 'unknown',
'usage.imageUnitPrice': 'Per-image price',
'usage.imageTotalPrice': 'Image total price',
'admin.usage.billingModeToken': 'Token',
'admin.usage.billingModePerRequest': 'Per request',
'admin.usage.billingModeImage': 'Image',
}
vi.mock('vue-i18n', async () => {
@@ -40,12 +60,42 @@ const DataTableStub = {
<div>
<div v-for="row in data" :key="row.request_id">
<slot name="cell-model" :row="row" :value="row.model" />
<slot name="cell-billing_mode" :row="row" />
<slot name="cell-tokens" :row="row" />
<slot name="cell-cost" :row="row" />
</div>
</div>
`,
}
const baseImageRow = {
request_id: 'req-admin-image',
model: 'gpt-image-2',
actual_cost: 0.4,
total_cost: 0.4,
account_rate_multiplier: 1,
rate_multiplier: 1,
service_tier: null,
input_cost: 0,
output_cost: 0,
cache_creation_cost: 0,
cache_read_cost: 0,
input_tokens: 0,
output_tokens: 0,
cache_creation_tokens: 0,
cache_read_tokens: 0,
cache_creation_5m_tokens: 0,
cache_creation_1h_tokens: 0,
cache_ttl_overridden: false,
billing_mode: 'image',
image_count: 2,
image_size: '2K',
image_input_size: null,
image_output_size: null,
image_size_source: null,
image_size_breakdown: null,
}
describe('admin UsageTable tooltip', () => {
beforeEach(() => {
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({
@@ -93,7 +143,8 @@ describe('admin UsageTable tooltip', () => {
},
})
await wrapper.find('.group.relative').trigger('mouseenter')
const tooltipTriggers = wrapper.findAll('.group.relative')
await tooltipTriggers[tooltipTriggers.length - 1].trigger('mouseenter')
await nextTick()
const text = wrapper.text()
@@ -147,4 +198,126 @@ describe('admin UsageTable tooltip', () => {
expect(text).toContain('claude-sonnet-4')
expect(text).toContain('claude-sonnet-4-20250514')
})
it.each([
{
name: 'defaulted row',
row: {
...baseImageRow,
request_id: 'req-admin-default-image',
image_size: '2K',
image_input_size: 'auto',
image_output_size: null,
image_size_source: 'default',
},
expected: ['2K', 'Default billing tier', 'auto', 'unknown'],
},
{
name: 'output-sourced row',
row: {
...baseImageRow,
request_id: 'req-admin-output-image',
image_size: '4K',
image_input_size: '1024x1024',
image_output_size: '3840x2160',
image_size_source: 'output',
image_size_breakdown: { '4K': 1 },
},
expected: ['4K', 'Upstream output', '1024x1024', '3840x2160', '4K x 1'],
},
{
name: 'input-sourced row',
row: {
...baseImageRow,
request_id: 'req-admin-input-image',
image_size: '1K',
image_input_size: '1024x1024',
image_output_size: null,
image_size_source: 'input',
},
expected: ['1K', 'Request input', '1024x1024', 'unknown'],
},
{
name: 'legacy unstandardized row',
row: {
...baseImageRow,
request_id: 'req-admin-legacy-unstandardized-image',
image_size: '512x512',
image_input_size: null,
image_output_size: null,
image_size_source: null,
},
expected: ['legacy unstandardized: 512x512', 'Legacy record', 'unknown'],
},
])('shows image usage metadata for $name', async ({ row, expected }) => {
const wrapper = mount(UsageTable, {
props: {
data: [row],
loading: false,
columns: [],
},
global: {
stubs: {
DataTable: DataTableStub,
EmptyState: true,
Icon: true,
Teleport: true,
},
},
})
await wrapper.find('.group.relative').trigger('mouseenter')
await nextTick()
const text = wrapper.text()
expect(text).toContain('Image count')
expect(text).toContain('Billing size')
expect(text).toContain('Size source')
expect(text).toContain('Input size')
expect(text).toContain('Output size')
expect(text).toContain('Per-image price')
expect(text).toContain('Image total price')
for (const value of expected) {
expect(text).toContain(value)
}
})
it('displays historical image rows with missing billing_mode as image usage without a 2K fallback', async () => {
const wrapper = mount(UsageTable, {
props: {
data: [
{
...baseImageRow,
request_id: 'req-admin-legacy-missing-image',
billing_mode: null,
image_size: null,
image_input_size: null,
image_output_size: null,
image_size_source: null,
image_size_breakdown: null,
},
],
loading: false,
columns: [],
},
global: {
stubs: {
DataTable: DataTableStub,
EmptyState: true,
Icon: true,
Teleport: true,
},
},
})
await wrapper.find('.group.relative').trigger('mouseenter')
await nextTick()
const text = wrapper.text()
expect(text).toContain('Image')
expect(text).toContain('Image count')
expect(text).toContain('Per-image price')
expect(text).toContain('not recorded')
expect(text).not.toContain('(2K)')
})
})