From d84cf61915d3da49a85c57732be5eb4a43a8b15f Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 5 Apr 2026 11:14:46 +0800 Subject: [PATCH] feat: improve usage latency formatting --- src/components/usage/ModelStatsCard.tsx | 60 +++++- .../usage/RequestEventsDetailsCard.tsx | 12 +- src/components/usage/StatCards.tsx | 20 +- src/i18n/locales/en.json | 1 + src/i18n/locales/ru.json | 1 + src/i18n/locales/zh-CN.json | 1 + src/pages/UsagePage.module.scss | 5 + src/utils/usage.ts | 194 ++++++++++++++++-- 8 files changed, 242 insertions(+), 52 deletions(-) diff --git a/src/components/usage/ModelStatsCard.tsx b/src/components/usage/ModelStatsCard.tsx index 71ad330..cfa34dc 100644 --- a/src/components/usage/ModelStatsCard.tsx +++ b/src/components/usage/ModelStatsCard.tsx @@ -1,17 +1,15 @@ import { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Card } from '@/components/ui/Card'; -import { formatCompactNumber, formatUsd } from '@/utils/usage'; +import { + formatCompactNumber, + formatDurationMs, + formatUsd, + type ModelStatsSummary, +} from '@/utils/usage'; import styles from '@/pages/UsagePage.module.scss'; -export interface ModelStat { - model: string; - requests: number; - successCount: number; - failureCount: number; - tokens: number; - cost: number; -} +export type ModelStat = ModelStatsSummary; export interface ModelStatsCardProps { modelStats: ModelStat[]; @@ -19,7 +17,14 @@ export interface ModelStatsCardProps { hasPrices: boolean; } -type SortKey = 'model' | 'requests' | 'tokens' | 'cost' | 'successRate'; +type SortKey = + | 'model' + | 'requests' + | 'tokens' + | 'cost' + | 'successRate' + | 'averageLatencyMs' + | 'totalLatencyMs'; type SortDir = 'asc' | 'desc'; interface ModelStatWithRate extends ModelStat { @@ -48,7 +53,15 @@ export function ModelStatsCard({ modelStats, loading, hasPrices }: ModelStatsCar const dir = sortDir === 'asc' ? 1 : -1; list.sort((a, b) => { if (sortKey === 'model') return dir * a.model.localeCompare(b.model); - return dir * ((a[sortKey] as number) - (b[sortKey] as number)); + const left = a[sortKey]; + const right = b[sortKey]; + const leftValid = typeof left === 'number' && Number.isFinite(left); + const rightValid = typeof right === 'number' && Number.isFinite(right); + + if (!leftValid && !rightValid) return 0; + if (!leftValid) return 1; + if (!rightValid) return -1; + return dir * (left - right); }); return list; }, [modelStats, sortKey, sortDir]); @@ -95,6 +108,27 @@ export function ModelStatsCard({ modelStats, loading, hasPrices }: ModelStatsCar {t('usage_stats.tokens_count')}{arrow('tokens')} + + + + + +