fix: fallback scale adjust on provider failure

This commit is contained in:
134 2026-06-20 00:09:42 +08:00
parent c0e16a354f
commit 0b1a3a715b

View File

@ -514,6 +514,81 @@ async function buildScaleAdjustFallbackImage(params: {
return workingBuffer;
}
async function loadScaleAdjustSourceImage(
verification: CanvasScaleAdjustVerificationConfig
) {
const sourceBuffer = await sharp(
await fetchImageBufferFromUrl(verification.sourceImageUrl),
{ failOn: "none" }
)
.rotate()
.png({ compressionLevel: 6 })
.toBuffer();
const sourceMetadata = await sharp(sourceBuffer, { failOn: "none" }).metadata();
const sourceWidth = Number(sourceMetadata.width || 0);
const sourceHeight = Number(sourceMetadata.height || 0);
if (sourceWidth <= 0 || sourceHeight <= 0) {
return null;
}
return {
sourceBuffer,
sourceHeight,
sourceWidth,
};
}
async function createScaleAdjustFallbackUpload(params: {
sourceBuffer: Buffer;
sourceWidth: number;
sourceHeight: number;
verification: CanvasScaleAdjustVerificationConfig;
debugLabel: string;
imageIndex?: number;
reason: string;
diffScores?: number[];
}) {
const fallbackBuffer = await buildScaleAdjustFallbackImage({
sourceBuffer: params.sourceBuffer,
sourceHeight: params.sourceHeight,
sourceWidth: params.sourceWidth,
verification: params.verification,
});
const thumbnailBuffer = await sharp(fallbackBuffer, { failOn: "none" })
.resize(512, 512, { fit: "inside", withoutEnlargement: true })
.png({ compressionLevel: 6 })
.toBuffer();
const suffix = `${Date.now()}-${params.imageIndex || 1}-${Math.random()
.toString(36)
.slice(2, 8)}`;
const [originalUpload, thumbnailUpload] = await Promise.all([
uploadFile(
`canvas-scale-adjust-fallback-${suffix}.png`,
bufferToArrayBuffer(fallbackBuffer)
),
uploadFile(
`canvas-scale-adjust-fallback-thumb-${suffix}.png`,
bufferToArrayBuffer(thumbnailBuffer)
),
]);
console.warn("[Canvas Generate Image] Scale adjust fallback applied:", {
debugLabel: params.debugLabel,
diffScores: params.diffScores?.map((score) => Math.round(score * 10) / 10),
imageIndex: params.imageIndex,
reason: params.reason,
regionCount: params.verification.regions.length,
scalePercent: params.verification.scalePercent,
sourceHeight: params.sourceHeight,
sourceWidth: params.sourceWidth,
});
return {
originalUrl: originalUpload.url,
thumbnailUrl: thumbnailUpload.url,
};
}
async function applyScaleAdjustVerificationToImages(params: {
images: ParsedImage[];
verification: CanvasScaleAdjustVerificationConfig | null;
@ -523,19 +598,11 @@ async function applyScaleAdjustVerificationToImages(params: {
if (!verification || images.length === 0) return images;
try {
const sourceBuffer = await sharp(
await fetchImageBufferFromUrl(verification.sourceImageUrl),
{ failOn: "none" }
)
.rotate()
.png({ compressionLevel: 6 })
.toBuffer();
const sourceMetadata = await sharp(sourceBuffer, { failOn: "none" }).metadata();
const sourceWidth = Number(sourceMetadata.width || 0);
const sourceHeight = Number(sourceMetadata.height || 0);
if (sourceWidth <= 0 || sourceHeight <= 0) {
const sourceImage = await loadScaleAdjustSourceImage(verification);
if (!sourceImage) {
return images;
}
const { sourceBuffer, sourceHeight, sourceWidth } = sourceImage;
const checkedImages: ParsedImage[] = [];
for (const [index, image] of images.entries()) {
@ -563,41 +630,17 @@ async function applyScaleAdjustVerificationToImages(params: {
continue;
}
const fallbackBuffer = await buildScaleAdjustFallbackImage({
const fallbackImage = await createScaleAdjustFallbackUpload({
debugLabel,
diffScores,
imageIndex: index + 1,
reason: "unchanged-output",
sourceBuffer,
sourceHeight,
sourceWidth,
verification,
});
const thumbnailBuffer = await sharp(fallbackBuffer, { failOn: "none" })
.resize(512, 512, { fit: "inside", withoutEnlargement: true })
.png({ compressionLevel: 6 })
.toBuffer();
const suffix = `${Date.now()}-${index + 1}-${Math.random().toString(36).slice(2, 8)}`;
const [originalUpload, thumbnailUpload] = await Promise.all([
uploadFile(
`canvas-scale-adjust-fallback-${suffix}.png`,
bufferToArrayBuffer(fallbackBuffer)
),
uploadFile(
`canvas-scale-adjust-fallback-thumb-${suffix}.png`,
bufferToArrayBuffer(thumbnailBuffer)
),
]);
console.warn("[Canvas Generate Image] Scale adjust fallback applied:", {
debugLabel,
diffScores: diffScores.map((score) => Math.round(score * 10) / 10),
imageIndex: index + 1,
regionCount: verification.regions.length,
scalePercent: verification.scalePercent,
sourceHeight,
sourceWidth,
});
checkedImages.push({
originalUrl: originalUpload.url,
thumbnailUrl: thumbnailUpload.url,
});
checkedImages.push(fallbackImage);
}
return checkedImages;
@ -1483,6 +1526,82 @@ export async function generateCanvasImage(
await slot.release();
}
if (scaleAdjustVerification) {
try {
const sourceImage = await loadScaleAdjustSourceImage(scaleAdjustVerification);
if (sourceImage) {
const fallbackImage = await createScaleAdjustFallbackUpload({
debugLabel,
imageIndex: 1,
reason: "provider-error",
sourceBuffer: sourceImage.sourceBuffer,
sourceHeight: sourceImage.sourceHeight,
sourceWidth: sourceImage.sourceWidth,
verification: scaleAdjustVerification,
});
await recordAiModelHealthFailure({
model: selectedModel.model,
provider,
error: caughtError,
context: debugLabel,
elapsedMs: Date.now() - startedAt,
metadata: {
aspectRatio,
imageSize,
imageCount,
requestedReferenceImageCount: normalizedReferenceImages.length,
reachableReferenceImageCount: reachableReferenceImages.length,
effectiveReferenceImageCount: effectiveReferenceImages.length,
droppedReferenceImageCount: unreachableReferenceImages.length,
scaleAdjustFallbackReason: "provider-error",
...debugMeta,
},
}).catch((healthError) => {
console.warn("[Canvas Generate Image] Record model health failure failed:", {
selectedModelRef: selectedModel.model.id,
selectedModelId: selectedModel.model.modelId,
error: healthError instanceof Error ? healthError.message : String(healthError),
});
});
recordCanvasImageModelExecutionDuration(
resolvedModelKey,
Date.now() - startedAt
);
console.warn("[Canvas Generate Image] Scale adjust provider failed; returned deterministic fallback:", {
debugLabel,
elapsedMs: Date.now() - startedAt,
providerType: provider.type,
selectedModelRef: selectedModel.model.id,
selectedModelId: selectedModel.model.modelId,
error: caughtError instanceof Error ? caughtError.message : String(caughtError),
...debugMeta,
});
return {
images: [fallbackImage],
model: selectedModel.model.displayName,
modelRef: selectedModel.model.id,
prompt: resultPrompt,
};
}
} catch (fallbackError) {
console.warn("[Canvas Generate Image] Scale adjust provider-error fallback failed:", {
debugLabel,
error:
fallbackError instanceof Error
? fallbackError.message
: String(fallbackError),
originalError:
caughtError instanceof Error ? caughtError.message : String(caughtError),
regionCount: scaleAdjustVerification.regions.length,
scalePercent: scaleAdjustVerification.scalePercent,
});
}
}
await recordAiModelHealthFailure({
model: selectedModel.model,
provider,