fix(agents): surface OpenAI model capacity errors · openclaw/openclaw@f1ad5e2
vincentkoc
·
2026-04-24
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -64,6 +64,12 @@ describe("formatAssistantErrorText", () => {
|
64 | 64 | "The AI service is temporarily overloaded. Please try again in a moment.", |
65 | 65 | ); |
66 | 66 | }); |
| 67 | +it("returns a model-switch hint for OpenAI model capacity errors", () => { |
| 68 | +const msg = makeAssistantError("Selected model is at capacity. Please try a different model."); |
| 69 | +expect(formatAssistantErrorText(msg)).toBe( |
| 70 | +"⚠️ Selected model is at capacity. Try a different model, or wait and retry.", |
| 71 | +); |
| 72 | +}); |
67 | 73 | it("returns a recovery hint when tool call input is missing", () => { |
68 | 74 | const msg = makeAssistantError("tool_use.input: Field required"); |
69 | 75 | const result = formatAssistantErrorText(msg); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -141,6 +141,17 @@ describe("sanitizeUserFacingText", () => {
|
141 | 141 | ); |
142 | 142 | }); |
143 | 143 | |
| 144 | +it("returns a model-switch hint for OpenAI model capacity errors", () => { |
| 145 | +expect( |
| 146 | +sanitizeUserFacingText( |
| 147 | +"OpenAI error: Selected model is at capacity. Please try a different model.", |
| 148 | +{ |
| 149 | +errorContext: true, |
| 150 | +}, |
| 151 | +), |
| 152 | +).toBe("⚠️ Selected model is at capacity. Try a different model, or wait and retry."); |
| 153 | +}); |
| 154 | + |
144 | 155 | it("returns a transport-specific message for prefixed ECONNREFUSED errors", () => { |
145 | 156 | expect( |
146 | 157 | sanitizeUserFacingText("Error: connect ECONNREFUSED 127.0.0.1:443", { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
2 | 2 | import { |
3 | 3 | isAuthErrorMessage, |
4 | 4 | isBillingErrorMessage, |
| 5 | +isOverloadedErrorMessage, |
5 | 6 | isRateLimitErrorMessage, |
6 | 7 | } from "./failover-matches.js"; |
7 | 8 | |
@@ -68,6 +69,12 @@ describe("Z.ai vendor error codes (#48988)", () => {
|
68 | 69 | expect(isRateLimitErrorMessage("rate limit exceeded")).toBe(true); |
69 | 70 | }); |
70 | 71 | |
| 72 | +it("OpenAI model-capacity text is classified as overloaded", () => { |
| 73 | +expect( |
| 74 | +isOverloadedErrorMessage("Selected model is at capacity. Please try a different model."), |
| 75 | +).toBe(true); |
| 76 | +}); |
| 77 | + |
71 | 78 | it("billing still classified correctly", () => { |
72 | 79 | expect(isBillingErrorMessage("insufficient credits")).toBe(true); |
73 | 80 | }); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -73,6 +73,7 @@ const ERROR_PATTERNS = {
|
73 | 73 | overloaded: [ |
74 | 74 | /overloaded_error|"type"\s*:\s*"overloaded_error"/i, |
75 | 75 | "overloaded", |
| 76 | +/\b(?:selected\s+)?model\s+(?:is\s+)?at capacity\b/i, |
76 | 77 | // Match "service unavailable" only when combined with an explicit overload |
77 | 78 | // indicator — a generic 503 from a proxy/CDN should not be classified as |
78 | 79 | // provider-overload (#32828). |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -34,6 +34,8 @@ export function formatBillingErrorMessage(provider?: string, model?: string): st
|
34 | 34 | export const BILLING_ERROR_USER_MESSAGE = formatBillingErrorMessage(); |
35 | 35 | |
36 | 36 | const RATE_LIMIT_ERROR_USER_MESSAGE = "⚠️ API rate limit reached. Please try again later."; |
| 37 | +const MODEL_CAPACITY_ERROR_USER_MESSAGE = |
| 38 | +"⚠️ Selected model is at capacity. Try a different model, or wait and retry."; |
37 | 39 | const OVERLOADED_ERROR_USER_MESSAGE = |
38 | 40 | "The AI service is temporarily overloaded. Please try again in a moment."; |
39 | 41 | const FINAL_TAG_RE = /<\s*\/?\s*final\s*>/gi; |
@@ -60,6 +62,7 @@ const HTTP_ERROR_HINTS = [
|
60 | 62 | ]; |
61 | 63 | const RATE_LIMIT_SPECIFIC_HINT_RE = |
62 | 64 | /\bmin(ute)?s?\b|\bhours?\b|\bseconds?\b|\btry again in\b|\breset\b|\bplan\b|\bquota\b/i; |
| 65 | +const MODEL_CAPACITY_ERROR_RE = /\b(?:selected\s+)?model\s+(?:is\s+)?at capacity\b/i; |
63 | 66 | const NON_ERROR_PROVIDER_PAYLOAD_MAX_LENGTH = 16_384; |
64 | 67 | const NON_ERROR_PROVIDER_PAYLOAD_PREFIX_RE = /^codex\s*error(?:\s+\d{3})?[:\s-]+/i; |
65 | 68 | |
@@ -93,6 +96,9 @@ export function formatRateLimitOrOverloadedErrorCopy(raw: string): string | unde
|
93 | 96 | if (isRateLimitErrorMessage(raw)) { |
94 | 97 | return extractProviderRateLimitMessage(raw) ?? RATE_LIMIT_ERROR_USER_MESSAGE; |
95 | 98 | } |
| 99 | +if (MODEL_CAPACITY_ERROR_RE.test(raw)) { |
| 100 | +return MODEL_CAPACITY_ERROR_USER_MESSAGE; |
| 101 | +} |
96 | 102 | if (isOverloadedErrorMessage(raw)) { |
97 | 103 | return OVERLOADED_ERROR_USER_MESSAGE; |
98 | 104 | } |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -101,6 +101,20 @@ describe("buildEmbeddedRunPayloads", () => {
|
101 | 101 | expect(payloads.some((payload) => payload.text?.includes("request_id"))).toBe(false); |
102 | 102 | }); |
103 | 103 | |
| 104 | +it("surfaces OpenAI model capacity errors instead of generic empty-response copy", () => { |
| 105 | +const payloads = buildPayloads({ |
| 106 | +lastAssistant: makeAssistant({ |
| 107 | +errorMessage: "Selected model is at capacity. Please try a different model.", |
| 108 | +content: [], |
| 109 | +}), |
| 110 | +}); |
| 111 | + |
| 112 | +expectSinglePayloadSummary(payloads, { |
| 113 | +text: "⚠️ Selected model is at capacity. Try a different model, or wait and retry.", |
| 114 | +isError: true, |
| 115 | +}); |
| 116 | +}); |
| 117 | + |
104 | 118 | it("includes provider and model context for billing errors", () => { |
105 | 119 | const payloads = buildPayloads({ |
106 | 120 | lastAssistant: makeAssistant({ |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。