mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
fix(agents): preserve overload wording for rate-limited responses (#98165)
* [AI] fix(agents): classify HTTP 429 overloaded bodies as overloaded, not rate_limit Reorder overloaded check before rate_limit across all three classification paths: - classifyFailoverClassificationFromHttpStatus (HTTP status path) - classifyFailoverClassificationFromMessage (message text path) - formatRateLimitOrOverloadedErrorCopy (user-facing copy path) Previously, isRateLimitErrorMessage matched the bare '429' token in overloaded-worded 429 bodies (e.g. z.ai code 1305), misclassifying them as rate_limit and showing the wrong user-facing error message. 503/499 already check overloaded before defaulting; 429 now follows the same pattern. Related to #98101 * [AI] fix(agents): put MODEL_CAPACITY_ERROR_RE before overloaded in user-facing copy MODEL_CAPACITY_ERROR_RE is the most specific overloaded variant ('selected model is at capacity') and must be checked before the broader isOverloadedErrorMessage matcher (which also covers model at capacity), otherwise it's shadowed with a less specific message. Related to #98101 * fix(agents): preserve overload copy for rate-limited responses * fix(agents): retain provider retry guidance --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
f805bb4e02
commit
5f2f06b21b
3 changed files with 43 additions and 4 deletions
|
|
@ -71,6 +71,14 @@ describe("formatAssistantErrorText", () => {
|
|||
"The AI service is temporarily overloaded. Please try again in a moment.",
|
||||
);
|
||||
});
|
||||
it("preserves overload wording for Z.AI rate-limit errors", () => {
|
||||
const msg = makeAssistantError(
|
||||
'429 status code (exceeded limit)\n{"code":1305,"message":"The service may be temporarily overloaded, please try again later."}',
|
||||
);
|
||||
expect(formatAssistantErrorText(msg)).toBe(
|
||||
"The AI service is temporarily overloaded. Please try again in a moment.",
|
||||
);
|
||||
});
|
||||
it("rewrites generic provider internal errors without support request ids", () => {
|
||||
const msg = makeAssistantError(
|
||||
"An error occurred while processing your request. You can retry your request, or contact us through our help center at help.openai.com if the error persists. Please include the request ID synthetic-provider-request-001 in your message.",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Covers provider-specific failover matcher regressions.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { classifyFailoverReason } from "./errors.js";
|
||||
import { classifyFailoverReason, classifyFailoverReasonFromHttpStatus } from "./errors.js";
|
||||
import {
|
||||
isAuthErrorMessage,
|
||||
isBillingErrorMessage,
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
isServerErrorMessage,
|
||||
isTimeoutErrorMessage,
|
||||
} from "./failover-matches.js";
|
||||
import { formatRateLimitOrOverloadedErrorCopy } from "./sanitize-user-facing-text.js";
|
||||
|
||||
describe("Z.ai vendor error codes (#48988)", () => {
|
||||
describe("error 1311 — model not included in subscription plan", () => {
|
||||
|
|
@ -210,3 +211,24 @@ describe("generic assistant error text classification (#93931)", () => {
|
|||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("HTTP 429 overload wording (#98101)", () => {
|
||||
it("keeps Z.AI code 1305 in rate-limit backoff while preserving overload copy", () => {
|
||||
const message =
|
||||
"429 status code (exceeded limit)\n" +
|
||||
'{"code":1305,"message":"The service may be temporarily overloaded, please try again later."}';
|
||||
expect(classifyFailoverReason(message)).toBe("rate_limit");
|
||||
expect(classifyFailoverReasonFromHttpStatus(429, message)).toBe("rate_limit");
|
||||
expect(formatRateLimitOrOverloadedErrorCopy(message)).toBe(
|
||||
"The AI service is temporarily overloaded. Please try again in a moment.",
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves actionable retry details when a rate limit also mentions overload", () => {
|
||||
expect(
|
||||
formatRateLimitOrOverloadedErrorCopy(
|
||||
"429 rate limit: service overloaded, try again in 30 seconds",
|
||||
),
|
||||
).toBe("⚠️ rate limit: service overloaded, try again in 30 seconds");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -127,15 +127,24 @@ function extractProviderRateLimitMessage(raw: string): string | undefined {
|
|||
}
|
||||
|
||||
export function formatRateLimitOrOverloadedErrorCopy(raw: string): string | undefined {
|
||||
if (isRateLimitErrorMessage(raw)) {
|
||||
return extractProviderRateLimitMessage(raw) ?? RATE_LIMIT_ERROR_USER_MESSAGE;
|
||||
}
|
||||
if (MODEL_CAPACITY_ERROR_RE.test(raw)) {
|
||||
return MODEL_CAPACITY_ERROR_USER_MESSAGE;
|
||||
}
|
||||
const isRateLimit = isRateLimitErrorMessage(raw);
|
||||
if (isRateLimit) {
|
||||
const providerMessage = extractProviderRateLimitMessage(raw);
|
||||
if (providerMessage) {
|
||||
return providerMessage;
|
||||
}
|
||||
}
|
||||
// Retry classification still owns 429 backoff; user copy can preserve the provider's
|
||||
// overload wording when there is no more actionable retry/reset detail.
|
||||
if (isOverloadedErrorMessage(raw)) {
|
||||
return OVERLOADED_ERROR_USER_MESSAGE;
|
||||
}
|
||||
if (isRateLimit) {
|
||||
return RATE_LIMIT_ERROR_USER_MESSAGE;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue