Compare commits
No commits in common. "main" and "codex/native-image-expansion-20261112" have entirely different histories.
main
...
codex/nati
@ -12,8 +12,6 @@ import { expandWithVip98kOutpaint, VIP98K_OUTPAINT_MODEL } from "@/lib/ai/extend
|
|||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
import { generatedImage } from "@/lib/db/schema";
|
import { generatedImage } from "@/lib/db/schema";
|
||||||
import { uploadFile } from "@/lib/storage/service";
|
import { uploadFile } from "@/lib/storage/service";
|
||||||
import { archiveTemporaryImage } from "@/lib/storage/image-archive";
|
|
||||||
import { randomUUID } from "node:crypto";
|
|
||||||
|
|
||||||
type ExtendConfig = {
|
type ExtendConfig = {
|
||||||
provider?: ExtendProvider;
|
provider?: ExtendProvider;
|
||||||
@ -436,10 +434,7 @@ export async function executeExtendJob(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
let buffer!: Buffer;
|
let buffer: Buffer;
|
||||||
let uploadResult: { thumbnail: string; original: string } | undefined;
|
|
||||||
let archivedBytes: number | undefined;
|
|
||||||
let downloadAsset: Awaited<ReturnType<typeof archiveTemporaryImage>>["downloadAsset"];
|
|
||||||
let modelId: string;
|
let modelId: string;
|
||||||
if (config.provider === "fal") {
|
if (config.provider === "fal") {
|
||||||
buffer = await expandWithFalOutpaint({ imageUrl: inputImageUrl, apiKey: config.apiKey!, xScale, yScale });
|
buffer = await expandWithFalOutpaint({ imageUrl: inputImageUrl, apiKey: config.apiKey!, xScale, yScale });
|
||||||
@ -455,28 +450,21 @@ export async function executeExtendJob(
|
|||||||
} else {
|
} else {
|
||||||
const prepared = config.provider === "vip98k" ? inputImageUrl : await normalizeDashscopeExpansionInput(inputImageUrl);
|
const prepared = config.provider === "vip98k" ? inputImageUrl : await normalizeDashscopeExpansionInput(inputImageUrl);
|
||||||
const url = await extendImage(prepared, config, xScale, yScale, requestId);
|
const url = await extendImage(prepared, config, xScale, yScale, requestId);
|
||||||
if (config.provider === "vip98k" && !includeBase64) {
|
const imageResponse = await fetch(url);
|
||||||
const archived = await archiveTemporaryImage(url, requestId || randomUUID());
|
if (!imageResponse.ok) {
|
||||||
uploadResult = { original: archived.url, thumbnail: archived.thumbnailUrl };
|
throw new ExtendServiceError("下载拓展图片失败", 500, `${imageResponse.status} ${imageResponse.statusText}`);
|
||||||
archivedBytes = archived.bytes;
|
|
||||||
downloadAsset = archived.downloadAsset;
|
|
||||||
} else {
|
|
||||||
const imageResponse = await fetch(url);
|
|
||||||
if (!imageResponse.ok) {
|
|
||||||
throw new ExtendServiceError("下载拓展图片失败", 500, `${imageResponse.status} ${imageResponse.statusText}`);
|
|
||||||
}
|
|
||||||
buffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
||||||
}
|
}
|
||||||
|
buffer = Buffer.from(await imageResponse.arrayBuffer());
|
||||||
modelId = config.provider === "vip98k" ? "image-expansion" : "extend-1.0";
|
modelId = config.provider === "vip98k" ? "image-expansion" : "extend-1.0";
|
||||||
}
|
}
|
||||||
const imageBase64Result = includeBase64 ? buffer.toString("base64") : undefined;
|
const imageBase64Result = includeBase64 ? buffer.toString("base64") : undefined;
|
||||||
uploadResult ??= await uploadBufferPairToS3(buffer, config.provider === "fal" || config.provider === "vip98k" ? "png" : "jpg");
|
const uploadResult = await uploadBufferPairToS3(buffer, config.provider === "fal" || config.provider === "vip98k" ? "png" : "jpg");
|
||||||
console.log("[Extend Service] Result uploaded:", {
|
console.log("[Extend Service] Result uploaded:", {
|
||||||
requestId,
|
requestId,
|
||||||
includeBase64,
|
includeBase64,
|
||||||
inputImageUrl: inputImageUrl.slice(0, 120),
|
inputImageUrl: inputImageUrl.slice(0, 120),
|
||||||
modelId,
|
modelId,
|
||||||
outputBytes: archivedBytes ?? buffer.length,
|
outputBytes: buffer.length,
|
||||||
});
|
});
|
||||||
let mediaAssetId: string | null = null;
|
let mediaAssetId: string | null = null;
|
||||||
let generatedImageId: string | null = null;
|
let generatedImageId: string | null = null;
|
||||||
@ -525,7 +513,6 @@ export async function executeExtendJob(
|
|||||||
xScale,
|
xScale,
|
||||||
yScale,
|
yScale,
|
||||||
imageSize: "2K",
|
imageSize: "2K",
|
||||||
...(downloadAsset ? { downloadAsset } : {}),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
mediaAssetId = media?.id || null;
|
mediaAssetId = media?.id || null;
|
||||||
|
|||||||
@ -13,7 +13,6 @@ test("native expansion sends the original image and never switches models after
|
|||||||
"@/lib/db": {},
|
"@/lib/db": {},
|
||||||
"@/lib/db/schema": {},
|
"@/lib/db/schema": {},
|
||||||
"@/lib/storage/service": {},
|
"@/lib/storage/service": {},
|
||||||
"@/lib/storage/image-archive": {},
|
|
||||||
"@/lib/ai/vip98k-runtime": {
|
"@/lib/ai/vip98k-runtime": {
|
||||||
executeConfiguredVip98kTask: async (params: unknown) => { calls.push(params); throw new Error(message); },
|
executeConfiguredVip98kTask: async (params: unknown) => { calls.push(params); throw new Error(message); },
|
||||||
},
|
},
|
||||||
@ -23,41 +22,6 @@ test("native expansion sends the original image and never switches models after
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("native expansion archives the completed image without transferring its bytes through the app", async () => {
|
|
||||||
const calls: string[] = [];
|
|
||||||
const service = serverLoader({
|
|
||||||
"@/lib/db/queries": { getSystemSetting: async () => ({ value: { provider: "vip98k", modelId: "image-expansion" } }) },
|
|
||||||
"@/lib/db": {},
|
|
||||||
"@/lib/db/schema": {},
|
|
||||||
"@/lib/storage/service": {},
|
|
||||||
"@/lib/ai/vip98k-runtime": { executeConfiguredVip98kTask: async () => ({ outputUrls: ["https://relay.test/result.png"] }) },
|
|
||||||
"@/lib/storage/image-archive": { archiveTemporaryImage: async (url: string, id: string) => {
|
|
||||||
calls.push(url, id);
|
|
||||||
return { url: "https://cdn.test/native.png", thumbnailUrl: "https://cdn.test/native.png", bytes: 12375212 };
|
|
||||||
} },
|
|
||||||
})("lib/ai/extend/service.ts");
|
|
||||||
const result = await service.executeExtendJob({ userId: "test", imageUrl: "https://source.test/original.jpg", requestId: "request-1", skipGallerySave: true });
|
|
||||||
expect(calls).toEqual(["https://relay.test/result.png", "request-1"]);
|
|
||||||
expect(result.original_url).toBe("https://cdn.test/native.png");
|
|
||||||
expect(result.image_base64).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("native expansion still returns image bytes to callers requesting base64", async () => {
|
|
||||||
const image = await sharp({ create: { width: 12, height: 8, channels: 3, background: "red" } }).png().toBuffer();
|
|
||||||
const service = serverLoader({
|
|
||||||
"@/lib/db/queries": { getSystemSetting: async () => ({ value: { provider: "vip98k", modelId: "image-expansion" } }) },
|
|
||||||
"@/lib/db": {},
|
|
||||||
"@/lib/db/schema": {},
|
|
||||||
"@/lib/storage/service": { uploadFile: async (name: string) => ({ url: `https://cdn.test/${name}` }) },
|
|
||||||
"@/lib/storage/image-archive": { archiveTemporaryImage: () => { throw new Error("Base64 callers need the image bytes"); } },
|
|
||||||
"@/lib/ai/vip98k-runtime": { executeConfiguredVip98kTask: async () => ({ outputUrls: ["https://relay.test/result.png"] }) },
|
|
||||||
fetch: async () => new Response(new Uint8Array(image)),
|
|
||||||
})("lib/ai/extend/service.ts");
|
|
||||||
const result = await service.executeExtendJob({ userId: "test", imageUrl: "https://source.test/original.jpg", includeBase64: true, skipGallerySave: true });
|
|
||||||
expect(result.image_base64).toBe(image.toString("base64"));
|
|
||||||
expect(result.original_url).toMatch(/\.png$/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe("image extension provider configuration", () => {
|
test.describe("image extension provider configuration", () => {
|
||||||
test("uses the most recently updated Alibaba image provider key", () => {
|
test("uses the most recently updated Alibaba image provider key", () => {
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user