fix(frontend): display image_output_tokens breakdown in usage pages

When image generation models are billed by token (channel pricing mode=token),
the usage pages previously showed only image count format instead of detailed
token breakdown. This fixes the display to properly show image output tokens
separately from text output tokens.
This commit is contained in:
erio
2026-05-31 22:53:22 +08:00
parent f18451e56f
commit ef5ad0fb10
9 changed files with 175 additions and 64 deletions
+21
View File
@@ -17,3 +17,24 @@ export function getBillingModeBadgeClass(mode: string | null | undefined): strin
default: return 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
}
}
interface ImageBillingRow {
image_count: number
billing_mode?: string | null
total_cost: number
}
export function isImageUsage(row: Pick<ImageBillingRow, 'image_count' | 'billing_mode'> | null | undefined): boolean {
return (row?.image_count ?? 0) > 0 && row?.billing_mode !== BILLING_MODE_TOKEN
}
export function getDisplayBillingMode(row: Pick<ImageBillingRow, 'billing_mode' | 'image_count'> | null | undefined): string | null | undefined {
return row?.billing_mode
}
export function imageUnitPrice(row: Pick<ImageBillingRow, 'image_count' | 'total_cost'> | null): number {
if (!row || row.image_count <= 0) return 0
const total = row.total_cost ?? 0
const price = total / row.image_count
return Number.isFinite(price) ? price : 0
}
+22
View File
@@ -2,6 +2,28 @@ import type { UsageLog } from '@/types'
type Translate = (key: string) => string
// --- Image output token / cost helpers ---
type ImageOutputTokenRow = Pick<UsageLog, 'output_tokens' | 'image_output_tokens'>
type ImageOutputCostRow = Pick<UsageLog, 'image_output_cost'>
/** Whether the row contains any image-output tokens. */
export const hasImageOutputTokens = (row: ImageOutputTokenRow | null | undefined): boolean =>
(row?.image_output_tokens ?? 0) > 0
/**
* Text-only output tokens (total output minus image-output).
* Returns 0 when no text tokens exist.
*/
export const textOutputTokens = (row: ImageOutputTokenRow | null | undefined): number =>
Math.max(0, (row?.output_tokens ?? 0) - (row?.image_output_tokens ?? 0))
/** Whether the row has a non-zero image-output cost. */
export const hasImageOutputCost = (row: ImageOutputCostRow | null | undefined): boolean =>
(row?.image_output_cost ?? 0) > 0
// --- Image size / billing helpers ---
const knownImageSizeSources = new Set(['output', 'input', 'default', 'legacy'])
const knownImageBillingSizes = new Set(['1K', '2K', '4K', 'mixed'])