From fa4b4183cf9ee49b53c32970959d7dd0de937db4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Jun 2026 14:35:44 +0800 Subject: [PATCH] fix: stabilize detail replacement result images --- .../api/chat/_lib/detail-replacement.ts | 70 ++++++++++--------- app/(chat)/api/chat/route.ts | 13 ++++ 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/app/(chat)/api/chat/_lib/detail-replacement.ts b/app/(chat)/api/chat/_lib/detail-replacement.ts index c3ac8a05a..08c667820 100644 --- a/app/(chat)/api/chat/_lib/detail-replacement.ts +++ b/app/(chat)/api/chat/_lib/detail-replacement.ts @@ -118,42 +118,48 @@ export async function normalizeReplacementResultImage(params: { const generatedBuffer = await fetchImageBufferFromUrl(params.generatedImageUrl); let targetWidth = params.referenceGeometry?.width; let targetHeight = params.referenceGeometry?.height; - - if (!targetWidth || !targetHeight) { - const referenceBuffer = await fetchImageBufferFromUrl(params.referenceImageUrl); - const referenceMetadata = await sharp(referenceBuffer, { failOn: "none" }) - .rotate() - .metadata(); - targetWidth = referenceMetadata.width; - targetHeight = referenceMetadata.height; - } - - if (!targetWidth || !targetHeight) { - throw new Error("无法读取参考图尺寸"); - } - const generatedMetadata = await sharp(generatedBuffer, { failOn: "none" }) .rotate() .metadata(); - if (generatedMetadata.width === targetWidth && generatedMetadata.height === targetHeight) { - return { - originalUrl: params.generatedImageUrl, - thumbnailUrl: params.generatedThumbnailUrl || params.generatedImageUrl, - imageSize: `${targetWidth}x${targetHeight}`, - }; + + if (!targetWidth || !targetHeight) { + try { + const referenceBuffer = await fetchImageBufferFromUrl(params.referenceImageUrl); + const referenceMetadata = await sharp(referenceBuffer, { failOn: "none" }) + .rotate() + .metadata(); + targetWidth = referenceMetadata.width; + targetHeight = referenceMetadata.height; + } catch (error) { + chatApiLogger.warn("[Chat API] Failed to read replacement reference size, hosting generated output as-is:", { + referenceImageUrl: params.referenceImageUrl, + generatedImageUrl: params.generatedImageUrl, + error: error instanceof Error ? error.message : "unknown", + }); + } } - const normalizedBuffer = await sharp(generatedBuffer, { failOn: "none" }) - .rotate() - .resize(targetWidth, targetHeight, { - // Keep the model output proportional when adapting it back to the reference page. - // `fill` stretches the picture and can squash products; `cover` behaves like a screenshot crop. - fit: "cover", - position: "centre", - withoutEnlargement: false, - }) - .png() - .toBuffer(); + const outputWidth = targetWidth || generatedMetadata.width; + const outputHeight = targetHeight || generatedMetadata.height; + + if (!outputWidth || !outputHeight) { + throw new Error("无法读取结果图尺寸"); + } + + const imagePipeline = sharp(generatedBuffer, { failOn: "none" }).rotate(); + const normalizedBuffer = + targetWidth && targetHeight + ? await imagePipeline + .resize(targetWidth, targetHeight, { + // Keep the model output proportional when adapting it back to the reference page. + // `fill` stretches the picture and can squash products; `cover` behaves like a screenshot crop. + fit: "cover", + position: "centre", + withoutEnlargement: false, + }) + .png() + .toBuffer() + : await imagePipeline.png().toBuffer(); const thumbnailBuffer = await createAgentImageThumbnailBuffer(normalizedBuffer); const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; @@ -167,7 +173,7 @@ export async function normalizeReplacementResultImage(params: { return { originalUrl: originalUpload.url, thumbnailUrl: thumbnailUpload.url, - imageSize: `${targetWidth}x${targetHeight}`, + imageSize: `${outputWidth}x${outputHeight}`, }; } diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 3bb818aae..8fed6e34c 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -249,6 +249,7 @@ import { ensureStructuredAgentFriendlyMessage, extractMessageImageAssetIds, extractMessageImageUrls, + isRemoteImageAccessible, injectHistoryImagesToLastUserMessage, normalizeAssistantImageMarkdown, normalizeResolvedTargetImageUrls, @@ -3544,6 +3545,12 @@ export async function POST(request: Request) { ? normalizeError.message : "unknown", }); + const rawResultAccessible = await isRemoteImageAccessible( + firstImage.originalUrl + ).catch(() => false); + if (!rawResultAccessible) { + throw normalizeError; + } } await emitReplacementResultMessage({ @@ -4341,6 +4348,12 @@ export async function POST(request: Request) { ? normalizeError.message : "unknown", }); + const rawResultAccessible = await isRemoteImageAccessible( + firstImage.originalUrl + ).catch(() => false); + if (!rawResultAccessible) { + throw normalizeError; + } } chatApiLogger.info("[Chat API] Replacement image generation completed:", { mode: replacementSourceType,