Skip to content

Commit ce09304

Browse files
bowenluo718claude
andcommitted
fix(agents): expand embedded fallback classifier to accept transient provider errors
- Rename classifyBusinessDenialErrorPayloadReason → classifyFailoverErrorPayloadReason - Add rate_limit, overloaded, server_error, timeout to fallback-eligible reasons - Add "upstream request failed" to serverError patterns in failover-matches - Add "type":"upstream_error" to isStructuredServerErrorMessage - Update tests: transient error payloads now trigger fallback instead of returning null Fixes #95519 Co-Authored-By: Claude <[email protected]>
1 parent 9242137 commit ce09304

4 files changed

Lines changed: 77 additions & 14 deletions

File tree

src/agents/embedded-agent-helpers/errors.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1658,7 +1658,11 @@ function isStructuredServerErrorMessage(raw: string): boolean {
16581658
return false;
16591659
}
16601660
const value = normalizeLowercaseStringOrEmpty(raw);
1661-
return value.includes('"type":"server_error"') || value.includes('"code":"server_error"');
1661+
return (
1662+
value.includes('"type":"server_error"') ||
1663+
value.includes('"code":"server_error"') ||
1664+
value.includes('"type":"upstream_error"')
1665+
);
16621666
}
16631667

16641668
export function parseImageDimensionError(raw: string): {

src/agents/embedded-agent-helpers/failover-matches.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ const ERROR_PATTERNS = {
119119
"bad gateway",
120120
"gateway timeout",
121121
"upstream error",
122+
"upstream request failed",
122123
"upstream connect error",
123124
"connection reset",
124125
// Chinese provider server error messages

src/agents/embedded-agent-runner/result-fallback-classifier.test.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe("classifyEmbeddedAgentRunResultForModelFallback", () => {
137137
expect(result).toBeNull();
138138
});
139139

140-
it("does not retry non-business transport error payloads", () => {
140+
it("classifies transient server-error payloads as fallback-worthy", () => {
141141
const result = classifyEmbeddedAgentRunResultForModelFallback({
142142
provider: "custom",
143143
model: "llama-3.1",
@@ -154,7 +154,58 @@ describe("classifyEmbeddedAgentRunResultForModelFallback", () => {
154154
},
155155
});
156156

157-
expect(result).toBeNull();
157+
expect(result).toEqual({
158+
message: "custom/llama-3.1 ended with a provider error: HTTP 500: internal server error",
159+
reason: "timeout",
160+
code: "embedded_error_payload",
161+
rawError: "HTTP 500: internal server error",
162+
});
163+
});
164+
165+
it("classifies upstream request failed as fallback-worthy (#95519)", () => {
166+
const result = classifyEmbeddedAgentRunResultForModelFallback({
167+
provider: "custom",
168+
model: "llama-3.1",
169+
result: {
170+
payloads: [
171+
{
172+
isError: true,
173+
text: "Upstream request failed",
174+
},
175+
],
176+
meta: {
177+
durationMs: 42,
178+
},
179+
},
180+
});
181+
182+
expect(result).toEqual({
183+
message: "custom/llama-3.1 ended with a provider error: Upstream request failed",
184+
reason: "timeout",
185+
code: "embedded_error_payload",
186+
rawError: "Upstream request failed",
187+
});
188+
});
189+
190+
it("classifies upstream_error JSON payload as fallback-worthy (#95519)", () => {
191+
const result = classifyEmbeddedAgentRunResultForModelFallback({
192+
provider: "custom",
193+
model: "llama-3.1",
194+
result: {
195+
payloads: [
196+
{
197+
isError: true,
198+
text: '{"error":{"message":"Upstream request failed","type":"upstream_error"}}',
199+
},
200+
],
201+
meta: {
202+
durationMs: 42,
203+
},
204+
},
205+
});
206+
207+
expect(result).not.toBeNull();
208+
expect(result?.reason).toBe("server_error");
158209
});
159210

160211
it("keeps tool-authored incomplete summaries fallback-eligible", () => {

src/agents/embedded-agent-runner/result-fallback-classifier.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,30 @@ function classifyHarnessResult(params: {
103103
}
104104
}
105105

106-
/** Maps provider error payloads to fallback-safe business reasons. */
107-
function classifyBusinessDenialErrorPayloadReason(
106+
/** Failover reasons that should trigger model fallback when detected in provider error payloads. */
107+
const FALLBACKABLE_PROVIDER_ERROR_REASONS = new Set<FailoverReason>([
108+
"auth",
109+
"auth_permanent",
110+
"billing",
111+
"rate_limit",
112+
"overloaded",
113+
"server_error",
114+
"timeout",
115+
]);
116+
117+
/** Maps provider error payloads to fallback-eligible reasons. */
118+
function classifyFailoverErrorPayloadReason(
108119
errorText: string,
109120
provider: string,
110-
): Extract<FailoverReason, "auth" | "auth_permanent" | "billing"> | null {
121+
): FailoverReason | null {
111122
if (!errorText.trim()) {
112123
return null;
113124
}
114125
const failoverReason = classifyFailoverReason(errorText, { provider });
115-
switch (failoverReason) {
116-
case "auth":
117-
case "auth_permanent":
118-
case "billing":
119-
return failoverReason;
120-
default:
121-
return null;
126+
if (failoverReason && FALLBACKABLE_PROVIDER_ERROR_REASONS.has(failoverReason)) {
127+
return failoverReason;
122128
}
129+
return null;
123130
}
124131

125132
/** Returns a fallback classification when an embedded run failed without user-visible output. */
@@ -189,7 +196,7 @@ export function classifyEmbeddedAgentRunResultForModelFallback(params: {
189196
.filter((payload) => payload?.isError === true)
190197
.map((payload) => (typeof payload.text === "string" ? payload.text : ""))
191198
.join("\n");
192-
const failoverReason = classifyBusinessDenialErrorPayloadReason(errorText, params.provider);
199+
const failoverReason = classifyFailoverErrorPayloadReason(errorText, params.provider);
193200
if (failoverReason) {
194201
return {
195202
message: `${params.provider}/${params.model} ended with a provider error: ${errorText}`,

0 commit comments

Comments
 (0)