From 4a0386472db43670d0ee2ea852d4bdd1736bc031 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Fri, 6 Feb 2026 11:23:50 +0800 Subject: [PATCH] feat(ui): show success/failure in API usage stats --- src/components/usage/ApiDetailsCard.tsx | 20 +++++++-- src/utils/usage.ts | 57 +++++++++++++++++++++---- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/components/usage/ApiDetailsCard.tsx b/src/components/usage/ApiDetailsCard.tsx index 9c69327..246a853 100644 --- a/src/components/usage/ApiDetailsCard.tsx +++ b/src/components/usage/ApiDetailsCard.tsx @@ -39,10 +39,18 @@ export function ApiDetailsCard({ apiStats, loading, hasPrices }: ApiDetailsCardP {api.endpoint}
- {t('usage_stats.requests_count')}: {api.totalRequests} + + + {t('usage_stats.requests_count')}: {api.totalRequests.toLocaleString()} + + + ({api.successCount.toLocaleString()}{' '} + {api.failureCount.toLocaleString()}) + + - Tokens: {formatTokensInMillions(api.totalTokens)} + {t('usage_stats.tokens_count')}: {formatTokensInMillions(api.totalTokens)} {hasPrices && api.totalCost > 0 && ( @@ -61,7 +69,13 @@ export function ApiDetailsCard({ apiStats, loading, hasPrices }: ApiDetailsCardP
{model} - {stats.requests} {t('usage_stats.requests_count')} + + {stats.requests.toLocaleString()} + + ({stats.successCount.toLocaleString()}{' '} + {stats.failureCount.toLocaleString()}) + + {formatTokensInMillions(stats.tokens)}
diff --git a/src/utils/usage.ts b/src/utils/usage.ts index 48bf20c..ca50ed2 100644 --- a/src/utils/usage.ts +++ b/src/utils/usage.ts @@ -54,9 +54,11 @@ export interface UsageDetail { export interface ApiStats { endpoint: string; totalRequests: number; + successCount: number; + failureCount: number; totalTokens: number; totalCost: number; - models: Record; + models: Record; } const TOKENS_PER_PRICE_UNIT = 1_000_000; @@ -542,28 +544,65 @@ export function getApiStats(usageData: any, modelPrices: Record).forEach(([endpoint, apiData]) => { - const models: Record = {}; + const models: Record = {}; + let derivedSuccessCount = 0; + let derivedFailureCount = 0; let totalCost = 0; const modelsData = apiData?.models || {}; Object.entries(modelsData as Record).forEach(([modelName, modelData]) => { - models[modelName] = { - requests: modelData.total_requests || 0, - tokens: modelData.total_tokens || 0 - }; + const details = Array.isArray(modelData.details) ? modelData.details : []; + const hasExplicitCounts = + typeof modelData.success_count === 'number' || typeof modelData.failure_count === 'number'; + + let successCount = 0; + let failureCount = 0; + if (hasExplicitCounts) { + successCount += Number(modelData.success_count) || 0; + failureCount += Number(modelData.failure_count) || 0; + } const price = modelPrices[modelName]; - if (price) { - const details = Array.isArray(modelData.details) ? modelData.details : []; + if (details.length > 0 && (!hasExplicitCounts || price)) { details.forEach((detail: any) => { - totalCost += calculateCost({ ...detail, __modelName: modelName }, modelPrices); + if (!hasExplicitCounts) { + if (detail?.failed === true) { + failureCount += 1; + } else { + successCount += 1; + } + } + + if (price) { + totalCost += calculateCost({ ...detail, __modelName: modelName }, modelPrices); + } }); } + + models[modelName] = { + requests: modelData.total_requests || 0, + successCount, + failureCount, + tokens: modelData.total_tokens || 0 + }; + derivedSuccessCount += successCount; + derivedFailureCount += failureCount; }); + const hasApiExplicitCounts = + typeof apiData?.success_count === 'number' || typeof apiData?.failure_count === 'number'; + const successCount = hasApiExplicitCounts + ? (Number(apiData?.success_count) || 0) + : derivedSuccessCount; + const failureCount = hasApiExplicitCounts + ? (Number(apiData?.failure_count) || 0) + : derivedFailureCount; + result.push({ endpoint: maskUsageSensitiveValue(endpoint) || endpoint, totalRequests: apiData.total_requests || 0, + successCount, + failureCount, totalTokens: apiData.total_tokens || 0, totalCost, models