From f27d8dc5d297705d08e5af400e44e353b0639309 Mon Sep 17 00:00:00 2001 From: 134 Date: Wed, 21 Oct 2026 10:26:00 +0800 Subject: [PATCH] fix(agent): handle legacy image service failure messages --- lib/ai/runtime/agent-image-failover.ts | 6 ++++-- tests/routes/canvas-agent-runtime-image-failover.test.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ai/runtime/agent-image-failover.ts b/lib/ai/runtime/agent-image-failover.ts index 617ca4e93..791be7004 100644 --- a/lib/ai/runtime/agent-image-failover.ts +++ b/lib/ai/runtime/agent-image-failover.ts @@ -8,12 +8,14 @@ export function agentImageFailurePolicy(error: unknown): { retry: boolean; pause const unknown = value?.providerOutcomeUnknown || /request_outcome_unknown|may still be running|outcome.unknown|timed out|timeout|超时|fetch failed|econnreset|socket hang up/.test(message); if (unknown && !/task failed|task.*(?:failed|cancelled|canceled)|任务.*(?:失败|取消)/.test(message)) return { retry: false, pause: null }; if (/content.?policy|moderation|safety|违规|敏感|积分不足|本月.*配额|user cancel|user abort/.test(message)) return { retry: false, pause: null }; - const status = Number(value?.statusCode || value?.status) || Number(message.match(/(?:http[\s::]*|status(?:code)?[\s:=]*)([45]\d{2})/i)?.[1]) || null; + const status = Number(value?.statusCode || value?.status) || Number(message.match(/(?:http[\s::]*|status(?:code)?[\s:=]*|api request failed[\s::]*)([45]\d{2})/i)?.[1]) || null; + if (/unauthorized|invalid[ _]?(?:api[ _]?)?key|authentication/.test(message)) return { retry: true, pause: { reason: "authentication", status } }; + if (/forbidden|access denied/.test(message)) return { retry: true, pause: { reason: "access_denied", status } }; if (/quota|insufficient.*(?:balance|credit)|credits? insufficient|余额|额度|配额|rate.?limit|too many requests/.test(message)) return { retry: true, pause: { reason: "rate_or_quota", status } }; const fault = agentModelFault(status ? { message, status } : error); if (fault) return { retry: true, pause: fault }; - if (/task failed|image generation failed|生成失败|任务.*失败|未返回.*图片|no images? (?:in|url)|no valid data|response body is null|empty.result|empty.response|provider.error|上游.*错误|模型.*维护/.test(message)) + if (/task failed|image generation failed|生成失败|任务.*失败|未返回.*图片|no images? (?:in|url)|no valid data|response body is null|empty.result|empty.response|provider.error|上游.*错误|模型.*维护|internal.server.error|service.unavailable|bad.gateway|server.busy|系统繁忙/.test(message)) return { retry: true, pause: { reason: "image_generation_failed", status: Number(value?.statusCode || value?.status) || null } }; return { retry: false, pause: null }; } diff --git a/tests/routes/canvas-agent-runtime-image-failover.test.ts b/tests/routes/canvas-agent-runtime-image-failover.test.ts index de0585ed8..f1de27324 100644 --- a/tests/routes/canvas-agent-runtime-image-failover.test.ts +++ b/tests/routes/canvas-agent-runtime-image-failover.test.ts @@ -71,7 +71,7 @@ test("the existing non-Agent auto alias continues to select its configured defau test("provider adapters that include HTTP status or quota only in their message still recover", () => { - for (const message of ["API request failed: HTTP 503", "server busy(HTTP 429)", "insufficient balance", "模型配额不足", "No images in response", "BananaImage 生成失败"]) { + for (const message of ["API request failed: 503", "Internal server error", "Service Unavailable", "Unauthorized", "API request failed: HTTP 503", "server busy(HTTP 429)", "insufficient balance", "模型配额不足", "No images in response", "BananaImage 生成失败"]) { expect(agentImageFailurePolicy(new Error(message)).retry).toBe(true); } expect(agentImageFailurePolicy(new Error("invalid input (HTTP 400)")).retry).toBe(false);