diff --git a/src/components/usage/ModelStatsCard.tsx b/src/components/usage/ModelStatsCard.tsx
index 859fa16..763cf10 100644
--- a/src/components/usage/ModelStatsCard.tsx
+++ b/src/components/usage/ModelStatsCard.tsx
@@ -6,6 +6,8 @@ import styles from '@/pages/UsagePage.module.scss';
export interface ModelStat {
model: string;
requests: number;
+ successCount: number;
+ failureCount: number;
tokens: number;
cost: number;
}
@@ -38,7 +40,15 @@ export function ModelStatsCard({ modelStats, loading, hasPrices }: ModelStatsCar
{modelStats.map((stat) => (
| {stat.model} |
- {stat.requests.toLocaleString()} |
+
+
+ {stat.requests.toLocaleString()}
+
+ ({stat.successCount.toLocaleString()}{' '}
+ {stat.failureCount.toLocaleString()})
+
+
+ |
{formatTokensInMillions(stat.tokens)} |
{hasPrices && {stat.cost > 0 ? formatUsd(stat.cost) : '--'} | }
diff --git a/src/pages/UsagePage.module.scss b/src/pages/UsagePage.module.scss
index 962913a..bce9d85 100644
--- a/src/pages/UsagePage.module.scss
+++ b/src/pages/UsagePage.module.scss
@@ -456,6 +456,18 @@
word-break: break-all;
}
+.requestCountCell {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 6px;
+ font-variant-numeric: tabular-nums;
+}
+
+.requestBreakdown {
+ color: var(--text-secondary);
+ white-space: nowrap;
+}
+
// Pricing Section (80%比例)
.pricingSection {
display: flex;
diff --git a/src/utils/usage.ts b/src/utils/usage.ts
index a01e3c4..48bf20c 100644
--- a/src/utils/usage.ts
+++ b/src/utils/usage.ts
@@ -579,6 +579,8 @@ export function getApiStats(usageData: any, modelPrices: Record): Array<{
model: string;
requests: number;
+ successCount: number;
+ failureCount: number;
tokens: number;
cost: number;
}> {
@@ -586,20 +588,39 @@ export function getModelStats(usageData: any, modelPrices: Record();
+ const modelMap = new Map();
Object.values(usageData.apis as Record).forEach(apiData => {
const models = apiData?.models || {};
Object.entries(models as Record).forEach(([modelName, modelData]) => {
- const existing = modelMap.get(modelName) || { requests: 0, tokens: 0, cost: 0 };
+ const existing = modelMap.get(modelName) || { requests: 0, successCount: 0, failureCount: 0, tokens: 0, cost: 0 };
existing.requests += modelData.total_requests || 0;
existing.tokens += modelData.total_tokens || 0;
+ const details = Array.isArray(modelData.details) ? modelData.details : [];
+
const price = modelPrices[modelName];
- if (price) {
- const details = Array.isArray(modelData.details) ? modelData.details : [];
+
+ const hasExplicitCounts =
+ typeof modelData.success_count === 'number' || typeof modelData.failure_count === 'number';
+ if (hasExplicitCounts) {
+ existing.successCount += Number(modelData.success_count) || 0;
+ existing.failureCount += Number(modelData.failure_count) || 0;
+ }
+
+ if (details.length > 0 && (!hasExplicitCounts || price)) {
details.forEach((detail: any) => {
- existing.cost += calculateCost({ ...detail, __modelName: modelName }, modelPrices);
+ if (!hasExplicitCounts) {
+ if (detail?.failed === true) {
+ existing.failureCount += 1;
+ } else {
+ existing.successCount += 1;
+ }
+ }
+
+ if (price) {
+ existing.cost += calculateCost({ ...detail, __modelName: modelName }, modelPrices);
+ }
});
}
modelMap.set(modelName, existing);