feat(usage): show per-model success/failure counts

This commit is contained in:
hkfires
2026-01-25 11:29:34 +08:00
parent 76e9eb4aa0
commit 034c086e31
3 changed files with 49 additions and 6 deletions

View File

@@ -6,6 +6,8 @@ import styles from '@/pages/UsagePage.module.scss';
export interface ModelStat { export interface ModelStat {
model: string; model: string;
requests: number; requests: number;
successCount: number;
failureCount: number;
tokens: number; tokens: number;
cost: number; cost: number;
} }
@@ -38,7 +40,15 @@ export function ModelStatsCard({ modelStats, loading, hasPrices }: ModelStatsCar
{modelStats.map((stat) => ( {modelStats.map((stat) => (
<tr key={stat.model}> <tr key={stat.model}>
<td className={styles.modelCell}>{stat.model}</td> <td className={styles.modelCell}>{stat.model}</td>
<td>{stat.requests.toLocaleString()}</td> <td>
<span className={styles.requestCountCell}>
<span>{stat.requests.toLocaleString()}</span>
<span className={styles.requestBreakdown}>
(<span className={styles.statSuccess}>{stat.successCount.toLocaleString()}</span>{' '}
<span className={styles.statFailure}>{stat.failureCount.toLocaleString()}</span>)
</span>
</span>
</td>
<td>{formatTokensInMillions(stat.tokens)}</td> <td>{formatTokensInMillions(stat.tokens)}</td>
{hasPrices && <td>{stat.cost > 0 ? formatUsd(stat.cost) : '--'}</td>} {hasPrices && <td>{stat.cost > 0 ? formatUsd(stat.cost) : '--'}</td>}
</tr> </tr>

View File

@@ -456,6 +456,18 @@
word-break: break-all; 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%比例) // Pricing Section (80%比例)
.pricingSection { .pricingSection {
display: flex; display: flex;

View File

@@ -579,6 +579,8 @@ export function getApiStats(usageData: any, modelPrices: Record<string, ModelPri
export function getModelStats(usageData: any, modelPrices: Record<string, ModelPrice>): Array<{ export function getModelStats(usageData: any, modelPrices: Record<string, ModelPrice>): Array<{
model: string; model: string;
requests: number; requests: number;
successCount: number;
failureCount: number;
tokens: number; tokens: number;
cost: number; cost: number;
}> { }> {
@@ -586,20 +588,39 @@ export function getModelStats(usageData: any, modelPrices: Record<string, ModelP
return []; return [];
} }
const modelMap = new Map<string, { requests: number; tokens: number; cost: number }>(); const modelMap = new Map<string, { requests: number; successCount: number; failureCount: number; tokens: number; cost: number }>();
Object.values(usageData.apis as Record<string, any>).forEach(apiData => { Object.values(usageData.apis as Record<string, any>).forEach(apiData => {
const models = apiData?.models || {}; const models = apiData?.models || {};
Object.entries(models as Record<string, any>).forEach(([modelName, modelData]) => { Object.entries(models as Record<string, any>).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.requests += modelData.total_requests || 0;
existing.tokens += modelData.total_tokens || 0; existing.tokens += modelData.total_tokens || 0;
const price = modelPrices[modelName];
if (price) {
const details = Array.isArray(modelData.details) ? modelData.details : []; const details = Array.isArray(modelData.details) ? modelData.details : [];
const price = modelPrices[modelName];
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) => { details.forEach((detail: any) => {
if (!hasExplicitCounts) {
if (detail?.failed === true) {
existing.failureCount += 1;
} else {
existing.successCount += 1;
}
}
if (price) {
existing.cost += calculateCost({ ...detail, __modelName: modelName }, modelPrices); existing.cost += calculateCost({ ...detail, __modelName: modelName }, modelPrices);
}
}); });
} }
modelMap.set(modelName, existing); modelMap.set(modelName, existing);