From 44d4a8065e7ea69742e60bfd5be533ddb40aa7e2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 12 May 2026 14:36:56 +0800 Subject: [PATCH] fix: preserve grsai gpt image reference ratio --- lib/ai/providers/gemini-image.ts | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/ai/providers/gemini-image.ts b/lib/ai/providers/gemini-image.ts index 3d6708ffc..cb48a9e2b 100644 --- a/lib/ai/providers/gemini-image.ts +++ b/lib/ai/providers/gemini-image.ts @@ -70,6 +70,47 @@ async function ensureImageUrl(url: string): Promise { return url; } +async function inferImageAspectRatioFromUrl(url: string): Promise { + const normalized = String(url || "").trim(); + if (!normalized) return null; + + try { + let buffer: Buffer | null = null; + if (normalized.startsWith("data:")) { + const match = normalized.match(/^data:([A-Za-z0-9.+/-]+);base64,(.+)$/); + if (!match) return null; + buffer = Buffer.from(match[2], "base64"); + } else if (/^https?:\/\//i.test(normalized)) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 20000); + try { + const response = await fetch(normalized, { + signal: controller.signal, + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + }, + }); + if (!response.ok) return null; + buffer = Buffer.from(await response.arrayBuffer()); + } finally { + clearTimeout(timeoutId); + } + } + + if (!buffer?.length) return null; + const sharp = await import("sharp"); + const metadata = await sharp.default(buffer).metadata(); + if (!metadata.width || !metadata.height || metadata.height <= 0) { + return null; + } + + return `${metadata.width}:${metadata.height}`; + } catch (error) { + console.warn("[Gemini Image] Failed to infer reference aspect ratio:", error); + return null; + } +} + /** * 将 base64 图片上传到 S3,返回缩略图和原图的 URL */ @@ -483,10 +524,19 @@ async function sendGeminiRequest( } if (isGptImageModel) { - requestBody.size = resolveGrsaiGptImageSize(imageParams?.aspectRatio, imageParams?.imageSize); + const requestedAspectRatio = imageParams?.aspectRatio || "auto"; + const inferredAspectRatio = + requestedAspectRatio === "auto" && processedUrls.length > 0 + ? await inferImageAspectRatioFromUrl(processedUrls[0]) + : null; + const resolvedAspectRatio = inferredAspectRatio || requestedAspectRatio; + requestBody.size = resolveGrsaiGptImageSize(resolvedAspectRatio, imageParams?.imageSize); requestBody.shutProgress = false; + if (inferredAspectRatio) { + console.log(`${apiLogPrefix} Inferred reference aspect ratio: ${inferredAspectRatio}`); + } console.log( - `${apiLogPrefix} Resolved size: aspectRatio=${imageParams?.aspectRatio || "auto"}, imageSize=${imageParams?.imageSize || "2K"} => ${requestBody.size}` + `${apiLogPrefix} Resolved size: aspectRatio=${resolvedAspectRatio}, imageSize=${imageParams?.imageSize || "2K"} => ${requestBody.size}` ); } else { // 添加 Nano Banana 特定参数