fix(ai): keep plan per-minute limits transient
This commit is contained in:
@@ -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", () => {
|
||||
|
||||
Reference in New Issue
Block a user