fix: 收敛 Sub2API 账号评分输出
This commit is contained in:
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user