Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ function buildPayload(): ChatUserQuotaPayload {
quota_type: QUOTA_TYPE.PERIODIC,
period_type: quotaSettingsForm.periodType,
period_value: quotaSettingsForm.periodValue,
token_limit: quotaSettingsForm.tokenLimit,
// 前端单位是 K,传给后端时为原始 token 数
token_limit: quotaSettingsForm.tokenLimit * 1000,
}
}

Expand Down Expand Up @@ -85,7 +86,7 @@ async function loadQuota(userId: string) {
quotaType: quota.quota_type,
periodType: quota.period_type ?? PERIOD_TYPE.MONTH,
periodValue: quota.period_value ?? 1,
tokenLimit: quota.token_limit ?? 10_000_000,
tokenLimit: quota.token_limit == null ? 10_000_000 : Math.round(quota.token_limit / 1000),
})
} catch {
// 请求层已提示错误;保留默认表单,用户可自行设置后再保存

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main issue: the default value was not converted to the new frontend unit.

Issues

  1. Critical: default 10_000_000 is now interpreted as K

    • loadQuota() sets tokenLimit: 10_000_000 when quota.token_limit is null.
    • Since buildPayload() multiplies by 1000, saving that default sends 10_000_000_000 tokens, not 10_000_000.
    • If the intended default is 10_000_000 raw tokens, the form default should be 10_000.
  2. Build-side conversion is not validated or rounded

    • quotaSettingsForm.tokenLimit * 1000 can produce:
      • fractional values if the form allows decimals,
      • NaN if the input is invalid,
      • Infinity for very large values.
    • Backend likely expects an integer token count. Use Math.round(...) and validate.
  3. Precision loss on load/save round trip

    • Math.round(quota.token_limit / 1000) loses precision.
    • Example: backend 10_000_500 becomes 10_001 K, then saves as 10_001_000.
    • If exact values matter, either keep fractional K in the form or preserve the original raw value.
  4. Unit conversion is duplicated and uses magic numbers

    • The 1000 conversion appears in two places.
    • This makes it easy to miss one conversion, as happened with the default.
    • Prefer a shared constant/helper and clearer naming such as tokenLimitK.
  5. Possible string-to-number coercion

    • If the form field is a string, tokenLimit * 1000 silently coerces it.
    • Empty string becomes 0; invalid input becomes NaN.
    • Explicitly parse and validate.

Suggested changes

const TOKENS_PER_K = 1000;
const DEFAULT_TOKEN_LIMIT_TOKENS = 10_000_000;
const DEFAULT_TOKEN_LIMIT_K = DEFAULT_TOKEN_LIMIT_TOKENS / TOKENS_PER_K; // 10_000

function kToTokens(k: number): number {
  return Math.round(k * TOKENS_PER_K);
}

function tokensToK(tokens: number): number {
  return Math.round(tokens / TOKENS_PER_K);
}
function buildPayload(): ChatUserQuotaPayload {
  const tokenLimitK = Number(quotaSettingsForm.tokenLimit);

  if (!Number.isFinite(tokenLimitK) || tokenLimitK < 0) {
    throw new Error("Invalid token limit");
  }

  const tokenLimit = kToTokens(tokenLimitK);

  if (!Number.isSafeInteger(tokenLimit)) {
    throw new Error("Token limit is too large or not an integer");
  }

  return {
    quota_type: QUOTA_TYPE.PERIODIC,
    period_type: quotaSettingsForm.periodType,
    period_value: quotaSettingsForm.periodValue,
    token_limit: tokenLimit,
  };
}
tokenLimit:
  quota.token_limit == null
    ? DEFAULT_TOKEN_LIMIT_K
    : tokensToK(quota.token_limit),

If exact backend values must be preserved, avoid Math.round on load and allow decimal K, e.g. quota.token_limit / TOKENS_PER_K, or store the original raw value separately.

Expand Down
Loading