修复连续对话误用缩略图导致画质下降

This commit is contained in:
Your Name 2026-02-08 23:10:58 +08:00
parent 75993d98af
commit 35d50973db

View File

@ -367,6 +367,86 @@ async function convertMessagesWithImages(messages: ChatMessage[], providerType:
return convertedMessages;
}
function parseImageAltMetadata(altText: string, thumbnailUrl: string): {
originalUrl: string;
imageSize: string;
} {
let originalUrl = thumbnailUrl;
let imageSize = "2K";
// 格式1: thumb|||originalUrl|||size (新格式)
if (altText.includes("thumb|||")) {
const content = altText.substring(altText.indexOf("thumb|||") + 8);
const parts = content.split("|||");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 格式2: thumb$$originalUrl$$size (中间格式,可能被 LaTeX 解析器破坏)
else if (altText.includes("thumb$$")) {
const content = altText.substring(altText.indexOf("thumb$$") + 7);
const parts = content.split("$$");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 格式3: thumb|originalUrl|size (旧格式)
else if (altText.includes("thumb|")) {
const content = altText.substring(altText.indexOf("thumb|") + 6);
const parts = content.split("|");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 回退策略:从缩略图 URL 推断原图 URL
if (originalUrl === thumbnailUrl) {
if (thumbnailUrl.includes("gemini-thumb-")) {
originalUrl = thumbnailUrl.replace("gemini-thumb-", "gemini-orig-");
} else if (thumbnailUrl.includes("openai-thumb-")) {
originalUrl = thumbnailUrl.replace("openai-thumb-", "openai-orig-");
} else if (thumbnailUrl.includes("volcano-thumb-")) {
originalUrl = thumbnailUrl.replace("volcano-thumb-", "volcano-orig-");
} else if (thumbnailUrl.includes("alibaba-image-thumb-")) {
originalUrl = thumbnailUrl.replace("alibaba-image-thumb-", "alibaba-image-orig-");
} else if (thumbnailUrl.includes("ruxa-image-thumb-")) {
originalUrl = thumbnailUrl.replace("ruxa-image-thumb-", "ruxa-image-orig-");
}
}
return { originalUrl, imageSize };
}
function extractOriginalImageUrlsFromText(text: string): string[] {
const imageRegex = /!\[([^\]]*)\]\((https?:\/\/[^\s)]+)\)/g;
const imageUrls: string[] = [];
const added = new Set<string>();
let hasMarkdownImages = false;
let match: RegExpExecArray | null;
while ((match = imageRegex.exec(text)) !== null) {
hasMarkdownImages = true;
const altText = match[1] || "";
const thumbnailUrl = match[2];
const { originalUrl } = parseImageAltMetadata(altText, thumbnailUrl);
const finalUrl = originalUrl || thumbnailUrl;
if (finalUrl && /^https?:\/\//i.test(finalUrl) && !added.has(finalUrl)) {
added.add(finalUrl);
imageUrls.push(finalUrl);
}
}
if (!hasMarkdownImages) {
const urlRegex = /https?:\/\/[^\s"'<>]+\.(?:jpg|jpeg|png|gif|webp|bmp|svg)(?:\?[^\s"'<>]*)?/gi;
const matchedUrls = text.match(urlRegex) || [];
for (const url of matchedUrls) {
if (!added.has(url)) {
added.add(url);
imageUrls.push(url);
}
}
}
return imageUrls;
}
/**
* 解析生图 Markdown 并保存图片记录到数据库
* 支持三种格式:thumb|||url|||size、thumb$$url$$size、thumb|url|size
@ -386,48 +466,7 @@ async function saveGeneratedImages(
for (const match of matches) {
const altText = match[1] || "";
const thumbnailUrl = match[2];
let originalUrl = thumbnailUrl;
let imageSize = "2K";
// 解析 alt 中的原图 URL 和尺寸
// 格式1: thumb|||originalUrl|||size (新格式)
if (altText.includes("thumb|||")) {
const content = altText.substring(altText.indexOf("thumb|||") + 8);
const parts = content.split("|||");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 格式2: thumb$$originalUrl$$size (中间格式,可能被 LaTeX 解析器破坏)
else if (altText.includes("thumb$$")) {
const content = altText.substring(altText.indexOf("thumb$$") + 7);
const parts = content.split("$$");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 格式3: thumb|originalUrl|size (旧格式)
else if (altText.includes("thumb|")) {
const content = altText.substring(altText.indexOf("thumb|") + 6);
const parts = content.split("|");
originalUrl = parts[0] || thumbnailUrl;
imageSize = parts[1] || "2K";
}
// 回退策略:从缩略图 URL 推断原图 URL
if (originalUrl === thumbnailUrl) {
if (thumbnailUrl.includes("gemini-thumb-")) {
originalUrl = thumbnailUrl.replace("gemini-thumb-", "gemini-orig-");
} else if (thumbnailUrl.includes("openai-thumb-")) {
originalUrl = thumbnailUrl.replace("openai-thumb-", "openai-orig-");
} else if (thumbnailUrl.includes("volcano-thumb-")) {
originalUrl = thumbnailUrl.replace("volcano-thumb-", "volcano-orig-");
} else if (thumbnailUrl.includes("alibaba-image-thumb-")) {
// 🆕 阿里生图模型
originalUrl = thumbnailUrl.replace("alibaba-image-thumb-", "alibaba-image-orig-");
} else if (thumbnailUrl.includes("ruxa-image-thumb-")) {
// 🆕 RUXA 生图模型
originalUrl = thumbnailUrl.replace("ruxa-image-thumb-", "ruxa-image-orig-");
}
}
const { originalUrl, imageSize } = parseImageAltMetadata(altText, thumbnailUrl);
try {
await db.insert(generatedImage).values({
@ -595,11 +634,11 @@ export async function POST(request: Request) {
if (msg.role === "assistant") {
for (const part of parts) {
if (part.type === "text" && part.text) {
// 匹配所有Markdown图片格式: ![alt](url)
const imgMatches = part.text.matchAll(/!\[.*?\]\((https?:\/\/[^\s)]+)\)/g);
for (const match of imgMatches) {
if (match[1] && !smartAutoHistoryImages.includes(match[1])) {
smartAutoHistoryImages.push(match[1]);
// 从 markdown 中优先提取原图 URL,避免把缩略图当作下一轮输入导致画质下降
const parsedImageUrls = extractOriginalImageUrlsFromText(part.text);
for (const parsedUrl of parsedImageUrls) {
if (!smartAutoHistoryImages.includes(parsedUrl)) {
smartAutoHistoryImages.push(parsedUrl);
}
}
}