修复连续改图变糊:历史图优先原始上传并提升默认清晰度
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-12 16:02:40 +08:00
parent 20c9f6d821
commit 3db32eceb0
2 changed files with 92 additions and 34 deletions

View File

@ -956,52 +956,81 @@ export async function POST(request: Request) {
if (!hasImage && smartAutoHistoryMessagesFromDb.length > 0) { if (!hasImage && smartAutoHistoryMessagesFromDb.length > 0) {
console.log("[Smart Auto Mode] 🔍 No image in current message, auto-searching history..."); console.log("[Smart Auto Mode] 🔍 No image in current message, auto-searching history...");
// 从历史消息中倒序查找最近一轮包含图片的消息 const explicitFollowLatestResult = /(上一张|上一轮|刚生成|最新生成|这个结果|该结果|按这个结果|继续在这张|基于这张结果|延续上一次结果)/i.test(
// 收集该消息中的所有图片(支持多图融合编辑) textContent
);
const latestAssistantImages: string[] = [];
const latestUserImages: string[] = [];
const pushUniqueUrl = (bucket: string[], maybeUrl?: string) => {
if (!maybeUrl || typeof maybeUrl !== "string") return;
const normalized = maybeUrl.trim();
if (!/^https?:\/\//i.test(normalized)) return;
if (!bucket.includes(normalized)) {
bucket.push(normalized);
}
};
// 从历史消息中倒序查找最近的“AI生成图”和“用户上传图”
for (let i = smartAutoHistoryMessagesFromDb.length - 1; i >= 0; i--) { for (let i = smartAutoHistoryMessagesFromDb.length - 1; i >= 0; i--) {
const msg = smartAutoHistoryMessagesFromDb[i]; const msg = smartAutoHistoryMessagesFromDb[i];
const parts = msg.parts as any[]; const parts = msg.parts as any[];
if (!parts) continue; if (!parts) continue;
// 查找AI生成的图片(从Markdown中提取所有图片) if (msg.role === "assistant" && latestAssistantImages.length === 0) {
if (msg.role === "assistant") {
for (const part of parts) { for (const part of parts) {
if (part.type === "text" && part.text) { if (part.type === "text" && part.text) {
// 从 markdown 中优先提取原图 URL,避免把缩略图当作下一轮输入导致画质下降
const parsedImageUrls = extractOriginalImageUrlsFromText(part.text); const parsedImageUrls = extractOriginalImageUrlsFromText(part.text);
for (const parsedUrl of parsedImageUrls) { for (const parsedUrl of parsedImageUrls) {
if (!smartAutoHistoryImages.includes(parsedUrl)) { pushUniqueUrl(latestAssistantImages, parsedUrl);
smartAutoHistoryImages.push(parsedUrl);
}
} }
} }
} }
// 如果在这条assistant消息中找到了图片,就停止搜索(使用最近一轮的结果)
if (smartAutoHistoryImages.length > 0) {
console.log("[Smart Auto Mode] 🖼️ Found AI-generated images:", smartAutoHistoryImages.length);
break;
}
} }
// 查找用户上传的图片(收集该消息中的所有图片) if (msg.role === "user" && latestUserImages.length === 0) {
if (msg.role === "user" && smartAutoHistoryImages.length === 0) {
for (const part of parts) { for (const part of parts) {
if ((part.type === "file" || part.type === "image") && part.url) { if ((part.type === "file" || part.type === "image") && part.url) {
if (!smartAutoHistoryImages.includes(part.url)) { pushUniqueUrl(latestUserImages, part.url);
smartAutoHistoryImages.push(part.url); }
if (part.type === "image_url" && part.image_url?.url) {
pushUniqueUrl(latestUserImages, part.image_url.url);
}
if (part.type === "text" && part.text) {
const parsedImageUrls = extractOriginalImageUrlsFromText(part.text);
for (const parsedUrl of parsedImageUrls) {
pushUniqueUrl(latestUserImages, parsedUrl);
} }
} }
} }
// 如果在这条user消息中找到了图片,就停止搜索 }
if (smartAutoHistoryImages.length > 0) {
console.log("[Smart Auto Mode] 🖼️ Found user-uploaded images:", smartAutoHistoryImages.length); if (latestAssistantImages.length > 0 && latestUserImages.length > 0) {
break; break;
}
} }
} }
// 如果找到了历史图片,标记为有图片 if (explicitFollowLatestResult && latestAssistantImages.length > 0) {
smartAutoHistoryImages = latestAssistantImages.slice(0, 4);
console.log(
"[Smart Auto Mode] 🖼️ Using latest generated image(s) by explicit instruction:",
smartAutoHistoryImages.length
);
} else if (latestUserImages.length > 0) {
smartAutoHistoryImages = latestUserImages.slice(0, 4);
console.log(
"[Smart Auto Mode] 🖼️ Using latest user-uploaded image(s) to avoid iterative blur:",
smartAutoHistoryImages.length
);
} else if (latestAssistantImages.length > 0) {
smartAutoHistoryImages = latestAssistantImages.slice(0, 4);
console.log(
"[Smart Auto Mode] 🖼️ Fallback to latest generated image(s):",
smartAutoHistoryImages.length
);
}
if (smartAutoHistoryImages.length > 0) { if (smartAutoHistoryImages.length > 0) {
hasImage = true; hasImage = true;
imageUrls = smartAutoHistoryImages; imageUrls = smartAutoHistoryImages;
@ -1201,6 +1230,11 @@ export async function POST(request: Request) {
hasUserCustomSettings: hasUserCustomImageParams, hasUserCustomSettings: hasUserCustomImageParams,
}) })
: requestBody.imageParams; : requestBody.imageParams;
const defaultImageQualityParams = {
aspectRatio: "auto",
imageSize: smartAutoDetectedIntent === "image_edit" ? "4K" : "2K",
imageCount: 1,
} as const;
if (shouldApplyAgentImageParams) { if (shouldApplyAgentImageParams) {
console.log("[Smart Auto Mode] 🎯 Effective image params:", { console.log("[Smart Auto Mode] 🎯 Effective image params:", {
@ -1564,7 +1598,11 @@ export async function POST(request: Request) {
// 调用 doGenerate 获取图片(如果是备用模型,传入 overrideModelId) // 调用 doGenerate 获取图片(如果是备用模型,传入 overrideModelId)
const result = await (languageModel as any).doGenerate({ const result = await (languageModel as any).doGenerate({
messages: modelMessages, messages: modelMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K" }, imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
},
overrideModelId: isRetry ? modelIdToUse : undefined, // 🆕 使用备用模型 ID overrideModelId: isRetry ? modelIdToUse : undefined, // 🆕 使用备用模型 ID
}); });
@ -1930,7 +1968,12 @@ export async function POST(request: Request) {
// 调用 doGenerate 获取图片,传递模型显示名称 // 调用 doGenerate 获取图片,传递模型显示名称
const result = await (languageModel as any).doGenerate({ const result = await (languageModel as any).doGenerate({
messages: modelMessages, messages: modelMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 }, imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
imageCount: defaultImageQualityParams.imageCount,
},
modelDisplayName, // 🆕 传递模型显示名称 modelDisplayName, // 🆕 传递模型显示名称
overrideModelId: isRetry ? modelIdToUse : undefined, overrideModelId: isRetry ? modelIdToUse : undefined,
}); });
@ -2028,7 +2071,12 @@ export async function POST(request: Request) {
const candidateResult = await (candidateLanguageModel as any).doGenerate({ const candidateResult = await (candidateLanguageModel as any).doGenerate({
messages: candidateMessages, messages: candidateMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 }, imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
imageCount: defaultImageQualityParams.imageCount,
},
modelDisplayName: candidate.model.displayName, modelDisplayName: candidate.model.displayName,
overrideModelId: candidate.model.modelId, overrideModelId: candidate.model.modelId,
}); });
@ -2274,7 +2322,12 @@ export async function POST(request: Request) {
const tryGenerateImage = async (modelIdToUse: string, isRetry: boolean) => { const tryGenerateImage = async (modelIdToUse: string, isRetry: boolean) => {
const result = await (languageModel as any).doGenerate({ const result = await (languageModel as any).doGenerate({
messages: modelMessages, messages: modelMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 }, imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
imageCount: defaultImageQualityParams.imageCount,
},
overrideModelId: isRetry ? modelIdToUse : undefined, overrideModelId: isRetry ? modelIdToUse : undefined,
}); });
if (result?.finishReason === "error") { if (result?.finishReason === "error") {
@ -2366,7 +2419,12 @@ export async function POST(request: Request) {
const candidateResult = await (candidateLanguageModel as any).doGenerate({ const candidateResult = await (candidateLanguageModel as any).doGenerate({
messages: candidateMessages, messages: candidateMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 }, imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
imageCount: defaultImageQualityParams.imageCount,
},
modelDisplayName: candidate.model.displayName, modelDisplayName: candidate.model.displayName,
overrideModelId: candidate.model.modelId, overrideModelId: candidate.model.modelId,
}); });

View File

@ -171,7 +171,7 @@ export function ImagePreview({
}; };
const { thumbnail, original, maxWidth, is4K } = parseImageUrls(); const { thumbnail, original, maxWidth, is4K } = parseImageUrls();
const preferOriginalForPreview = maxWidth >= 380 && original && original !== thumbnail; const preferOriginalForPreview = Boolean(original && original !== thumbnail);
const previewSrc = preferOriginalForPreview ? original : thumbnail; const previewSrc = preferOriginalForPreview ? original : thumbnail;
const previewSrcSet = original const previewSrcSet = original
? `${thumbnail} 1x, ${original} 2x` ? `${thumbnail} 1x, ${original} 2x`