fix(ai): keep plan per-minute limits transient

This commit is contained in:
can1357
2026-08-07 13:24:24 +02:00
parent 68dece477f
commit f087a9050d
2 changed files with 14 additions and 2 deletions
+9 -2
View File
@@ -24,6 +24,13 @@ const INSUFFICIENT_BALANCE_PATTERN = /insufficient.?balance/i;
const SPEND_LIMIT_PATTERN = /spend.?limit/i;
const SUBSCRIPTION_CAP_PATTERN =
/\b(?:subscription|plan|membership)\b[^\n]{0,80}\b(?:rate.?limits?|quota|cap)\b|\b(?:rate.?limits?|quota|cap)\b[^\n]{0,80}\b(?:subscription|plan|membership)\b/i;
const TRANSIENT_INTERVAL_RATE_LIMIT_PATTERN = /\bper\s+(?:second|minute)\b/i;
function matchesSubscriptionCapText(errorMessage: string): boolean {
return (
SUBSCRIPTION_CAP_PATTERN.test(errorMessage) && !TRANSIENT_INTERVAL_RATE_LIMIT_PATTERN.test(errorMessage)
);
}
const OPENROUTER_DAILY_FREE_LIMIT_PATTERN = /\bfree[-_ ]models[-_ ]per[-_ ]day\b/i;
// gRPC/Connect end-streams carry the status as its name (`resource_exhausted`),
// while HTTP bodies use the phrase ("resource exhausted"). Strip either form
@@ -109,7 +116,7 @@ export function parseRateLimitReason(errorMessage: string): RateLimitReason {
return "QUOTA_EXHAUSTED";
}
if (SUBSCRIPTION_CAP_PATTERN.test(errorMessage)) {
if (matchesSubscriptionCapText(errorMessage)) {
return "QUOTA_EXHAUSTED";
}
@@ -273,7 +280,7 @@ export function matchesUsageLimitText(errorMessage: string): boolean {
(CN_QUOTA_EXHAUSTED_PATTERN.test(errorMessage) && !CN_TRANSIENT_CAP_PATTERN.test(errorMessage)) ||
SPEND_LIMIT_PATTERN.test(errorMessage) ||
ACCOUNT_RATE_LIMIT_PATTERN.test(errorMessage) ||
SUBSCRIPTION_CAP_PATTERN.test(errorMessage) ||
matchesSubscriptionCapText(errorMessage) ||
OPENROUTER_DAILY_FREE_LIMIT_PATTERN.test(errorMessage)
);
}
@@ -324,6 +324,11 @@ describe("isUsageLimitOutcome", () => {
expect(parseRateLimitReason(transient)).toBe("RATE_LIMIT_EXCEEDED");
expect(isUsageLimitOutcome(429, transient)).toBe(false);
expect(isUsageLimit(Object.assign(new Error(transient), { status: 429 }))).toBe(false);
const planPerMinuteLimit = "429 Your plan has a rate limit of 60 requests per minute";
expect(parseRateLimitReason(planPerMinuteLimit)).toBe("RATE_LIMIT_EXCEEDED");
expect(isUsageLimitOutcome(429, planPerMinuteLimit)).toBe(false);
expect(isUsageLimit(Object.assign(new Error(planPerMinuteLimit), { status: 429 }))).toBe(false);
});
it("still rotates on 429 with explicit account rate-limit framing", () => {