Add automatic B/T compact unit formatting

This commit is contained in:
fawney19
2026-05-22 17:13:16 +08:00
parent ecf6019ccb
commit ef04f4b0fb
16 changed files with 161 additions and 125 deletions
@@ -559,6 +559,7 @@ import RoutingTab from './RoutingTab.vue'
import ModelMappingsTab from './ModelMappingsTab.vue'
import { sortResolutionEntries } from '@/utils/form'
import { parseApiError } from '@/utils/errorParser'
import { formatCompactNumber, formatTokens } from '@/utils/format'
import { getGlobalModelRoutingPreview } from '@/api/global-models'
// 使用外部类型定义
@@ -734,13 +735,7 @@ function formatPixelLimit(value: number | null): string {
}
function formatPixels(value: number): string {
if (value >= 1_000_000) {
return `${(value / 1_000_000).toFixed(value % 1_000_000 === 0 ? 0 : 2)}M px`
}
if (value >= 1_000) {
return `${(value / 1_000).toFixed(0)}K px`
}
return `${value} px`
return `${formatCompactNumber(value)} px`
}
const detailTab = ref('basic')
@@ -790,12 +785,7 @@ function getTierCount(tieredPricing: TieredPricingConfig | undefined | null): nu
// 格式化阶梯上限(tokens 数量简化显示)
function formatTierLimit(limit: number | null | undefined): string {
if (limit == null) return ''
if (limit >= 1000000) {
return `${(limit / 1000000).toFixed(1)}M`
} else if (limit >= 1000) {
return `${(limit / 1000).toFixed(0)}K`
}
return limit.toString()
return formatTokens(limit)
}
// 获取 1h 缓存价格
@@ -286,6 +286,7 @@
import { ref, computed, watch, reactive } from 'vue'
import { Plus, X } from 'lucide-vue-next'
import { Button, Input, Label } from '@/components/ui'
import { formatTokens } from '@/utils/format'
import type { TieredPricingConfig, PricingTier, ImageOutputPriceRange } from '@/api/endpoints/types'
type ImageOutputQuality = 'low' | 'medium' | 'high'
@@ -434,17 +435,6 @@ function getAvailableThresholds(index: number) {
return options
}
// 格式化 token 数量
function formatTokens(tokens: number): string {
if (tokens >= 1000000) {
return `${(tokens / 1000000).toFixed(tokens % 1000000 === 0 ? 0 : 1)}M`
}
if (tokens >= 1000) {
return `${(tokens / 1000).toFixed(0)}K`
}
return tokens.toString()
}
// 缓存价格自动计算
function getAutoCacheCreation(index: number): number {
const inputPrice = localTiers.value[index]?.input_price_per_1m || 0
@@ -112,4 +112,16 @@ describe('poolStatsDisplay', () => {
total_cost_usd: '$12.35',
})
})
it('promotes large token totals above M', () => {
const display = buildPoolStatsDisplay(
createCodexKey({ total_tokens: 1_500_000_000 }),
'openai',
'account_total',
)
expect(display.kind).toBe('account_total')
if (display.kind !== 'account_total') throw new Error('expected account total display')
expect(metricValues(display.metrics).total_tokens).toBe('1.5B')
})
})
@@ -1,5 +1,6 @@
import type { QuotaWindowUsageSnapshot } from '@/api/endpoints/types/statusSnapshot'
import type { PoolManagementStatsMode } from '@/features/pool/utils/poolManagementState'
import { formatCompactNumber } from '@/utils/format'
export type PoolStatsMetricKey = 'request_count' | 'total_tokens' | 'total_cost_usd'
export type PoolStatsDisplayKind = 'account_total' | 'codex_cycle'
@@ -63,9 +64,7 @@ export function formatPoolStatInteger(value: number | null | undefined): string
export function formatPoolTokenCount(value: number | null | undefined): string {
const n = Number(value ?? 0)
if (!Number.isFinite(n) || n <= 0) return '0'
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
return String(Math.round(n))
return formatCompactNumber(Math.round(n), { fractionDigits: 1 })
}
export function formatPoolStatUsd(value: number | string | null | undefined): string {
@@ -205,6 +205,7 @@ import { RefreshCw, RotateCcw } from 'lucide-vue-next'
import { getPoolStatus, clearPoolCooldown, resetPoolCost } from '@/api/endpoints/pool'
import type { PoolStatusResponse } from '@/api/endpoints/pool'
import { parseApiError } from '@/utils/errorParser'
import { formatTokens } from '@/utils/format'
import { useToast } from '@/composables/useToast'
import Card from '@/components/ui/card.vue'
@@ -287,12 +288,6 @@ function formatTTL(seconds: number): string {
return m > 0 ? `${m}m ${s}s` : `${s}s`
}
function formatTokens(tokens: number): string {
if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`
if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(1)}K`
return String(tokens)
}
function formatEmaHeat(value: number): string {
if (!Number.isFinite(value) || value <= 0) return '0.0'
return value.toFixed(1)
@@ -1440,6 +1440,7 @@ import {
} from '../utils/quotaAutoRefreshCooldown'
import { getOAuthOrgBadge } from '@/utils/oauthIdentity'
import { getOAuthRefreshFeedback } from '@/utils/oauthRefreshFeedback'
import { formatCompactNumber } from '@/utils/format'
import {
canEditOAuthCredential,
canExportOAuthCredential,
@@ -2633,13 +2634,10 @@ const formatKiroUpdatedAt = formatUpdatedAt
// 格式化 Kiro 使用量(带单位)
function formatKiroUsage(value: number | undefined): string {
if (value === undefined || value === null) return '-'
if (value >= 1000000) {
return `${(value / 1000000).toFixed(1)}M`
}
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}K`
}
return value.toFixed(1)
const normalized = Number(value)
if (!Number.isFinite(normalized)) return '-'
if (normalized >= 1000) return formatCompactNumber(normalized, { fractionDigits: 1 })
return normalized.toFixed(1)
}
// 格式化 Kiro 重置时间
@@ -542,6 +542,7 @@ import { ChevronLeft, ChevronRight, ExternalLink } from 'lucide-vue-next'
import { requestTraceApi, type RequestTrace, type CandidateRecord, type ImageProgress } from '@/api/requestTrace'
import { log } from '@/utils/logger'
import { parseApiError } from '@/utils/errorParser'
import { formatTokens } from '@/utils/format'
import { formatApiFormat } from '@/api/endpoints/types/api-format'
import { useDarkMode } from '@/composables/useDarkMode'
import { resolveTimelineFinalStatus } from '../utils/status'
@@ -629,7 +630,7 @@ const usageData = computed(() => props.usageData)
// 格式化数字
const formatNumber = (num: number): string => {
return num.toLocaleString('zh-CN')
return formatTokens(num)
}
// 获取最终状态标签
@@ -794,7 +794,7 @@ import { AlertTriangle, Check, Columns2, RefreshCw, X, Monitor, Server, MessageS
import { dashboardApi, type RequestDetail, type RequestErrorDomain } from '@/api/dashboard'
import type { ImageProgress, RequestTrace } from '@/api/requestTrace'
import { formatApiFormat } from '@/api/endpoints/types/api-format'
import { formatShortRequestId } from '@/utils/format'
import { formatCompactNumber, formatShortRequestId, formatTokens } from '@/utils/format'
import { log } from '@/utils/logger'
import { getEffectiveInputTokens } from '../token-normalization'
import {
@@ -2274,12 +2274,7 @@ function getTaskTypeLabel(taskType: string): string {
}
function formatNumber(num: number): string {
if (num >= 1_000_000) {
return `${(num / 1_000_000).toFixed(1) }M`
} else if (num >= 1_000) {
return `${(num / 1_000).toFixed(1) }K`
}
return num.toLocaleString()
return formatTokens(num)
}
function parseImageSizePixels(size: string | null): number | null {
@@ -2301,13 +2296,7 @@ function formatImagePriceBucket(bucket: string): string {
}
function formatPixels(value: number): string {
if (value >= 1_000_000) {
return `${(value / 1_000_000).toFixed(value % 1_000_000 === 0 ? 0 : 2)}M px`
}
if (value >= 1_000) {
return `${(value / 1_000).toFixed(0)}K px`
}
return `${value} px`
return `${formatCompactNumber(value)} px`
}
// 格式化响应时间,自动选择合适的单位