diff --git a/web/app/components/base/chat/chat/answer/index.tsx b/web/app/components/base/chat/chat/answer/index.tsx index 9663512e53..b49f4a8a50 100644 --- a/web/app/components/base/chat/chat/answer/index.tsx +++ b/web/app/components/base/chat/chat/answer/index.tsx @@ -162,14 +162,14 @@ const Answer: FC = ({ ) } { - responding && contentIsEmpty && !hasAgentThoughts && ( + responding && contentIsEmpty && !hasAgentThoughts && !toolCalls?.length && (
) } { - !contentIsEmpty && !hasAgentThoughts && ( + !contentIsEmpty && !hasAgentThoughts && !toolCalls?.length && ( ) } diff --git a/web/app/components/base/chat/chat/answer/tool-calls/index.tsx b/web/app/components/base/chat/chat/answer/tool-calls/index.tsx index 66118006fe..fea7d4c50f 100644 --- a/web/app/components/base/chat/chat/answer/tool-calls/index.tsx +++ b/web/app/components/base/chat/chat/answer/tool-calls/index.tsx @@ -1,4 +1,5 @@ import type { ToolCallItem } from '@/types/workflow' +import { Markdown } from '@/app/components/base/markdown' import ToolCallItemComponent from '@/app/components/workflow/run/llm-log/tool-call-item' type ToolCallsProps = { @@ -9,13 +10,23 @@ const ToolCalls = ({ }: ToolCallsProps) => { return (
- {toolCalls.map((toolCall: ToolCallItem, index: number) => ( - - ))} + {toolCalls.map((toolCall: ToolCallItem, index: number) => { + if (toolCall.type === 'text') { + return ( + + ) + } + return ( + + ) + })}
) } diff --git a/web/app/components/base/chat/chat/hooks.ts b/web/app/components/base/chat/chat/hooks.ts index 56f2b44de5..253e5e818a 100644 --- a/web/app/components/base/chat/chat/hooks.ts +++ b/web/app/components/base/chat/chat/hooks.ts @@ -343,8 +343,27 @@ export const useChat = ( tool_elapsed_time, }: any) => { if (!isAgentMode) { - if (chunk_type === 'text') + if (chunk_type === 'text') { + // Append text to toolCalls array to preserve order with tool calls + if (!responseItem.toolCalls) + responseItem.toolCalls = [] + + const lastItem = responseItem.toolCalls.at(-1) + if (lastItem?.type === 'text') { + // Merge consecutive text chunks into the same text item + lastItem.textContent = (lastItem.textContent || '') + message + } + else { + // Create a new text item + responseItem.toolCalls.push({ + id: uuidV4(), + type: 'text', + textContent: message, + }) + } + // Also update content for compatibility responseItem.content = responseItem.content + message + } } else { const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1] diff --git a/web/app/components/base/chat/utils.ts b/web/app/components/base/chat/utils.ts index 958190e7b2..a3f63f05a8 100644 --- a/web/app/components/base/chat/utils.ts +++ b/web/app/components/base/chat/utils.ts @@ -253,6 +253,12 @@ const buildToolCallsFromHistorySequence = (message: ChatMessageRes): { case 'content': { const text = answer?.substring(segment.start, segment.end) if (text?.trim()) { + // Add text as a toolCall item to preserve order + toolCalls.push({ + id: uuidV4(), + type: 'text', + textContent: text, + }) answerMessage += text } break diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks/use-chat-message-sender.ts b/web/app/components/workflow/panel/debug-and-preview/hooks/use-chat-message-sender.ts index 163c0797af..5e245aac55 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks/use-chat-message-sender.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks/use-chat-message-sender.ts @@ -158,8 +158,27 @@ export function useChatMessageSender({ }) => { if (!isCurrentRun()) return - if (chunk_type === 'text') + if (chunk_type === 'text') { + // Append text to toolCalls array to preserve order with tool calls + if (!responseItem.toolCalls) + responseItem.toolCalls = [] + + const lastItem = responseItem.toolCalls.at(-1) + if (lastItem?.type === 'text') { + // Merge consecutive text chunks into the same text item + lastItem.textContent = (lastItem.textContent || '') + message + } + else { + // Create a new text item + responseItem.toolCalls.push({ + id: uuidV4(), + type: 'text', + textContent: message, + }) + } + // Also update content for compatibility responseItem.content = responseItem.content + message + } if (chunk_type === 'tool_call') { if (!responseItem.toolCalls) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 279a098195..51c6abd8ff 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -35,7 +35,7 @@ export type IconObject = { export type ToolCallItem = { id: string - type: 'model' | 'tool' | 'thought' + type: 'model' | 'tool' | 'thought' | 'text' thoughtCompleted?: boolean thoughtOutput?: string @@ -55,6 +55,9 @@ export type ToolCallItem = { modelDuration?: number modelIcon?: string | IconObject modelIconDark?: string | IconObject + + // text type fields + textContent?: string } export type ToolCallDetail = {