fix: route canvas capabilities through local runtime
This commit is contained in:
parent
452effecbc
commit
1fd9be49c7
@ -10,6 +10,7 @@ import {
|
||||
} from "@/lib/ai/rmbg/service";
|
||||
import type { CanvasCapabilityClientCommand } from "@/lib/canvas/capabilities/client-command";
|
||||
import type { CanvasCapabilityTarget } from "@/lib/canvas/capabilities/context";
|
||||
import { resolveCanvasRuntimeEndpoint } from "@/lib/canvas/capabilities/internal-endpoint";
|
||||
import { generateCanvasImage } from "@/lib/canvas/generate-image";
|
||||
import { requestLongTaskResult } from "@/lib/client/long-task";
|
||||
import {
|
||||
@ -55,7 +56,7 @@ async function postRuntimeEndpoint(params: {
|
||||
requestUrl: string;
|
||||
timeoutMs?: number;
|
||||
}) {
|
||||
const response = await fetch(new URL(params.path, params.requestUrl), {
|
||||
const response = await fetch(resolveCanvasRuntimeEndpoint(params.path, params.requestUrl), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@ -80,7 +81,7 @@ async function postRuntimeFormData(params: {
|
||||
requestUrl: string;
|
||||
timeoutMs?: number;
|
||||
}) {
|
||||
const response = await fetch(new URL(params.path, params.requestUrl), {
|
||||
const response = await fetch(resolveCanvasRuntimeEndpoint(params.path, params.requestUrl), {
|
||||
method: "POST",
|
||||
headers: getCookieHeaders(params.requestHeaders),
|
||||
body: params.formData,
|
||||
@ -233,7 +234,7 @@ async function executeForTarget(
|
||||
|
||||
if (command.capabilityId === "image.enhance") {
|
||||
const result = await requestLongTaskResult({
|
||||
endpoint: new URL("/api/enhance", params.requestUrl).toString(),
|
||||
endpoint: resolveCanvasRuntimeEndpoint("/api/enhance", params.requestUrl),
|
||||
payload: {
|
||||
imageUrl: target.sourceImageUrl,
|
||||
prompt: prompt || "图像增强处理",
|
||||
@ -258,7 +259,10 @@ async function executeForTarget(
|
||||
|
||||
if (command.capabilityId === "image.remove_watermark") {
|
||||
const result = await requestLongTaskResult({
|
||||
endpoint: new URL("/api/watermark-remove", params.requestUrl).toString(),
|
||||
endpoint: resolveCanvasRuntimeEndpoint(
|
||||
"/api/watermark-remove",
|
||||
params.requestUrl
|
||||
),
|
||||
payload: { image_url: target.sourceImageUrl },
|
||||
timeoutMs: 20 * 60 * 1000,
|
||||
headers: getCookieHeaders(params.requestHeaders),
|
||||
|
||||
32
lib/canvas/capabilities/internal-endpoint.ts
Normal file
32
lib/canvas/capabilities/internal-endpoint.ts
Normal file
@ -0,0 +1,32 @@
|
||||
type CanvasRuntimeEndpointOptions = {
|
||||
internalOrigin?: string;
|
||||
port?: string;
|
||||
};
|
||||
|
||||
function normalizeInternalOrigin(value: string) {
|
||||
const url = new URL(value);
|
||||
url.pathname = "/";
|
||||
url.search = "";
|
||||
url.hash = "";
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export function resolveCanvasRuntimeEndpoint(
|
||||
path: string,
|
||||
requestUrl: string,
|
||||
options: CanvasRuntimeEndpointOptions = {}
|
||||
) {
|
||||
const configuredOrigin = String(
|
||||
options.internalOrigin ?? process.env.CANVAS_RUNTIME_INTERNAL_ORIGIN ?? ""
|
||||
).trim();
|
||||
if (configuredOrigin) {
|
||||
return new URL(path, normalizeInternalOrigin(configuredOrigin)).toString();
|
||||
}
|
||||
|
||||
const port = String(options.port ?? process.env.PORT ?? "").trim();
|
||||
if (/^\d{1,5}$/.test(port) && Number(port) > 0 && Number(port) <= 65_535) {
|
||||
return new URL(path, `http://127.0.0.1:${port}`).toString();
|
||||
}
|
||||
|
||||
return new URL(path, requestUrl).toString();
|
||||
}
|
||||
@ -20,7 +20,11 @@ export async function enqueueCanvasCapabilityExecution<T>(params: {
|
||||
const state = getQueueState();
|
||||
const previous = state.tails.get(params.projectId) || Promise.resolve();
|
||||
const execution = previous.catch(() => undefined).then(params.execute);
|
||||
const tail = execution.finally(() => {
|
||||
const settledExecution = execution.then(
|
||||
() => undefined,
|
||||
() => undefined
|
||||
);
|
||||
const tail = settledExecution.finally(() => {
|
||||
if (state.tails.get(params.projectId) === tail) {
|
||||
state.tails.delete(params.projectId);
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ export default defineConfig({
|
||||
/routes\/canvas-entry-performance\.test\.ts/,
|
||||
/routes\/settings-visual-consistency\.test\.ts/,
|
||||
/routes\/canvas-scale-adjust-intent\.test\.ts/,
|
||||
/routes\/canvas-runtime-internal-endpoint\.test\.ts/,
|
||||
/routes\/model-classification\.test\.ts/,
|
||||
],
|
||||
fullyParallel: true,
|
||||
|
||||
55
tests/routes/canvas-runtime-internal-endpoint.test.ts
Normal file
55
tests/routes/canvas-runtime-internal-endpoint.test.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { resolveCanvasRuntimeEndpoint } from "@/lib/canvas/capabilities/internal-endpoint";
|
||||
import {
|
||||
enqueueCanvasCapabilityExecution,
|
||||
getCanvasCapabilityQueueDepth,
|
||||
} from "@/lib/canvas/capabilities/task-queue";
|
||||
|
||||
test.describe("canvas runtime internal endpoints", () => {
|
||||
test("uses the local HTTP listener for server-internal capability calls", () => {
|
||||
expect(
|
||||
resolveCanvasRuntimeEndpoint(
|
||||
"/api/enhance",
|
||||
"https://ai.yydd.ai/api/canvas/canvas-1/capabilities",
|
||||
{ port: "3125" }
|
||||
)
|
||||
).toBe("http://127.0.0.1:3125/api/enhance");
|
||||
expect(
|
||||
resolveCanvasRuntimeEndpoint(
|
||||
"/api/enhance",
|
||||
"https://ai.yydd.ai/api/canvas/canvas-1/capabilities",
|
||||
{ internalOrigin: "http://canvas-runtime:3000/base" }
|
||||
)
|
||||
).toBe("http://canvas-runtime:3000/api/enhance");
|
||||
});
|
||||
|
||||
test("falls back to the incoming origin without an internal listener", () => {
|
||||
expect(
|
||||
resolveCanvasRuntimeEndpoint(
|
||||
"/api/enhance",
|
||||
"https://ai.yydd.ai/api/canvas/canvas-1/capabilities",
|
||||
{ port: "invalid" }
|
||||
)
|
||||
).toBe("https://ai.yydd.ai/api/enhance");
|
||||
});
|
||||
|
||||
test("continues the project queue after a failed capability execution", async () => {
|
||||
await expect(
|
||||
enqueueCanvasCapabilityExecution({
|
||||
projectId: "failed-capability-project",
|
||||
execute: async () => {
|
||||
throw new Error("expected capability failure");
|
||||
},
|
||||
})
|
||||
).rejects.toThrow("expected capability failure");
|
||||
|
||||
await expect(
|
||||
enqueueCanvasCapabilityExecution({
|
||||
projectId: "failed-capability-project",
|
||||
execute: async () => "recovered",
|
||||
})
|
||||
).resolves.toBe("recovered");
|
||||
expect(getCanvasCapabilityQueueDepth("failed-capability-project")).toBe(0);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user