Commit fb38d14f by Archer Committed by GitHub

fix: reason (#6963)

parent 05254ce0
...@@ -142,9 +142,12 @@ export const mergeAssistantFieldMessages = (messages: ChatCompletionMessageParam ...@@ -142,9 +142,12 @@ export const mergeAssistantFieldMessages = (messages: ChatCompletionMessageParam
): message is AssistantToolCallMessage => { ): message is AssistantToolCallMessage => {
if (message?.role !== ChatCompletionRequestMessageRoleEnum.Assistant) return false; if (message?.role !== ChatCompletionRequestMessageRoleEnum.Assistant) return false;
const hasNoAssistantText =
message.content === undefined || message.content === null || message.content === '';
return ( return (
hasAssistantToolCalls(message) && hasAssistantToolCalls(message) &&
typeof message.content !== 'string' && hasNoAssistantText &&
typeof message.reasoning_content !== 'string' typeof message.reasoning_content !== 'string'
); );
}; };
......
...@@ -353,6 +353,56 @@ describe('mergeAssistantFieldMessages', () => { ...@@ -353,6 +353,56 @@ describe('mergeAssistantFieldMessages', () => {
]); ]);
}); });
it('should treat empty assistant content on tool call messages as no answer text', () => {
const messages: ChatCompletionMessageParam[] = [
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
reasoning_content: 'Need to call a tool.'
},
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: '',
tool_calls: [
{
id: 'call_search',
type: 'function',
function: {
name: 'search_web',
arguments: '{}'
}
}
]
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_search',
content: 'compressed result'
}
];
expect(mergeAssistantFieldMessages(messages)).toEqual([
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
reasoning_content: 'Need to call a tool.',
tool_calls: [
{
id: 'call_search',
type: 'function',
function: {
name: 'search_web',
arguments: '{}'
}
}
]
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_search',
content: 'compressed result'
}
]);
});
it('should merge consecutive tool call groups into one assistant message', () => { it('should merge consecutive tool call groups into one assistant message', () => {
const messages: ChatCompletionMessageParam[] = [ const messages: ChatCompletionMessageParam[] = [
{ {
...@@ -1171,6 +1221,61 @@ describe('chats2GPTMessages', () => { ...@@ -1171,6 +1221,61 @@ describe('chats2GPTMessages', () => {
]); ]);
}); });
it('should not skip tool calls when the same value also has empty text', () => {
const messages: ChatItemMiniType[] = [
{
obj: ChatRoleEnum.AI,
value: [
{
text: {
content: ''
},
tools: [
{
id: 'call_search',
toolName: 'Search',
toolAvatar: '',
functionName: 'search_web',
params: '{}',
response: 'compressed result'
}
]
},
{
text: {
content: 'Final answer'
}
}
]
}
];
expect(chats2GPTMessages({ messages, reserveId: false, reserveTool: true })).toEqual([
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls: [
{
id: 'call_search',
type: 'function',
function: {
name: 'search_web',
arguments: '{}'
}
}
]
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_search',
content: 'compressed result'
},
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: 'Final answer'
}
]);
});
it('should skip empty AI text values when there are multiple values', () => { it('should skip empty AI text values when there are multiple values', () => {
const messages: ChatItemMiniType[] = [ const messages: ChatItemMiniType[] = [
{ {
......
...@@ -192,10 +192,16 @@ export const createWorkflowAgentLoopEventMapper = ({ ...@@ -192,10 +192,16 @@ export const createWorkflowAgentLoopEventMapper = ({
assistantText?: string; assistantText?: string;
reasoningText?: string; reasoningText?: string;
}) => { }) => {
// 没有可见 assistantText 时,不在 request end 阶段回填 reasoning 到已有 tool。 /*
// reasoning 的归属需要按流式顺序附着到后续 content/tool,避免误挂到上一轮工具。 * 只处理“本轮 LLM 结束于工具调用”的 assistant 内容归属。
if (!assistantText) return; *
* 例子:
* request 1: reasoningText="需要先查时间", assistantText=undefined, toolCalls=[call_time]
* request 2: reasoningText="工具返回了时间", assistantText="现在是 10 点"
*
* request 1 没有可见回答,但 reasoning 仍然属于 call_time 前的 assistant turn。
* 如果不挂到对应 tools value 上,刷新后/下一轮上下文会只剩 tool,丢失第一段思考。
*/
const runtimeToolCalls = toolCalls.filter((call) => { const runtimeToolCalls = toolCalls.filter((call) => {
const functionName = call.function.name; const functionName = call.function.name;
return ( return (
...@@ -211,6 +217,22 @@ export const createWorkflowAgentLoopEventMapper = ({ ...@@ -211,6 +217,22 @@ export const createWorkflowAgentLoopEventMapper = ({
const existingIndex = assistantResponses.findIndex((item) => const existingIndex = assistantResponses.findIndex((item) =>
item.tools?.some((tool) => runtimeToolCallIds.has(tool.id)) item.tools?.some((tool) => runtimeToolCallIds.has(tool.id))
); );
if (!assistantText) {
// reason -> tool:没有 answerText 可单独插入时,把 reasoning 按 callId 挂到已创建的工具卡。
if (!reasoningText || existingIndex < 0) return;
const currentValue = assistantResponses[existingIndex];
assistantResponses[existingIndex] = {
...currentValue,
reasoning: {
content: [currentValue.reasoning?.content, reasoningText].filter(Boolean).join('\n\n')
},
...(!showReasoning ? { hideReason: true } : {})
};
return;
}
const insertIndex = existingIndex >= 0 ? existingIndex : assistantResponses.length; const insertIndex = existingIndex >= 0 ? existingIndex : assistantResponses.length;
const assistantValue: AIChatItemValueItemType = { const assistantValue: AIChatItemValueItemType = {
......
...@@ -36,6 +36,22 @@ const toolResponse = ({ id, name, response }: { id: string; name: string; respon ...@@ -36,6 +36,22 @@ const toolResponse = ({ id, name, response }: { id: string; name: string; respon
seconds: 0.1 seconds: 0.1
}) as const; }) as const;
const createToolCall = ({ id, name, args = '{}' }: { id: string; name: string; args?: string }) =>
({
id,
type: 'function',
function: {
name,
arguments: args
}
}) as const;
const toolCall = (params: { id: string; name: string; args?: string }) =>
({
type: 'tool_call',
call: createToolCall(params)
}) as const;
describe('createWorkflowAgentLoopEventMapper', () => { describe('createWorkflowAgentLoopEventMapper', () => {
it('streams main answer deltas', () => { it('streams main answer deltas', () => {
const workflowStreamResponse = vi.fn(); const workflowStreamResponse = vi.fn();
...@@ -172,6 +188,293 @@ describe('createWorkflowAgentLoopEventMapper', () => { ...@@ -172,6 +188,293 @@ describe('createWorkflowAgentLoopEventMapper', () => {
]); ]);
}); });
it('persists the first reasoning text on reasoning-only tool requests', () => {
const workflowStreamResponse = vi.fn();
const mapper = createWorkflowAgentLoopEventMapper({
workflowStreamResponse,
getSubAppInfo: (id) => ({
name: id,
avatar: '',
toolDescription: ''
}),
internalToolNames: new Set()
});
mapper.emitEvent({
type: 'reasoning_delta',
text: 'first reasoning'
});
mapper.emitEvent(toolCall({ id: 'call_time', name: 'get_time' }));
mapper.emitEvent({
type: 'llm_request_end',
requestIndex: 1,
modelName: 'GPT-4',
requestId: 'req_tool',
finishReason: 'tool_calls',
reasoningText: 'first reasoning',
toolCalls: [createToolCall({ id: 'call_time', name: 'get_time' })]
});
mapper.emitEvent({
...toolResponse({
id: 'call_time',
name: 'get_time',
response: '2026-05-22 10:00:00'
})
});
// Final answer values are appended by the agent dispatcher after the loop finishes.
mapper.assistantResponses.push({
text: {
content: '现在是 10 点。'
},
reasoning: {
content: 'second reasoning'
}
});
expect(mapper.assistantResponses).toEqual([
{
id: 'call_time',
reasoning: {
content: 'first reasoning'
},
tools: [
{
id: 'call_time',
toolName: 'get_time',
toolAvatar: '',
functionName: 'get_time',
params: '{}',
response: '2026-05-22 10:00:00'
}
]
},
{
text: {
content: '现在是 10 点。'
},
reasoning: {
content: 'second reasoning'
}
}
]);
const restoredMessages = chats2GPTMessages({
messages: [
{
obj: ChatRoleEnum.AI,
value: mapper.assistantResponses
}
],
reserveId: false,
reserveTool: true
});
expect(restoredMessages).toEqual([
{
dataId: undefined,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
reasoning_content: 'first reasoning',
tool_calls: [
{
id: 'call_time',
type: 'function',
function: {
name: 'get_time',
arguments: '{}'
}
}
]
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_time',
content: '2026-05-22 10:00:00'
},
{
dataId: undefined,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: '现在是 10 点。',
reasoning_content: 'second reasoning'
}
]);
});
it('persists hidden reasoning on reasoning-only tool requests', () => {
const workflowStreamResponse = vi.fn();
const mapper = createWorkflowAgentLoopEventMapper({
workflowStreamResponse,
getSubAppInfo: (id) => ({
name: id,
avatar: '',
toolDescription: ''
}),
internalToolNames: new Set(),
showReasoning: false
});
mapper.emitEvent({
type: 'reasoning_delta',
text: 'hidden first reasoning'
});
mapper.emitEvent(toolCall({ id: 'call_time', name: 'get_time' }));
mapper.emitEvent({
type: 'llm_request_end',
requestIndex: 1,
modelName: 'GPT-4',
requestId: 'req_tool',
finishReason: 'tool_calls',
reasoningText: 'hidden first reasoning',
toolCalls: [createToolCall({ id: 'call_time', name: 'get_time' })]
});
expect(workflowStreamResponse).toHaveBeenCalledTimes(1);
expect(workflowStreamResponse).toHaveBeenCalledWith({
id: 'call_time',
event: SseResponseEventEnum.toolCall,
data: {
tool: {
id: 'call_time',
toolName: 'get_time',
toolAvatar: '',
functionName: 'get_time',
params: '{}'
}
}
});
expect(mapper.assistantResponses).toEqual([
{
id: 'call_time',
reasoning: {
content: 'hidden first reasoning'
},
hideReason: true,
tools: [
{
id: 'call_time',
toolName: 'get_time',
toolAvatar: '',
functionName: 'get_time',
params: '{}'
}
]
}
]);
});
it('keeps reasoning-only parallel tool calls continuous when restoring messages', () => {
const mapper = createWorkflowAgentLoopEventMapper({
getSubAppInfo: (id) => ({
name: id,
avatar: '',
toolDescription: ''
}),
internalToolNames: new Set()
});
mapper.emitEvent(toolCall({ id: 'call_weather', name: 'weather', args: '{"city":"Beijing"}' }));
mapper.emitEvent(toolCall({ id: 'call_time', name: 'time' }));
mapper.emitEvent({
type: 'llm_request_end',
requestIndex: 1,
modelName: 'GPT-4',
requestId: 'req_parallel',
finishReason: 'tool_calls',
reasoningText: 'Need weather and time.',
toolCalls: [
createToolCall({ id: 'call_weather', name: 'weather', args: '{"city":"Beijing"}' }),
createToolCall({ id: 'call_time', name: 'time' })
]
});
mapper.emitEvent({
...toolResponse({
id: 'call_weather',
name: 'weather',
response: 'sunny'
})
});
mapper.emitEvent({
...toolResponse({
id: 'call_time',
name: 'time',
response: '10:00'
})
});
expect(mapper.assistantResponses).toEqual([
{
id: 'call_weather',
reasoning: {
content: 'Need weather and time.'
},
tools: [
expect.objectContaining({
id: 'call_weather',
functionName: 'weather',
response: 'sunny'
})
]
},
{
id: 'call_time',
tools: [
expect.objectContaining({
id: 'call_time',
functionName: 'time',
response: '10:00'
})
]
}
]);
const restoredMessages = chats2GPTMessages({
messages: [
{
obj: ChatRoleEnum.AI,
value: mapper.assistantResponses
}
],
reserveId: false,
reserveTool: true
});
expect(restoredMessages).toEqual([
{
dataId: undefined,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
reasoning_content: 'Need weather and time.',
tool_calls: [
{
id: 'call_weather',
type: 'function',
function: {
name: 'weather',
arguments: '{"city":"Beijing"}'
}
},
{
id: 'call_time',
type: 'function',
function: {
name: 'time',
arguments: '{}'
}
}
]
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_weather',
content: 'sunny'
},
{
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: 'call_time',
content: '10:00'
}
]);
});
it('filters internal tool calls and streams runtime tool lifecycle events', () => { it('filters internal tool calls and streams runtime tool lifecycle events', () => {
const workflowStreamResponse = vi.fn(); const workflowStreamResponse = vi.fn();
const mapper = createWorkflowAgentLoopEventMapper({ const mapper = createWorkflowAgentLoopEventMapper({
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
"date-fns": "catalog:", "date-fns": "catalog:",
"dayjs": "catalog:", "dayjs": "catalog:",
"i18next": "catalog:", "i18next": "catalog:",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.7",
"lexical": "0.12.6", "lexical": "0.12.6",
"lodash": "catalog:", "lodash": "catalog:",
"next": "catalog:", "next": "catalog:",
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
"zustand": "^4.3.5" "zustand": "^4.3.5"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.5", "@types/js-cookie": "^3.0.6",
"@types/lodash": "catalog:", "@types/lodash": "catalog:",
"@types/papaparse": "^5.3.7", "@types/papaparse": "^5.3.7",
"@types/react": "catalog:", "@types/react": "catalog:",
......
...@@ -278,13 +278,13 @@ importers: ...@@ -278,13 +278,13 @@ importers:
version: 11.3.4 version: 11.3.4
fumadocs-core: fumadocs-core:
specifier: 15.6.3 specifier: 15.6.3
version: 15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
fumadocs-mdx: fumadocs-mdx:
specifier: 11.6.11 specifier: 11.6.11
version: 11.6.11(fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(vite@6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4)) version: 11.6.11(fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(vite@6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4))
fumadocs-ui: fumadocs-ui:
specifier: 15.6.3 specifier: 15.6.3
version: 15.6.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@4.2.4) version: 15.6.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@4.2.4)
gray-matter: gray-matter:
specifier: ^4.0.3 specifier: ^4.0.3
version: 4.0.3 version: 4.0.3
...@@ -293,7 +293,7 @@ importers: ...@@ -293,7 +293,7 @@ importers:
version: 0.525.0(react@18.3.1) version: 0.525.0(react@18.3.1)
next: next:
specifier: ^15.5.18 specifier: ^15.5.18
version: 15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) version: 15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
react: react:
specifier: ^18 specifier: ^18
version: 18.3.1 version: 18.3.1
...@@ -327,7 +327,7 @@ importers: ...@@ -327,7 +327,7 @@ importers:
version: 0.15.0(typescript@5.9.3) version: 0.15.0(typescript@5.9.3)
'@content-collections/next': '@content-collections/next':
specifier: ^0.2.6 specifier: ^0.2.6
version: 0.2.11(@content-collections/core@0.15.0(typescript@5.9.3))(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)) version: 0.2.11(@content-collections/core@0.15.0(typescript@5.9.3))(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.1.11 specifier: ^4.1.11
version: 4.2.4 version: 4.2.4
...@@ -577,7 +577,7 @@ importers: ...@@ -577,7 +577,7 @@ importers:
version: 16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) version: 16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
nextjs-cors: nextjs-cors:
specifier: 2.2.1 specifier: 2.2.1
version: 2.2.1(next@16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)) version: 2.2.1(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))
node-cron: node-cron:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3 version: 3.0.3
...@@ -751,8 +751,8 @@ importers: ...@@ -751,8 +751,8 @@ importers:
specifier: 'catalog:' specifier: 'catalog:'
version: 23.16.8 version: 23.16.8
js-cookie: js-cookie:
specifier: ^3.0.5 specifier: ^3.0.7
version: 3.0.5 version: 3.0.7
lexical: lexical:
specifier: 0.12.6 specifier: 0.12.6
version: 0.12.6 version: 0.12.6
...@@ -809,7 +809,7 @@ importers: ...@@ -809,7 +809,7 @@ importers:
version: 4.5.6(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1) version: 4.5.6(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)
devDependencies: devDependencies:
'@types/js-cookie': '@types/js-cookie':
specifier: ^3.0.5 specifier: ^3.0.6
version: 3.0.6 version: 3.0.6
'@types/lodash': '@types/lodash':
specifier: 'catalog:' specifier: 'catalog:'
...@@ -10351,9 +10351,9 @@ packages: ...@@ -10351,9 +10351,9 @@ packages:
js-base64@3.7.8: js-base64@3.7.8:
resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==}
js-cookie@3.0.5: js-cookie@3.0.7:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} resolution: {integrity: sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==}
engines: {node: '>=14'} engines: {node: '>=20'}
js-tokens@10.0.0: js-tokens@10.0.0:
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
...@@ -16975,11 +16975,11 @@ snapshots: ...@@ -16975,11 +16975,11 @@ snapshots:
dependencies: dependencies:
'@content-collections/core': 0.15.0(typescript@5.9.3) '@content-collections/core': 0.15.0(typescript@5.9.3)
'@content-collections/next@0.2.11(@content-collections/core@0.15.0(typescript@5.9.3))(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))': '@content-collections/next@0.2.11(@content-collections/core@0.15.0(typescript@5.9.3))(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))':
dependencies: dependencies:
'@content-collections/core': 0.15.0(typescript@5.9.3) '@content-collections/core': 0.15.0(typescript@5.9.3)
'@content-collections/integrations': 0.5.0(@content-collections/core@0.15.0(typescript@5.9.3)) '@content-collections/integrations': 0.5.0(@content-collections/core@0.15.0(typescript@5.9.3))
next: 15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next: 15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
'@crawlee/basic@3.16.0': '@crawlee/basic@3.16.0':
dependencies: dependencies:
...@@ -21861,7 +21861,7 @@ snapshots: ...@@ -21861,7 +21861,7 @@ snapshots:
'@types/js-cookie': 3.0.6 '@types/js-cookie': 3.0.6
dayjs: 1.11.19 dayjs: 1.11.19
intersection-observer: 0.12.2 intersection-observer: 0.12.2
js-cookie: 3.0.5 js-cookie: 3.0.7
lodash: 4.17.23 lodash: 4.17.23
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
...@@ -24653,7 +24653,7 @@ snapshots: ...@@ -24653,7 +24653,7 @@ snapshots:
xregexp: 2.0.0 xregexp: 2.0.0
optional: true optional: true
fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@formatjs/intl-localematcher': 0.6.2 '@formatjs/intl-localematcher': 0.6.2
'@orama/orama': 3.1.18 '@orama/orama': 3.1.18
...@@ -24674,20 +24674,20 @@ snapshots: ...@@ -24674,20 +24674,20 @@ snapshots:
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
optionalDependencies: optionalDependencies:
'@types/react': 18.3.1 '@types/react': 18.3.1
next: 15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next: 15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
fumadocs-mdx@11.6.11(fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(vite@6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4)): fumadocs-mdx@11.6.11(fumadocs-core@15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(vite@6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4)):
dependencies: dependencies:
'@mdx-js/mdx': 3.1.1 '@mdx-js/mdx': 3.1.1
'@standard-schema/spec': 1.1.0 '@standard-schema/spec': 1.1.0
chokidar: 4.0.3 chokidar: 4.0.3
esbuild: 0.25.11 esbuild: 0.25.11
estree-util-value-to-estree: 3.5.0 estree-util-value-to-estree: 3.5.0
fumadocs-core: 15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fumadocs-core: 15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
js-yaml: 4.1.1 js-yaml: 4.1.1
lru-cache: 11.2.7 lru-cache: 11.2.7
picocolors: 1.1.1 picocolors: 1.1.1
...@@ -24696,12 +24696,12 @@ snapshots: ...@@ -24696,12 +24696,12 @@ snapshots:
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
zod: 4.1.12 zod: 4.1.12
optionalDependencies: optionalDependencies:
next: 15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next: 15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
vite: 6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4) vite: 6.2.2(@types/node@24.0.13)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.85.1)(terser@5.39.0)(tsx@4.20.6)(yaml@2.8.4)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
fumadocs-ui@15.6.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@4.2.4): fumadocs-ui@15.6.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@4.2.4):
dependencies: dependencies:
'@radix-ui/react-accordion': 1.2.12(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-accordion': 1.2.12(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...@@ -24714,7 +24714,7 @@ snapshots: ...@@ -24714,7 +24714,7 @@ snapshots:
'@radix-ui/react-slot': 1.2.4(@types/react@18.3.1)(react@18.3.1) '@radix-ui/react-slot': 1.2.4(@types/react@18.3.1)(react@18.3.1)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tabs': 1.1.13(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
class-variance-authority: 0.7.1 class-variance-authority: 0.7.1
fumadocs-core: 15.6.3(@types/react@18.3.1)(next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) fumadocs-core: 15.6.3(@types/react@18.3.1)(next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
lodash.merge: 4.6.2 lodash.merge: 4.6.2
next-themes: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
postcss-selector-parser: 7.1.1 postcss-selector-parser: 7.1.1
...@@ -24725,7 +24725,7 @@ snapshots: ...@@ -24725,7 +24725,7 @@ snapshots:
tailwind-merge: 3.5.0 tailwind-merge: 3.5.0
optionalDependencies: optionalDependencies:
'@types/react': 18.3.1 '@types/react': 18.3.1
next: 15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next: 15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
tailwindcss: 4.2.4 tailwindcss: 4.2.4
transitivePeerDependencies: transitivePeerDependencies:
- '@oramacloud/client' - '@oramacloud/client'
...@@ -25844,7 +25844,7 @@ snapshots: ...@@ -25844,7 +25844,7 @@ snapshots:
js-base64@3.7.8: {} js-base64@3.7.8: {}
js-cookie@3.0.5: {} js-cookie@3.0.7: {}
js-tokens@10.0.0: {} js-tokens@10.0.0: {}
...@@ -27467,7 +27467,7 @@ snapshots: ...@@ -27467,7 +27467,7 @@ snapshots:
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
next@15.5.18(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1): next@15.5.18(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1):
dependencies: dependencies:
'@next/env': 15.5.18 '@next/env': 15.5.18
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
...@@ -27518,7 +27518,7 @@ snapshots: ...@@ -27518,7 +27518,7 @@ snapshots:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros
nextjs-cors@2.2.1(next@16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)): nextjs-cors@2.2.1(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)):
dependencies: dependencies:
cors: 2.8.6 cors: 2.8.6
next: 16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next: 16.2.6(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
...@@ -36,6 +36,25 @@ import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance'; ...@@ -36,6 +36,25 @@ import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
const ResponseTags = dynamic(() => import('./ResponseTags')); const ResponseTags = dynamic(() => import('./ResponseTags'));
const shouldFilterAiValue = (item: AIChatItemValueItemType) => {
if (item.hideInUI) return true;
if (item.text?.content?.trim() || item.reasoning?.content?.trim()) return false;
if (!item.text && !item.reasoning) return false;
return !(
item.tools?.length ||
item.tool ||
item.skills?.length ||
item.interactive ||
item.plan ||
item.planStatus ||
item.agentPlanUpdate ||
item.agentAsk ||
item.agentStopGate ||
item.contextCheckpoint
);
};
const colorMap = { const colorMap = {
[ChatStatusEnum.loading]: { [ChatStatusEnum.loading]: {
bg: 'myGray.100', bg: 'myGray.100',
...@@ -217,18 +236,7 @@ const ChatItem = (props: Props) => { ...@@ -217,18 +236,7 @@ const ChatItem = (props: Props) => {
if (chat.obj === ChatRoleEnum.AI) { if (chat.obj === ChatRoleEnum.AI) {
// Remove empty text node // Remove empty text node
const filterList = chat.value.filter((item, i) => { const filterList = chat.value.filter((item) => !shouldFilterAiValue(item));
if (item.hideInUI) {
return false;
}
if (item.text && !item.text.content?.trim()) {
return false;
}
if (item.reasoning && !item.reasoning.content?.trim()) {
return false;
}
return item;
});
const groupedValues: AIChatItemValueItemType[][] = []; const groupedValues: AIChatItemValueItemType[][] = [];
let currentGroup: AIChatItemValueItemType[] = []; let currentGroup: AIChatItemValueItemType[] = [];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment