mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-19 03:00:49 +08:00
feat(ui): show success/failure in API usage stats
This commit is contained in:
@@ -39,10 +39,18 @@ export function ApiDetailsCard({ apiStats, loading, hasPrices }: ApiDetailsCardP
|
|||||||
<span className={styles.apiEndpoint}>{api.endpoint}</span>
|
<span className={styles.apiEndpoint}>{api.endpoint}</span>
|
||||||
<div className={styles.apiStats}>
|
<div className={styles.apiStats}>
|
||||||
<span className={styles.apiBadge}>
|
<span className={styles.apiBadge}>
|
||||||
{t('usage_stats.requests_count')}: {api.totalRequests}
|
<span className={styles.requestCountCell}>
|
||||||
|
<span>
|
||||||
|
{t('usage_stats.requests_count')}: {api.totalRequests.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
<span className={styles.requestBreakdown}>
|
||||||
|
(<span className={styles.statSuccess}>{api.successCount.toLocaleString()}</span>{' '}
|
||||||
|
<span className={styles.statFailure}>{api.failureCount.toLocaleString()}</span>)
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.apiBadge}>
|
<span className={styles.apiBadge}>
|
||||||
Tokens: {formatTokensInMillions(api.totalTokens)}
|
{t('usage_stats.tokens_count')}: {formatTokensInMillions(api.totalTokens)}
|
||||||
</span>
|
</span>
|
||||||
{hasPrices && api.totalCost > 0 && (
|
{hasPrices && api.totalCost > 0 && (
|
||||||
<span className={styles.apiBadge}>
|
<span className={styles.apiBadge}>
|
||||||
@@ -61,7 +69,13 @@ export function ApiDetailsCard({ apiStats, loading, hasPrices }: ApiDetailsCardP
|
|||||||
<div key={model} className={styles.modelRow}>
|
<div key={model} className={styles.modelRow}>
|
||||||
<span className={styles.modelName}>{model}</span>
|
<span className={styles.modelName}>{model}</span>
|
||||||
<span className={styles.modelStat}>
|
<span className={styles.modelStat}>
|
||||||
{stats.requests} {t('usage_stats.requests_count')}
|
<span className={styles.requestCountCell}>
|
||||||
|
<span>{stats.requests.toLocaleString()}</span>
|
||||||
|
<span className={styles.requestBreakdown}>
|
||||||
|
(<span className={styles.statSuccess}>{stats.successCount.toLocaleString()}</span>{' '}
|
||||||
|
<span className={styles.statFailure}>{stats.failureCount.toLocaleString()}</span>)
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.modelStat}>{formatTokensInMillions(stats.tokens)}</span>
|
<span className={styles.modelStat}>{formatTokensInMillions(stats.tokens)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,9 +54,11 @@ export interface UsageDetail {
|
|||||||
export interface ApiStats {
|
export interface ApiStats {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
totalRequests: number;
|
totalRequests: number;
|
||||||
|
successCount: number;
|
||||||
|
failureCount: number;
|
||||||
totalTokens: number;
|
totalTokens: number;
|
||||||
totalCost: number;
|
totalCost: number;
|
||||||
models: Record<string, { requests: number; tokens: number }>;
|
models: Record<string, { requests: number; successCount: number; failureCount: number; tokens: number }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOKENS_PER_PRICE_UNIT = 1_000_000;
|
const TOKENS_PER_PRICE_UNIT = 1_000_000;
|
||||||
@@ -542,28 +544,65 @@ export function getApiStats(usageData: any, modelPrices: Record<string, ModelPri
|
|||||||
const result: ApiStats[] = [];
|
const result: ApiStats[] = [];
|
||||||
|
|
||||||
Object.entries(apis as Record<string, any>).forEach(([endpoint, apiData]) => {
|
Object.entries(apis as Record<string, any>).forEach(([endpoint, apiData]) => {
|
||||||
const models: Record<string, { requests: number; tokens: number }> = {};
|
const models: Record<string, { requests: number; successCount: number; failureCount: number; tokens: number }> = {};
|
||||||
|
let derivedSuccessCount = 0;
|
||||||
|
let derivedFailureCount = 0;
|
||||||
let totalCost = 0;
|
let totalCost = 0;
|
||||||
|
|
||||||
const modelsData = apiData?.models || {};
|
const modelsData = apiData?.models || {};
|
||||||
Object.entries(modelsData as Record<string, any>).forEach(([modelName, modelData]) => {
|
Object.entries(modelsData as Record<string, any>).forEach(([modelName, modelData]) => {
|
||||||
models[modelName] = {
|
const details = Array.isArray(modelData.details) ? modelData.details : [];
|
||||||
requests: modelData.total_requests || 0,
|
const hasExplicitCounts =
|
||||||
tokens: modelData.total_tokens || 0
|
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];
|
const price = modelPrices[modelName];
|
||||||
if (price) {
|
if (details.length > 0 && (!hasExplicitCounts || price)) {
|
||||||
const details = Array.isArray(modelData.details) ? modelData.details : [];
|
|
||||||
details.forEach((detail: any) => {
|
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({
|
result.push({
|
||||||
endpoint: maskUsageSensitiveValue(endpoint) || endpoint,
|
endpoint: maskUsageSensitiveValue(endpoint) || endpoint,
|
||||||
totalRequests: apiData.total_requests || 0,
|
totalRequests: apiData.total_requests || 0,
|
||||||
|
successCount,
|
||||||
|
failureCount,
|
||||||
totalTokens: apiData.total_tokens || 0,
|
totalTokens: apiData.total_tokens || 0,
|
||||||
totalCost,
|
totalCost,
|
||||||
models
|
models
|
||||||
|
|||||||
Reference in New Issue
Block a user