fix: stabilize scale adjust fal timeout and uploads
This commit is contained in:
parent
134411f7f8
commit
0405113d52
@ -28,6 +28,7 @@ interface FalImageParams {
|
||||
systemPrompt?: string;
|
||||
thinkingLevel?: string;
|
||||
syncMode?: boolean;
|
||||
timeoutMs?: number;
|
||||
}
|
||||
|
||||
interface UploadedImage {
|
||||
@ -50,6 +51,8 @@ const DEFAULT_RESOLUTION = "1K";
|
||||
const DEFAULT_OUTPUT_FORMAT = "png";
|
||||
const DEFAULT_SAFETY_TOLERANCE = "4";
|
||||
const DEFAULT_TIMEOUT_MS = 600000;
|
||||
const MIN_TIMEOUT_MS = 30_000;
|
||||
const MAX_TIMEOUT_MS = 600_000;
|
||||
const FAL_GENERIC_MAX_EDGE = 4096;
|
||||
const FAL_GENERIC_MAX_PIXELS = 4096 * 4096;
|
||||
const GPT_IMAGE_2_MAX_EDGE = 3840;
|
||||
@ -434,6 +437,12 @@ function normalizeOptionalHttpUrl(value?: string) {
|
||||
return /^https?:\/\//i.test(normalized) ? normalized : "";
|
||||
}
|
||||
|
||||
function normalizeTimeoutMs(value: unknown) {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) return DEFAULT_TIMEOUT_MS;
|
||||
return Math.max(MIN_TIMEOUT_MS, Math.min(MAX_TIMEOUT_MS, Math.round(numeric)));
|
||||
}
|
||||
|
||||
function detectImageExtension(imageUrl: string, contentType?: string | null) {
|
||||
const type = (contentType || "").toLowerCase();
|
||||
if (type.includes("jpeg") || type.includes("jpg")) return "jpg";
|
||||
@ -911,13 +920,14 @@ export function createFalImage({ apiKey, baseURL }: FalImageConfig) {
|
||||
});
|
||||
|
||||
const fal = await createFalClientFromApiKey(apiKey);
|
||||
const timeoutMs = normalizeTimeoutMs(options.imageParams?.timeoutMs);
|
||||
|
||||
let result: any;
|
||||
try {
|
||||
result = await fal.subscribe(request.falEndpointId, {
|
||||
input: request.input,
|
||||
logs: true,
|
||||
timeout: DEFAULT_TIMEOUT_MS,
|
||||
timeout: timeoutMs,
|
||||
onQueueUpdate: (update: any) => {
|
||||
if (update?.status === "IN_PROGRESS" && Array.isArray(update.logs)) {
|
||||
for (const log of update.logs) {
|
||||
|
||||
@ -292,6 +292,15 @@ function getCanvasProviderRuntimeParams(params: {
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
params.providerType === "fal-image" &&
|
||||
isCanvasScaleAdjustDebugRequest(params.debugLabel, params.debugMeta)
|
||||
) {
|
||||
return {
|
||||
timeoutMs: CANVAS_SCALE_ADJUST_FINAL_PROVIDER_BATCH_TIMEOUT_MS,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -3059,6 +3068,13 @@ async function applyScaleAdjustPatchCompositeToImages(params: {
|
||||
touchesTop,
|
||||
width: workspaceBox.width,
|
||||
});
|
||||
const staticPatchMaskRaw = staticPatchMask
|
||||
? await normalizeOpacityMaskToRaw({
|
||||
height: workspaceBox.height,
|
||||
maskBuffer: staticPatchMask,
|
||||
width: workspaceBox.width,
|
||||
})
|
||||
: null;
|
||||
const regionClipPatchMask = maskRegionBoxes?.length
|
||||
? buildScaleAdjustPatchCompositeMaskSvg({
|
||||
featherPx,
|
||||
@ -3177,12 +3193,22 @@ async function applyScaleAdjustPatchCompositeToImages(params: {
|
||||
imageBuffer: resizedGeneratedBuffer,
|
||||
width: workspaceBox.width,
|
||||
})
|
||||
: await sharp(resizedGeneratedBuffer, { failOn: "none" })
|
||||
.ensureAlpha()
|
||||
.composite([{ input: patchMask, blend: "dest-in" }])
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer();
|
||||
const mergedBuffer = await sharp(sourceBuffer, { failOn: "none" })
|
||||
: activeClipPatchMaskRaw || staticPatchMaskRaw
|
||||
? await applyRawAlphaToImage({
|
||||
alphaMaskRaw:
|
||||
activeClipPatchMaskRaw && staticPatchMaskRaw
|
||||
? multiplyAlphaMasks(staticPatchMaskRaw, activeClipPatchMaskRaw)
|
||||
: activeClipPatchMaskRaw || staticPatchMaskRaw!,
|
||||
height: workspaceBox.height,
|
||||
imageBuffer: resizedGeneratedBuffer,
|
||||
width: workspaceBox.width,
|
||||
})
|
||||
: await sharp(resizedGeneratedBuffer, { failOn: "none" })
|
||||
.ensureAlpha()
|
||||
.composite([{ input: patchMask, blend: "dest-in" }])
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer();
|
||||
const mergedPngBuffer = await sharp(sourceBuffer, { failOn: "none" })
|
||||
.ensureAlpha()
|
||||
.composite([
|
||||
{
|
||||
@ -3193,20 +3219,25 @@ async function applyScaleAdjustPatchCompositeToImages(params: {
|
||||
])
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer();
|
||||
const thumbnailBuffer = await sharp(mergedBuffer, { failOn: "none" })
|
||||
const mergedBuffer = await sharp(mergedPngBuffer, { failOn: "none" })
|
||||
.flatten({ background: "#ffffff" })
|
||||
.jpeg({ quality: 94, mozjpeg: true })
|
||||
.toBuffer();
|
||||
const thumbnailBuffer = await sharp(mergedPngBuffer, { failOn: "none" })
|
||||
.flatten({ background: "#ffffff" })
|
||||
.resize(512, 512, { fit: "inside", withoutEnlargement: true })
|
||||
.png({ compressionLevel: 6 })
|
||||
.jpeg({ quality: 88, mozjpeg: true })
|
||||
.toBuffer();
|
||||
const suffix = `${Date.now()}-${index + 1}-${Math.random()
|
||||
.toString(36)
|
||||
.slice(2, 8)}`;
|
||||
const [originalUpload, thumbnailUpload] = await Promise.all([
|
||||
uploadFile(
|
||||
`canvas-scale-adjust-ai-patch-composited-${suffix}.png`,
|
||||
`canvas-scale-adjust-ai-patch-composited-${suffix}.jpg`,
|
||||
bufferToArrayBuffer(mergedBuffer)
|
||||
),
|
||||
uploadFile(
|
||||
`canvas-scale-adjust-ai-patch-composited-thumb-${suffix}.png`,
|
||||
`canvas-scale-adjust-ai-patch-composited-thumb-${suffix}.jpg`,
|
||||
bufferToArrayBuffer(thumbnailBuffer)
|
||||
),
|
||||
]);
|
||||
@ -3293,26 +3324,31 @@ async function applyScaleAdjustProductLockCompositeToImages(params: {
|
||||
width: generatedWidth,
|
||||
})
|
||||
: resizedOverlayBaseBuffer;
|
||||
const mergedBuffer = await sharp(normalizedGeneratedBuffer, {
|
||||
const mergedPngBuffer = await sharp(normalizedGeneratedBuffer, {
|
||||
failOn: "none",
|
||||
})
|
||||
.composite([{ input: resizedOverlayBuffer, left: 0, top: 0 }])
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer();
|
||||
const thumbnailBuffer = await sharp(mergedBuffer, { failOn: "none" })
|
||||
const mergedBuffer = await sharp(mergedPngBuffer, { failOn: "none" })
|
||||
.flatten({ background: "#ffffff" })
|
||||
.jpeg({ quality: 94, mozjpeg: true })
|
||||
.toBuffer();
|
||||
const thumbnailBuffer = await sharp(mergedPngBuffer, { failOn: "none" })
|
||||
.flatten({ background: "#ffffff" })
|
||||
.resize(512, 512, { fit: "inside", withoutEnlargement: true })
|
||||
.png({ compressionLevel: 6 })
|
||||
.jpeg({ quality: 88, mozjpeg: true })
|
||||
.toBuffer();
|
||||
const suffix = `${Date.now()}-${index + 1}-${Math.random()
|
||||
.toString(36)
|
||||
.slice(2, 8)}`;
|
||||
const [originalUpload, thumbnailUpload] = await Promise.all([
|
||||
uploadFile(
|
||||
`canvas-scale-adjust-product-lock-${suffix}.png`,
|
||||
`canvas-scale-adjust-product-lock-${suffix}.jpg`,
|
||||
bufferToArrayBuffer(mergedBuffer)
|
||||
),
|
||||
uploadFile(
|
||||
`canvas-scale-adjust-product-lock-thumb-${suffix}.png`,
|
||||
`canvas-scale-adjust-product-lock-thumb-${suffix}.jpg`,
|
||||
bufferToArrayBuffer(thumbnailBuffer)
|
||||
),
|
||||
]);
|
||||
|
||||
@ -59,7 +59,7 @@ interface QueueState {
|
||||
}
|
||||
|
||||
const DEFAULT_RUNNING_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
const SCALE_ADJUST_RUNNING_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
const SCALE_ADJUST_RUNNING_TIMEOUT_MS = 8 * 60 * 1000;
|
||||
const LOCAL_ACTIVE_RUNNING_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
||||
const PENDING_LOOKAHEAD_LIMIT = 100;
|
||||
const QUEUE_STATUS_PENDING_LOOKAHEAD_LIMIT = 500;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user