From 73d1ab7912398e7a060b1227d022e7062af3984b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Feb 2026 01:56:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=99=BA=E8=83=BD=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=AF=B9=E8=AF=9D=E5=92=8C=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复流程: 1. 用户发送请求 2. 显示占位图 3. 图片生成后显示(对话文字 + 图片) - 将对话消息和图片合并成一条消息 - 对话文字在前,图片在后 - 占位图显示到图片出现为止 Co-authored-by: Cursor --- app/(chat)/api/chat/route.ts | 90 ++++++++---------------------------- components/messages.tsx | 10 ++-- 2 files changed, 26 insertions(+), 74 deletions(-) diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index aac60b8ad..88dd8eda6 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1032,36 +1032,6 @@ export async function POST(request: Request) { }); } - // 🆕 如果是智能模式且有对话回复,先发送对话消息 - if (smartAutoFriendlyMessage) { - const chatMessageId = generateUUID(); - console.log("[Chat API] 💬 Sending conversational reply first:", smartAutoFriendlyMessage); - - // 写入对话消息到流 - writer.write({ - type: "data-appendMessage", - data: JSON.stringify({ - id: chatMessageId, - role: "assistant", - parts: [{ type: "text", text: smartAutoFriendlyMessage }], - }), - }); - - // 保存对话消息到数据库 - await saveMessages({ - messages: [ - { - id: chatMessageId, - chatId: id, - role: "assistant", - parts: [{ type: "text", text: smartAutoFriendlyMessage }], - attachments: [], - createdAt: new Date(), - }, - ], - }); - } - // 转换消息(阿里生图支持 URL,会自动上传 base64) const modelMessages = await convertMessagesWithImages(messagesForAlibaba, "alibaba-image"); @@ -1075,20 +1045,25 @@ export async function POST(request: Request) { const imageMarkdown = result.text || "图片生成失败"; console.log("[Chat API] Alibaba generated, length:", imageMarkdown.length); - // 生成消息 ID(图片消息) + // 生成消息 ID const messageId = generateUUID(); - // 写入图片消息到流 + // 合并对话和图片的完整内容 + const fullContent = smartAutoFriendlyMessage + ? `${smartAutoFriendlyMessage}\n\n${imageMarkdown}` + : imageMarkdown; + + // 写入完整消息到流 writer.write({ type: "data-appendMessage", data: JSON.stringify({ id: messageId, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: fullContent }], }), }); - // 保存图片消息到数据库 + // 保存完整消息到数据库 try { await saveMessages({ messages: [ @@ -1096,7 +1071,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: fullContent }], attachments: [], createdAt: new Date(), videoStorageStatus: null, @@ -1200,36 +1175,6 @@ export async function POST(request: Request) { const volcanoStream = createUIMessageStream({ execute: async ({ writer }) => { try { - // 🆕 如果是智能模式且有对话回复,先发送对话消息 - if (smartAutoFriendlyMessage) { - const chatMessageId = generateUUID(); - console.log("[Chat API] 💬 Volcano: Sending conversational reply first:", smartAutoFriendlyMessage); - - // 写入对话消息到流 - writer.write({ - type: "data-appendMessage", - data: JSON.stringify({ - id: chatMessageId, - role: "assistant", - parts: [{ type: "text", text: smartAutoFriendlyMessage }], - }), - }); - - // 保存对话消息到数据库 - await saveMessages({ - messages: [ - { - id: chatMessageId, - chatId: id, - role: "assistant", - parts: [{ type: "text", text: smartAutoFriendlyMessage }], - attachments: [], - createdAt: new Date(), - }, - ], - }); - } - // 转换消息,火山引擎支持 URL 和 base64 const modelMessages = await convertMessagesWithImages(messagesForVolcano, "volcano"); @@ -1242,20 +1187,25 @@ 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)); - // 生成一个统一的消息 ID(图片消息) + // 生成消息 ID const messageId = generateUUID(); - // 写入图片消息到流 + // 合并对话和图片的完整内容 + const fullContent = smartAutoFriendlyMessage + ? `${smartAutoFriendlyMessage}\n\n${imageMarkdown}` + : imageMarkdown; + + // 写入完整消息到流 writer.write({ type: "data-appendMessage", data: JSON.stringify({ id: messageId, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: fullContent }], }), }); - // 保存图片消息到数据库 + // 保存完整消息到数据库 try { await saveMessages({ messages: [ @@ -1263,7 +1213,7 @@ export async function POST(request: Request) { id: messageId, chatId: id, role: "assistant", - parts: [{ type: "text", text: imageMarkdown }], + parts: [{ type: "text", text: fullContent }], attachments: [], // 必须提供 attachments 字段(数据库 notNull) createdAt: new Date(), videoStorageStatus: null, diff --git a/components/messages.tsx b/components/messages.tsx index 305731deb..a880bcc99 100644 --- a/components/messages.tsx +++ b/components/messages.tsx @@ -169,16 +169,18 @@ function PureMessages({ // 判断是否是图片生成模型(包括 Gemini、火山、阿里) const isImageGenModel = isGeminiModel || isVolcanoVisionModel || isAlibabaImageModel; - // 🆕 智能自动模式:检查用户消息是否包含图片,如果有则视为图片编辑场景 - const isAutoModeWithImage = selectedModelId === "auto" && lastMessage?.role === "user" && - lastMessage.parts?.some((p: any) => p.type === "file" || p.type === "image"); + // 🆕 智能自动模式:检查用户最后一条消息是否包含图片,如果有则视为图片编辑场景 + // 注意:需要找用户的最后一条消息,而不是整个对话的最后一条 + const lastUserMessage = [...messages].reverse().find(m => m.role === "user"); + const isAutoModeWithImage = selectedModelId === "auto" && + lastUserMessage?.parts?.some((p: any) => p.type === "file" || p.type === "image"); // 根据模型类型判断是否有有效内容 let hasContent = false; if (isQianwenVideoModel || isVideoModel) { hasContent = hasVideoContent ?? false; // 视频模型:只有真正的视频才算有内容 } else if (isImageGenModel || isAutoModeWithImage) { - hasContent = hasImageContent ?? false; // 图片模型或智能自动+图片:只有图片才算有内容 + hasContent = hasImageContent ?? false; // 图片模型或智能自动+图片:只有图片才算有内容(文字不算) } else { hasContent = hasTextContent ?? false; // 其他模型:任何文本都算有内容 }