From 751cffae05b2513e05aec1ddb2a87f5bb9154e38 Mon Sep 17 00:00:00 2001 From: WuYongJie <549727955@qq.com> Date: Thu, 5 Nov 2026 13:23:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=A0=E5=9B=BE=E8=BE=93=E5=85=A5=E8=B6=85?= =?UTF-8?q?=E9=99=90=E8=BF=94=E5=9B=9E=E6=98=8E=E7=A1=AE=E7=9A=84=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E9=94=99=E8=AF=AF=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/canvas/[id]/capabilities/route.ts | 3 ++- tests/routes/rmbg-delivery.test.ts | 25 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/api/canvas/[id]/capabilities/route.ts b/app/api/canvas/[id]/capabilities/route.ts index f146da491..4d1ae30e5 100644 --- a/app/api/canvas/[id]/capabilities/route.ts +++ b/app/api/canvas/[id]/capabilities/route.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import { ZodError } from "zod"; import { auth } from "@/app/(auth)/auth"; +import { RmbgServiceError } from "@/lib/ai/rmbg/service"; import { canvasCapabilityClientCommandSchema } from "@/lib/canvas/capabilities/client-command"; import { CanvasCapabilityRuntimeError } from "@/lib/canvas/capabilities/context"; import { getCanvasCapabilityManifest } from "@/lib/canvas/capabilities/registry"; @@ -90,7 +91,7 @@ export async function POST( const failure = getRequestFailureDetails(error, "执行画布能力失败"); return NextResponse.json( { success: false, error: formatRequestFailure(failure, requestId), failure, requestId }, - { status: 500 } + { status: error instanceof RmbgServiceError ? error.status : 500 } ); } } diff --git a/tests/routes/rmbg-delivery.test.ts b/tests/routes/rmbg-delivery.test.ts index 18462e74b..2c1f44d09 100644 --- a/tests/routes/rmbg-delivery.test.ts +++ b/tests/routes/rmbg-delivery.test.ts @@ -133,6 +133,31 @@ test("AOS input rejections retain their cause instead of becoming a generic proc } }); +test("the canvas API returns input-limit errors as HTTP 400 and preserves the same advice when polling", async () => { + const service = rmbgLoader(async () => { throw new Error("Unexpected provider call"); }); + const error = new service.RmbgServiceError("抠图参考图分辨率超限(5056×3392 像素),当前 AOS 上游要求宽、高均不超过 4096 像素。", 400); + const route = serverLoader({ + "@/app/(auth)/auth": { auth: async () => ({ user: { id: "user-a" } }) }, + "@/lib/ai/rmbg/service": { RmbgServiceError: service.RmbgServiceError }, + "@/lib/canvas/capabilities/client-command": { canvasCapabilityClientCommandSchema: { parse: (v: unknown) => v } }, + "@/lib/canvas/capabilities/context": { CanvasCapabilityRuntimeError: class extends Error {} }, + "@/lib/canvas/agent-mutations": { CanvasAgentMutationError: class extends Error {} }, + "@/lib/canvas/capabilities/registry": { getCanvasCapabilityManifest: () => [] }, + "@/lib/canvas/capabilities/runtime": { executeCanvasCapabilityRuntime: async () => { throw error; } }, + "@/lib/canvas/capabilities/task-store": { getCanvasCapabilityTask: async () => ({ id: "task-a", status: "failed", error: error.message }) }, + })("app/api/canvas/[id]/capabilities/route.ts"); + const params = { params: Promise.resolve({ id: "project-a" }) }; + const response = await route.POST(new Request("https://example.com/api/canvas/project-a/capabilities", { method: "POST", body: JSON.stringify({ requestId: "request-a" }) }), params); + expect(response.status).toBe(400); + const submitted = await response.json(); + expect(submitted.failure).toMatchObject({ code: "RMBG_INPUT_RESOLUTION_EXCEEDED", outcome: "failed" }); + expect(submitted.error).toContain("5056×3392"); + expect(submitted.error).toContain("本次未提交上游抠图任务"); + const polled = await (await route.GET(new Request("https://example.com/api/canvas/project-a/capabilities?requestId=request-a"), params)).json(); + expect(polled.task.failure).toEqual(submitted.failure); + expect(polled.task.error).toContain(submitted.failure.recommendation); +}); + test("a create timeout preserves its cause and never creates another paid task", async () => { const image = await sharp({ create: { width: 16, height: 16, channels: 3, background: "white" } }).png().toBuffer(); let creates = 0;