From b97e96d29eb17f8a239f406e8a2bfa394fe01b1d Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Mon, 23 Mar 2026 10:23:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E9=80=80=E5=87=BA=E6=8C=89=E9=92=AE=E6=B2=A1=E6=9C=89=E4=B8=AD?= =?UTF-8?q?=E6=96=AD=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9B=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E4=BA=86=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E5=90=8E=E8=BF=87=E6=BB=A4=E4=B8=80=E9=81=8Dmessage=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/workspace/chats/[thread_id]/page.tsx | 11 +++- frontend/src/core/messages/utils.ts | 60 ++++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 750bc1d6..e7bc36cf 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -504,10 +504,15 @@ export default function ChatPage() { diff --git a/frontend/src/core/messages/utils.ts b/frontend/src/core/messages/utils.ts index d73417db..92951ca6 100644 --- a/frontend/src/core/messages/utils.ts +++ b/frontend/src/core/messages/utils.ts @@ -33,9 +33,47 @@ export function groupMessages( if (messages.length === 0) { return []; } + + // 预处理:收集所有 ToolMessage 的 tool_call_id + const toolMessageIds = new Set(); + for (const message of messages) { + if (message.type === "tool" && message.tool_call_id) { + toolMessageIds.add(message.tool_call_id); + } + } + + // 预处理:检查哪些 tool_calls 没有对应的响应 + const danglingToolCallIds = new Set(); + for (const message of messages) { + if (message.type === "ai" && message.tool_calls) { + for (const tc of message.tool_calls) { + const tcId = tc.id; + if (tcId && !toolMessageIds.has(tcId)) { + danglingToolCallIds.add(tcId); + } + } + } + } + + // 过滤掉只有悬空 tool_calls 且没有其他内容的 AI 消息 + const filteredMessages = messages.filter((message) => { + if (message.type === "ai" && hasToolCalls(message)) { + // 检查是否所有 tool_calls 都是悬空的 + const allDangling = message.tool_calls?.every((tc) => + danglingToolCallIds.has(tc.id!), + ); + // 如果全部悬空且没有其他内容,跳过该消息 + if (allDangling && !hasReasoning(message) && !hasContent(message)) { + console.warn("过滤只有悬空 tool_calls 的 AI 消息:", message.id); + return false; + } + } + return true; + }); + const groups: MessageGroup[] = []; - for (const message of messages) { + for (const message of filteredMessages) { const lastGroup = groups[groups.length - 1]; if (message.type === "human") { groups.push({ @@ -44,9 +82,9 @@ export function groupMessages( messages: [message], }); } else if (message.type === "tool") { - // Check if this is a clarification tool message + // 检查是否为澄清问题的工具消息 if (isClarificationToolMessage(message)) { - // Add to processing group if available (to maintain tool call association) + // 如果有可用的处理组,添加到其中(保持工具调用关联) if ( lastGroup && lastGroup.type !== "human" && @@ -55,7 +93,7 @@ export function groupMessages( ) { lastGroup.messages.push(message); } - // Also create a separate clarification group for prominent display + // 同时创建单独的澄清组以便突出显示 groups.push({ id: message.id, type: "assistant:clarification", @@ -69,9 +107,17 @@ export function groupMessages( ) { lastGroup.messages.push(message); } else { - throw new Error( - "Tool message must be matched with a previous assistant message with tool calls", + // 悬空的工具消息(如生成被中断导致) + // 创建独立的处理组以便显示 + console.warn( + "检测到悬空的工具消息,创建独立组:", + message.tool_call_id, ); + groups.push({ + id: message.id, + type: "assistant:processing", + messages: [message], + }); } } else if (message.type === "ai") { if (hasReasoning(message) || hasToolCalls(message)) { @@ -100,7 +146,7 @@ export function groupMessages( currentGroup.messages.push(message); } else { throw new Error( - "Assistant message with reasoning or tool calls must be preceded by a processing group", + "带有推理或工具调用的 AI 消息必须位于处理组之后", ); } }