Skip to content

Commit 86c1cf9

Browse files
committed
fix(agents): fallback on upstream provider errors
1 parent 3a93d7f commit 86c1cf9

3 files changed

Lines changed: 49 additions & 6 deletions

File tree

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_error",
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: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,49 @@ describe("classifyEmbeddedAgentRunResultForModelFallback", () => {
282282
expect(result).toBeNull();
283283
});
284284

285-
it("does not retry non-business transport error payloads", () => {
285+
it("classifies transient provider error payloads as fallback-worthy", () => {
286+
const errorText = JSON.stringify({
287+
error: {
288+
message: "Upstream request failed",
289+
type: "upstream_error",
290+
param: "",
291+
code: null,
292+
},
293+
});
294+
295+
const result = classifyEmbeddedAgentRunResultForModelFallback({
296+
provider: "custom",
297+
model: "llama-3.1",
298+
result: {
299+
payloads: [
300+
{
301+
isError: true,
302+
text: errorText,
303+
},
304+
],
305+
meta: {
306+
durationMs: 42,
307+
},
308+
},
309+
});
310+
311+
expect(result).toEqual({
312+
message: `custom/llama-3.1 ended with a provider error: ${errorText}`,
313+
reason: "timeout",
314+
code: "embedded_error_payload",
315+
rawError: errorText,
316+
});
317+
});
318+
319+
it("does not retry schema-rejection error payloads", () => {
286320
const result = classifyEmbeddedAgentRunResultForModelFallback({
287321
provider: "custom",
288322
model: "llama-3.1",
289323
result: {
290324
payloads: [
291325
{
292326
isError: true,
293-
text: "HTTP 500: internal server error",
327+
text: "LLM request failed: provider rejected the request schema or tool payload.",
294328
},
295329
],
296330
meta: {

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,16 @@ function classifyHarnessResult(params: {
146146
}
147147
}
148148

149-
/** Maps provider error payloads to fallback-safe business reasons. */
150-
function classifyBusinessDenialErrorPayloadReason(
149+
type EmbeddedErrorPayloadFailoverReason = Extract<
150+
FailoverReason,
151+
"auth" | "auth_permanent" | "billing" | "overloaded" | "rate_limit" | "server_error" | "timeout"
152+
>;
153+
154+
/** Maps provider error payloads to fallback-safe reasons. */
155+
function classifyProviderErrorPayloadReason(
151156
errorText: string,
152157
provider: string,
153-
): Extract<FailoverReason, "auth" | "auth_permanent" | "billing" | "rate_limit"> | null {
158+
): EmbeddedErrorPayloadFailoverReason | null {
154159
if (!errorText.trim()) {
155160
return null;
156161
}
@@ -159,7 +164,10 @@ function classifyBusinessDenialErrorPayloadReason(
159164
case "auth":
160165
case "auth_permanent":
161166
case "billing":
167+
case "overloaded":
162168
case "rate_limit":
169+
case "server_error":
170+
case "timeout":
163171
return failoverReason;
164172
default:
165173
return null;
@@ -252,7 +260,7 @@ export function classifyEmbeddedAgentRunResultForModelFallback(params: {
252260
.filter((payload) => payload?.isError === true)
253261
.map((payload) => (typeof payload.text === "string" ? payload.text : ""))
254262
.join("\n");
255-
const failoverReason = classifyBusinessDenialErrorPayloadReason(errorText, params.provider);
263+
const failoverReason = classifyProviderErrorPayloadReason(errorText, params.provider);
256264
if (failoverReason) {
257265
return {
258266
message: `${params.provider}/${params.model} ended with a provider error: ${errorText}`,

0 commit comments

Comments
 (0)