test(ai-review): 補使用量統計 NaN 安全測試並排除微優化誤報 #47
+22
-14
@@ -211,31 +211,39 @@ function round1(n) {
|
|||||||
|
|
||||||
const RATE_KIND_LABEL = { tokens: 'token', requests: '次數' };
|
const RATE_KIND_LABEL = { tokens: 'token', requests: '次數' };
|
||||||
|
|
||||||
/** 是否為有限正數(排除 0、負數、NaN、Infinity),避免算出 Infinity%/NaN%/負百分比。 */
|
/**
|
||||||
function isFinitePositive(n) {
|
* 計算「剩餘百分比」= remaining / limit × 100。
|
||||||
return Number.isFinite(Number(n)) && Number(n) > 0;
|
* limit 或 remaining 為 null/undefined/NaN/Infinity,或 limit ≤ 0 時回 null,
|
||||||
|
* 避免算出 Infinity%/NaN%/負百分比或除以零。
|
||||||
|
*/
|
||||||
|
function calculatePercent(remaining, limit) {
|
||||||
|
if (remaining == null || limit == null) return null;
|
||||||
|
const rem = Number(remaining);
|
||||||
|
const lim = Number(limit);
|
||||||
|
if (!Number.isFinite(rem) || !Number.isFinite(lim) || lim <= 0) return null;
|
||||||
|
return round1((rem / lim) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 計算「剩餘可用百分比」,依優先序擇一:
|
* 計算「剩餘可用百分比」,依優先序擇一:
|
||||||
* 1. 帳號額度(quota 有有限正數上限)→ 剩餘 credits / 上限;
|
* 1. 帳號額度(quota 有有效上限)→ 剩餘 credits / 上限;
|
||||||
* 2. 速率配額(rate limit header,有限正數上限)→ 當前視窗剩餘 / 上限;
|
* 2. 速率配額(rate limit header,有有效上限)→ 當前視窗剩餘 / 上限;
|
||||||
* 上限或剩餘為 0/負數/NaN/Infinity 等無效值時不計算,落到 { percent: null, reason }。
|
* 上限或剩餘為無效值(null/0/負數/NaN/Infinity)時跳過計算,落到 { percent: null, reason }。
|
||||||
*/
|
*/
|
||||||
export function resolveRemainingPercent(quota, rate) {
|
export function resolveRemainingPercent(quota, rate) {
|
||||||
if (quota?.available && quota.limit != null && isFinitePositive(quota.limit)) {
|
if (quota?.available && quota.limit != null) {
|
||||||
const limit = Number(quota.limit);
|
const limit = Number(quota.limit);
|
||||||
const remaining = quota.remaining == null ? limit - num(quota.used) : Number(quota.remaining);
|
const remaining = quota.remaining == null ? limit - num(quota.used) : Number(quota.remaining);
|
||||||
if (Number.isFinite(remaining)) {
|
const percent = calculatePercent(remaining, limit);
|
||||||
return { percent: round1((remaining / limit) * 100), basis: '帳號額度', remaining, limit, unit: quota.currency || '' };
|
if (percent != null) {
|
||||||
|
return { percent, basis: '帳號額度', remaining, limit, unit: quota.currency || '' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rate?.hasData && isFinitePositive(rate.limit)) {
|
if (rate?.hasData) {
|
||||||
const limit = Number(rate.limit);
|
const percent = calculatePercent(rate.remaining, rate.limit);
|
||||||
const remaining = Number(rate.remaining);
|
if (percent != null) {
|
||||||
if (Number.isFinite(remaining)) {
|
|
||||||
const kindLabel = RATE_KIND_LABEL[rate.kind] || rate.kind;
|
const kindLabel = RATE_KIND_LABEL[rate.kind] || rate.kind;
|
||||||
return { percent: round1((remaining / limit) * 100), basis: `速率配額(當前視窗,${kindLabel})`, remaining, limit, unit: '' };
|
return { percent, basis: `速率配額(當前視窗,${kindLabel})`, remaining: Number(rate.remaining), limit: Number(rate.limit), unit: '' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let reason;
|
let reason;
|
||||||
|
|||||||
Reference in New Issue
Block a user