From 9e69e9e1fe9452549d1149c2c5114c8d016114ca Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 21 Feb 2026 04:30:23 +0800 Subject: [PATCH] fix(chat): always prepend image model badge in result message --- app/(chat)/api/chat/route.ts | 93 ++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 89fa8bac1..018ace80d 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1437,6 +1437,34 @@ async function normalizeAssistantImageMarkdown( return normalizedText; } +function prependImageModelBadge(text: string, modelDisplayName?: string | null): string { + const normalizedText = String(text || "").trim(); + if (!normalizedText) return normalizedText; + + const normalizedModelName = String(modelDisplayName || "").trim(); + if (!normalizedModelName) return normalizedText; + + if ( + /^\s*[◈◇◆🔹⬡✦•·]\s*[^\n]+/im.test(normalizedText) || + /^\s*(?:Routed to|Using model|使用模型|模型)\s*[::]/im.test(normalizedText) + ) { + return normalizedText; + } + + return `◈ ${normalizedModelName}\n\n${normalizedText}`; +} + +function resolveModelDisplayNameByModelId( + models: Awaited> | undefined, + modelId: string | undefined, +): string { + if (!modelId || !models?.length) return ""; + const match = models.find( + (item) => item.model.modelId === modelId || item.model.id === modelId, + ); + return String(match?.model?.displayName || ""); +} + /** * 解析生图 Markdown 并保存图片记录到数据库 * 支持三种格式:thumb|||url|||size、thumb$$url$$size、thumb|url|size @@ -2969,6 +2997,15 @@ export async function POST(request: Request) { console.log("[Chat API] Gemini generated, length:", imageMarkdown.length); console.log("[Chat API] Gemini image markdown:", imageMarkdown.substring(0, 200)); console.log("[Chat API] Used model ID:", usedModelId); + + const usedModelDisplayName = + resolveModelDisplayNameByModelId(allModels, usedModelId) || + currentModelConfig?.displayName || + actualModelId; + const imageMarkdownWithModel = prependImageModelBadge( + imageMarkdown, + usedModelDisplayName, + ); // 生成一个统一的消息 ID const messageId = generateUUID(); @@ -2976,7 +3013,7 @@ export async function POST(request: Request) { // 写入消息到流 - 使用正确的 parts 格式 writeAssistantTextChunk( writer, - imageMarkdown, + imageMarkdownWithModel, "gemini:image-result", messageId, ); @@ -2996,7 +3033,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: imageMarkdownWithModel }], attachments: [], // 必须提供 attachments 字段(数据库 notNull) createdAt: new Date(), videoStorageStatus: null, @@ -3015,7 +3052,7 @@ export async function POST(request: Request) { // 保存图片记录到 GeneratedImage 表 await saveGeneratedImages( - imageMarkdown, + imageMarkdownWithModel, session.user.id, id, messageId, @@ -3068,6 +3105,10 @@ export async function POST(request: Request) { }); const imageMarkdown = volcanoResult.text || "图片生成失败"; + const imageMarkdownWithModel = prependImageModelBadge( + imageMarkdown, + volcanoModel.model.displayName || volcanoModel.model.modelId, + ); console.log("[Chat API] Volcano Vision failover success, length:", imageMarkdown.length); // 生成消息 ID @@ -3076,7 +3117,7 @@ export async function POST(request: Request) { // 写入消息到流 writeAssistantTextChunk( writer, - imageMarkdown, + imageMarkdownWithModel, "gemini:volcano-failover-result", messageId, ); @@ -3096,7 +3137,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: imageMarkdownWithModel }], attachments: [], createdAt: new Date(), videoStorageStatus: null, @@ -3115,7 +3156,7 @@ export async function POST(request: Request) { // 保存图片记录到 GeneratedImage 表 await saveGeneratedImages( - imageMarkdown, + imageMarkdownWithModel, session.user.id, id, messageId, @@ -3415,6 +3456,15 @@ export async function POST(request: Request) { console.log("[Chat API] Alibaba generated, length:", imageMarkdown.length); console.log("[Chat API] Alibaba used model:", usedModelId); + + const usedModelDisplayName = + resolveModelDisplayNameByModelId(allModels, usedModelId) || + alibabaModelConfig?.displayName || + actualModelId; + const imageMarkdownWithModel = prependImageModelBadge( + imageMarkdown, + usedModelDisplayName, + ); // 生成消息 ID(图片消息) const messageId = generateUUID(); @@ -3422,7 +3472,7 @@ export async function POST(request: Request) { // 写入图片消息到流 writeAssistantTextChunk( writer, - imageMarkdown, + imageMarkdownWithModel, "alibaba:image-result", messageId, ); @@ -3442,7 +3492,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: imageMarkdownWithModel }], attachments: [], createdAt: new Date(), videoStorageStatus: null, @@ -3460,7 +3510,7 @@ export async function POST(request: Request) { // 保存图片记录到 GeneratedImage 表 await saveGeneratedImages( - imageMarkdown, + imageMarkdownWithModel, session.user.id, id, messageId, @@ -3747,6 +3797,15 @@ export async function POST(request: Request) { } console.log("[Chat API] Volcano Vision generated, length:", imageMarkdown.length); console.log("[Chat API] Volcano Vision image markdown:", imageMarkdown.substring(0, 200)); + + const usedModelDisplayName = + resolveModelDisplayNameByModelId(allModels, usedModelId) || + volcanoModelConfig?.displayName || + actualModelId; + const imageMarkdownWithModel = prependImageModelBadge( + imageMarkdown, + usedModelDisplayName, + ); // 生成消息 ID(图片消息) const messageId = generateUUID(); @@ -3754,7 +3813,7 @@ export async function POST(request: Request) { // 写入图片消息到流 writeAssistantTextChunk( writer, - imageMarkdown, + imageMarkdownWithModel, "volcano:image-result", messageId, ); @@ -3774,7 +3833,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: imageMarkdownWithModel }], attachments: [], // 必须提供 attachments 字段(数据库 notNull) createdAt: new Date(), videoStorageStatus: null, @@ -3793,7 +3852,7 @@ export async function POST(request: Request) { // 保存图片记录到 GeneratedImage 表 await saveGeneratedImages( - imageMarkdown, + imageMarkdownWithModel, session.user.id, id, messageId, @@ -4096,6 +4155,10 @@ export async function POST(request: Request) { rawText, effectiveImageParams?.imageSize || "2K" ); + const normalizedTextWithModel = prependImageModelBadge( + normalizedText, + currentModelConfig?.displayName || actualModelId, + ); const messageId = generateUUID(); dataStream.write({ @@ -4103,7 +4166,7 @@ export async function POST(request: Request) { data: JSON.stringify({ id: messageId, role: "assistant", - parts: [{ type: "text", text: normalizedText }], + parts: [{ type: "text", text: normalizedTextWithModel }], }), }); @@ -4122,7 +4185,7 @@ export async function POST(request: Request) { { id: messageId, role: "assistant", - parts: [{ type: "text", text: normalizedText }], + parts: [{ type: "text", text: normalizedTextWithModel }], createdAt: new Date(), attachments: [], chatId: id, @@ -4141,7 +4204,7 @@ export async function POST(request: Request) { (p): p is { type: "text"; text: string } => p.type === "text" )?.text || ""; await saveGeneratedImages( - normalizedText, + normalizedTextWithModel, session.user.id, id, messageId,