From 535ea1aee4b8ec4d258a1c05718950c498140f96 Mon Sep 17 00:00:00 2001 From: WuYongJie <549727955@qq.com> Date: Thu, 5 Nov 2026 15:35:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83=E5=88=87=E6=8D=A2=E7=AD=89?= =?UTF-8?q?=E5=BE=85=E6=89=80=E6=9C=89=E6=AD=A3=E5=9C=A8=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E6=8A=A0=E5=9B=BE=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ai/rmbg/service.ts | 7 +++++++ tests/routes/rmbg-delivery.test.ts | 1 + tests/routes/runtime-active-work.test.ts | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/lib/ai/rmbg/service.ts b/lib/ai/rmbg/service.ts index 77dae6530..201f15569 100644 --- a/lib/ai/rmbg/service.ts +++ b/lib/ai/rmbg/service.ts @@ -8,6 +8,7 @@ import { executeConfiguredVip98kTask } from "@/lib/ai/vip98k-runtime"; import { db } from "@/lib/db"; import { generatedImage } from "@/lib/db/schema"; import { createLogger } from "@/lib/observability/logger"; +import { trackRuntimeWork } from "@/lib/observability/active-work"; import { uploadFile } from "@/lib/storage/service"; const logger = createLogger("rmbg-service"); @@ -654,6 +655,12 @@ export async function fetchImageBufferFromUrl(imageUrl: string): Promise export async function executeRmbgJob( params: ExecuteRmbgJobParams +): Promise { + return trackRuntimeWork(() => runRmbgJob(params)); +} + +async function runRmbgJob( + params: ExecuteRmbgJobParams ): Promise { const { requestId = crypto.randomUUID(), diff --git a/tests/routes/rmbg-delivery.test.ts b/tests/routes/rmbg-delivery.test.ts index d850364cf..e06025167 100644 --- a/tests/routes/rmbg-delivery.test.ts +++ b/tests/routes/rmbg-delivery.test.ts @@ -96,6 +96,7 @@ function rmbgLoader(fetch: typeof globalThis.fetch, timer?: typeof setTimeout) { "@/lib/db": { db: {} }, "@/lib/db/schema": { generatedImage: {} }, "@/lib/storage/service": { uploadFile: async (name: string) => ({ url: `${cdn}/${name}` }) }, + "@/lib/observability/active-work": { trackRuntimeWork: async (execute: () => unknown) => execute() }, fetch, setTimeout: timer || ((callback: () => void, ms: number) => setTimeout(callback, ms <= 2000 ? 0 : ms)), }, { NEXT_PUBLIC_CANVAS_CDN_ORIGIN: cdn })("lib/ai/rmbg/service.ts"); diff --git a/tests/routes/runtime-active-work.test.ts b/tests/routes/runtime-active-work.test.ts index beccc46a6..f6d4fe39c 100644 --- a/tests/routes/runtime-active-work.test.ts +++ b/tests/routes/runtime-active-work.test.ts @@ -28,3 +28,24 @@ test("durable archive handoff is separate from unfinished original generation", complete(); await original; expect(records.at(-1)).toMatchObject({ active: 0, durable: 0 }); }); + +test("direct matting stays visible to release draining until the provider attempt fails", async () => { + const counts: number[] = []; + let rejectProvider!: (error: Error) => void; + let providerStarted!: () => void; + const started = new Promise(resolve => { providerStarted = resolve; }); + const service = serverLoader({ + "node:fs": { writeFileSync: (_path: string, value: string) => counts.push(JSON.parse(value).active), unlinkSync() {} }, + "@/lib/db/queries": { getSystemSetting: async () => ({ value: { provider: "rmbg", apiUrl: "https://provider.example/remove", apiKey: "test" } }) }, + "@/lib/db": { db: {} }, "@/lib/db/schema": { generatedImage: {} }, + "@/lib/storage/service": {}, + fetch: async () => { providerStarted(); return new Promise((_resolve, reject) => { rejectProvider = reject; }); }, + })("lib/ai/rmbg/service.ts"); + const pending = service.executeRmbgJob({ userId: "user-a", imageBuffer: Buffer.from("image"), skipGallerySave: true }); + const assertion = expect(pending).rejects.toThrow(); + await started; + expect(counts).toEqual([1]); + rejectProvider(new Error("provider unavailable")); + await assertion; + expect(counts).toEqual([1, 0]); +});