修复连续改图变糊:历史图优先原始上传并提升默认清晰度
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

@ -955,53 +955,82 @@ export async function POST(request: Request) {
// 逻辑:只要用户在对话中没有附上新图片,就默认继续处理之前的图片
if (!hasImage && smartAutoHistoryMessagesFromDb.length > 0) {
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--) {
const msg = smartAutoHistoryMessagesFromDb[i];
const parts = msg.parts as any[];
if (!parts) continue;
// 查找AI生成的图片(从Markdown中提取所有图片)
if (msg.role === "assistant") {
if (msg.role === "assistant" && latestAssistantImages.length === 0) {
for (const part of parts) {
if (part.type === "text" && part.text) {
// 从 markdown 中优先提取原图 URL,避免把缩略图当作下一轮输入导致画质下降
const parsedImageUrls = extractOriginalImageUrlsFromText(part.text);
for (const parsedUrl of parsedImageUrls) {
if (!smartAutoHistoryImages.includes(parsedUrl)) {
smartAutoHistoryImages.push(parsedUrl);
}
pushUniqueUrl(latestAssistantImages, parsedUrl);
}
}
}
// 如果在这条assistant消息中找到了图片,就停止搜索(使用最近一轮的结果)
if (smartAutoHistoryImages.length > 0) {
console.log("[Smart Auto Mode] 🖼️ Found AI-generated images:", smartAutoHistoryImages.length);
break;
}
}
// 查找用户上传的图片(收集该消息中的所有图片)
if (msg.role === "user" && smartAutoHistoryImages.length === 0) {
if (msg.role === "user" && latestUserImages.length === 0) {
for (const part of parts) {
if ((part.type === "file" || part.type === "image") && part.url) {
if (!smartAutoHistoryImages.includes(part.url)) {
smartAutoHistoryImages.push(part.url);
pushUniqueUrl(latestUserImages, 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);
break;
}
}
if (latestAssistantImages.length > 0 && latestUserImages.length > 0) {
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) {
hasImage = true;
imageUrls = smartAutoHistoryImages;
@ -1201,6 +1230,11 @@ export async function POST(request: Request) {
hasUserCustomSettings: hasUserCustomImageParams,
})
: requestBody.imageParams;
const defaultImageQualityParams = {
aspectRatio: "auto",
imageSize: smartAutoDetectedIntent === "image_edit" ? "4K" : "2K",
imageCount: 1,
} as const;
if (shouldApplyAgentImageParams) {
console.log("[Smart Auto Mode] 🎯 Effective image params:", {
@ -1564,7 +1598,11 @@ export async function POST(request: Request) {
// 调用 doGenerate 获取图片(如果是备用模型,传入 overrideModelId)
const result = await (languageModel as any).doGenerate({
messages: modelMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K" },
imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
},
overrideModelId: isRetry ? modelIdToUse : undefined, // 🆕 使用备用模型 ID
});
@ -1930,7 +1968,12 @@ export async function POST(request: Request) {
// 调用 doGenerate 获取图片,传递模型显示名称
const result = await (languageModel as any).doGenerate({
messages: modelMessages,
imageParams: effectiveImageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 },
imageParams:
effectiveImageParams || {
aspectRatio: defaultImageQualityParams.aspectRatio,
imageSize: defaultImageQualityParams.imageSize,
imageCount: defaultImageQualityParams.imageCount,
},
modelDisplayName, // 🆕 传递模型显示名称
overrideModelId: isRetry ? modelIdToUse : undefined,
});
@ -2028,7 +2071,12 @@ export async function POST(request: Request) {
const candidateResult = await (candidateLanguageModel as any).doGenerate({
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,
overrideModelId: candidate.model.modelId,
});
@ -2274,7 +2322,12 @@ export async function POST(request: Request) {
const tryGenerateImage = async (modelIdToUse: string, isRetry: boolean) => {
const result = await (languageModel as any).doGenerate({
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,
});
if (result?.finishReason === "error") {
@ -2366,7 +2419,12 @@ export async function POST(request: Request) {
const candidateResult = await (candidateLanguageModel as any).doGenerate({
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,
overrideModelId: candidate.model.modelId,
});

View File

@ -171,7 +171,7 @@ export function ImagePreview({
};
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 previewSrcSet = original
? `${thumbnail} 1x, ${original} 2x`