fix: 收敛 Sub2API 账号评分输出

This commit is contained in:
Codex
2026-07-17 01:00:56 +02:00
parent 727656259f
commit 7f7695a268
4 changed files with 89 additions and 35 deletions
@@ -109,8 +109,8 @@ export function compactRuntimeErrors(value: unknown, options: RuntimeOptions): u
overview: nativeOps.overview,
responseHeaderTimeout: options.account === null || !options.json ? nativeOps.responseHeaderTimeout : undefined,
requestTiming: options.account === null || !options.json ? compactRequestTiming(nativeOps.requestTiming) : undefined,
customerErrors: compactCustomerErrors(nativeOps.customerErrors, options.account !== null || !options.json),
accountQuality: compactAccountQuality(nativeOps.accountQuality, options.account !== null || !options.json),
customerErrors: compactCustomerErrors(nativeOps.customerErrors, options.account !== null),
accountQuality: compactAccountQuality(nativeOps.accountQuality, options.account !== null),
accountAvailability: {
status: availability.status,
totalAccounts: availabilityGroup.total_accounts,
@@ -414,39 +414,54 @@ function renderRuntimeErrors(lines: string[], runtime: Record<string, unknown>,
]));
const qualityAccounts = runtimeRecords(accountQuality.accounts);
if (qualityAccounts.length > 0) {
lines.push("", "ACCOUNT QUALITY", renderTable([
["ACCOUNT", "ID", "PRIORITY", "SCORE", "GRADE", "QUALITY", "CONF", "ATTEMPTS", "SUCCESS", "FAIL", "UP_CUST", "EXCLUDED", "FAIL_RATE", "TTFT_P95_MS", "TTFT_N", "AVAILABLE", "REASONS"],
...qualityAccounts.map((item) => [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(item.priority), runtimeText(item.score), runtimeText(item.grade), runtimeText(item.assessment), runtimeText(item.confidence),
runtimeText(item.observedAttempts), runtimeText(item.successRequests), runtimeText(item.failureRequests), runtimeText(item.scoreableUpstreamErrorRequests),
runtimeText(item.excludedNonUpstreamErrorRequests), runtimeRate(item.failureRate),
runtimeText(item.ttftP95Ms), runtimeText(item.firstTokenSamples), runtimeText(item.currentlyAvailable),
runtimeShort(Array.isArray(item.reasons) ? item.reasons.join(",") : "", 58),
]),
]));
lines.push("ACCOUNT USAGE AND COST", renderTable([
["ACCOUNT", "ID", "REQUESTS", "TOKENS", "API_USD", "COST_RATE_CNY", "UPSTREAM_COST_CNY", "EXCLUDED_REASONS"],
...qualityAccounts.map((item) => {
const usage = runtimeRecord(item.usage) ?? {};
const excluded = runtimeRecords(item.excludedReasonBuckets).map((entry) => `${runtimeText(entry.reason)}:${runtimeText(entry.count)}`).join(",");
return [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(usage.requestCount), runtimeMillions(usage.tokenCount),
runtimeDecimal(usage.apiAmountUsd, 4), runtimeDecimal(usage.costRateCnyPerApiUsd, 3), runtimeDecimal(usage.upstreamCostCny, 4),
runtimeShort(excluded, 64),
];
}),
]));
const controlRows = qualityAccounts.filter((item) => [
item.failoverRequests, item.sameAccountRetryEvents, item.tempUnschedulableEvents, item.forwardFailedRequests, item.customerErrorRequests,
].some((value) => typeof value === "number" && value > 0));
if (controlRows.length > 0) lines.push("ACCOUNT FAILURE CONTROL", renderTable([
["ACCOUNT", "ID", "FAILOVER", "RECOVERED", "FAILED", "MISSING", "RETRY", "TEMP_UNSCHED", "FORWARD_FAIL", "CUSTOMER_ERR"],
...controlRows.map((item) => [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(item.failoverRequests), runtimeText(item.failoverRecovered),
runtimeText(item.failoverFailed), runtimeText(item.failoverOutcomeMissing), runtimeText(item.sameAccountRetryEvents),
runtimeText(item.tempUnschedulableEvents), runtimeText(item.forwardFailedRequests), runtimeText(item.customerErrorRequests),
]),
]));
if (options.account === null) {
lines.push("", "ACCOUNT SCORE AND USAGE", renderTable([
["ACCOUNT", "ID", "PRI", "SCORE", "GRADE", "CONF", "ATTEMPTS", "UP_FAIL", "FAIL_RATE", "TTFT_P95", "REQUESTS", "TOKENS", "API_USD", "RATE_CNY", "COST_CNY", "FAILOVER_OK", "AVAILABLE"],
...qualityAccounts.map((item) => {
const usage = runtimeRecord(item.usage) ?? {};
return [
runtimeShort(item.accountName, 36), runtimeText(item.accountId), runtimeText(item.priority), runtimeText(item.score), runtimeText(item.grade), runtimeText(item.confidence),
runtimeText(item.observedAttempts), runtimeText(item.scoreableUpstreamErrorRequests), runtimeRate(item.failureRate), runtimeText(item.ttftP95Ms),
runtimeText(usage.requestCount), runtimeMillions(usage.tokenCount), runtimeDecimal(usage.apiAmountUsd, 4), runtimeDecimal(usage.costRateCnyPerApiUsd, 3), runtimeDecimal(usage.upstreamCostCny, 4),
`${runtimeText(item.failoverRecovered)}/${runtimeText(item.failoverRequests)}`, runtimeText(item.currentlyAvailable),
];
}),
]));
} else {
lines.push("", "ACCOUNT QUALITY", renderTable([
["ACCOUNT", "ID", "PRIORITY", "SCORE", "GRADE", "QUALITY", "CONF", "ATTEMPTS", "SUCCESS", "FAIL", "UP_CUST", "EXCLUDED", "FAIL_RATE", "TTFT_P95_MS", "TTFT_N", "AVAILABLE", "REASONS"],
...qualityAccounts.map((item) => [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(item.priority), runtimeText(item.score), runtimeText(item.grade), runtimeText(item.assessment), runtimeText(item.confidence),
runtimeText(item.observedAttempts), runtimeText(item.successRequests), runtimeText(item.failureRequests), runtimeText(item.scoreableUpstreamErrorRequests),
runtimeText(item.excludedNonUpstreamErrorRequests), runtimeRate(item.failureRate),
runtimeText(item.ttftP95Ms), runtimeText(item.firstTokenSamples), runtimeText(item.currentlyAvailable),
runtimeShort(Array.isArray(item.reasons) ? item.reasons.join(",") : "", 58),
]),
]));
lines.push("ACCOUNT USAGE AND COST", renderTable([
["ACCOUNT", "ID", "REQUESTS", "TOKENS", "API_USD", "COST_RATE_CNY", "UPSTREAM_COST_CNY", "EXCLUDED_REASONS"],
...qualityAccounts.map((item) => {
const usage = runtimeRecord(item.usage) ?? {};
const excluded = runtimeRecords(item.excludedReasonBuckets).map((entry) => `${runtimeText(entry.reason)}:${runtimeText(entry.count)}`).join(",");
return [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(usage.requestCount), runtimeMillions(usage.tokenCount),
runtimeDecimal(usage.apiAmountUsd, 4), runtimeDecimal(usage.costRateCnyPerApiUsd, 3), runtimeDecimal(usage.upstreamCostCny, 4),
runtimeShort(excluded, 64),
];
}),
]));
const controlRows = qualityAccounts.filter((item) => [
item.failoverRequests, item.sameAccountRetryEvents, item.tempUnschedulableEvents, item.forwardFailedRequests, item.customerErrorRequests,
].some((value) => typeof value === "number" && value > 0));
if (controlRows.length > 0) lines.push("ACCOUNT FAILURE CONTROL", renderTable([
["ACCOUNT", "ID", "FAILOVER", "RECOVERED", "FAILED", "MISSING", "RETRY", "TEMP_UNSCHED", "FORWARD_FAIL", "CUSTOMER_ERR"],
...controlRows.map((item) => [
runtimeText(item.accountName), runtimeText(item.accountId), runtimeText(item.failoverRequests), runtimeText(item.failoverRecovered),
runtimeText(item.failoverFailed), runtimeText(item.failoverOutcomeMissing), runtimeText(item.sameAccountRetryEvents),
runtimeText(item.tempUnschedulableEvents), runtimeText(item.forwardFailedRequests), runtimeText(item.customerErrorRequests),
]),
]));
}
const scoreRule = runtimeRecord(accountQuality.scoreRule) ?? {};
lines.push(`SCORE RULE reliability=${runtimeText(scoreRule.reliabilityWeight)} latency=${runtimeText(scoreRule.latencyWeight)} availability=${runtimeText(scoreRule.availabilityWeight)} confidence=separate priority=lower-is-higher highest=0`);
if (typeof accountQuality.attribution === "string") lines.push(`NOTE: ${accountQuality.attribution}`);