Commit fb38d14f by Archer Committed by GitHub

fix: reason (#6963)

parent 05254ce0
......@@ -142,9 +142,12 @@ export const mergeAssistantFieldMessages = (messages: ChatCompletionMessageParam
): message is AssistantToolCallMessage => {
if (message?.role !== ChatCompletionRequestMessageRoleEnum.Assistant) return false;
const hasNoAssistantText =
message.content === undefined || message.content === null || message.content === '';
return (
hasAssistantToolCalls(message) &&
typeof message.content !== 'string' &&
hasNoAssistantText &&
typeof message.reasoning_content !== 'string'
);
};
......
......@@ -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', () => {
const messages: ChatCompletionMessageParam[] = [
{
......@@ -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', () => {
const messages: ChatItemMiniType[] = [
{
......
......@@ -192,10 +192,16 @@ export const createWorkflowAgentLoopEventMapper = ({
assistantText?: string;
reasoningText?: string;
}) => {
// 没有可见 assistantText 时,不在 request end 阶段回填 reasoning 到已有 tool。
// reasoning 的归属需要按流式顺序附着到后续 content/tool,避免误挂到上一轮工具。
if (!assistantText) return;
/*
* 只处理“本轮 LLM 结束于工具调用”的 assistant 内容归属。
*
* 例子:
* 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 functionName = call.function.name;
return (
......@@ -211,6 +217,22 @@ export const createWorkflowAgentLoopEventMapper = ({
const existingIndex = assistantResponses.findIndex((item) =>
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 assistantValue: AIChatItemValueItemType = {
......
......@@ -36,6 +36,22 @@ const toolResponse = ({ id, name, response }: { id: string; name: string; respon
seconds: 0.1
}) 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', () => {
it('streams main answer deltas', () => {
const workflowStreamResponse = vi.fn();
......@@ -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', () => {
const workflowStreamResponse = vi.fn();
const mapper = createWorkflowAgentLoopEventMapper({
......
......@@ -31,7 +31,7 @@
"date-fns": "catalog:",
"dayjs": "catalog:",
"i18next": "catalog:",
"js-cookie": "^3.0.5",
"js-cookie": "^3.0.7",
"lexical": "0.12.6",
"lodash": "catalog:",
"next": "catalog:",
......@@ -52,7 +52,7 @@
"zustand": "^4.3.5"
},
"devDependencies": {
"@types/js-cookie": "^3.0.5",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "catalog:",
"@types/papaparse": "^5.3.7",
"@types/react": "catalog:",
......
......@@ -278,13 +278,13 @@ importers:
version: 11.3.4
fumadocs-core:
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:
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:
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:
specifier: ^4.0.3
version: 4.0.3
......@@ -293,7 +293,7 @@ importers:
version: 0.525.0(react@18.3.1)
next:
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:
specifier: ^18
version: 18.3.1
......@@ -327,7 +327,7 @@ importers:
version: 0.15.0(typescript@5.9.3)
'@content-collections/next':
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':
specifier: ^4.1.11
version: 4.2.4
......@@ -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)
nextjs-cors:
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:
specifier: ^3.0.3
version: 3.0.3
......@@ -751,8 +751,8 @@ importers:
specifier: 'catalog:'
version: 23.16.8
js-cookie:
specifier: ^3.0.5
version: 3.0.5
specifier: ^3.0.7
version: 3.0.7
lexical:
specifier: 0.12.6
version: 0.12.6
......@@ -809,7 +809,7 @@ importers:
version: 4.5.6(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)
devDependencies:
'@types/js-cookie':
specifier: ^3.0.5
specifier: ^3.0.6
version: 3.0.6
'@types/lodash':
specifier: 'catalog:'
......@@ -10351,9 +10351,9 @@ packages:
js-base64@3.7.8:
resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==}
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
js-cookie@3.0.7:
resolution: {integrity: sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==}
engines: {node: '>=20'}
js-tokens@10.0.0:
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
......@@ -16975,11 +16975,11 @@ snapshots:
dependencies:
'@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:
'@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':
dependencies:
......@@ -21861,7 +21861,7 @@ snapshots:
'@types/js-cookie': 3.0.6
dayjs: 1.11.19
intersection-observer: 0.12.2
js-cookie: 3.0.5
js-cookie: 3.0.7
lodash: 4.17.23
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
......@@ -24653,7 +24653,7 @@ snapshots:
xregexp: 2.0.0
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:
'@formatjs/intl-localematcher': 0.6.2
'@orama/orama': 3.1.18
......@@ -24674,20 +24674,20 @@ snapshots:
unist-util-visit: 5.0.0
optionalDependencies:
'@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-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
- 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:
'@mdx-js/mdx': 3.1.1
'@standard-schema/spec': 1.1.0
chokidar: 4.0.3
esbuild: 0.25.11
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
lru-cache: 11.2.7
picocolors: 1.1.1
......@@ -24696,12 +24696,12 @@ snapshots:
unist-util-visit: 5.0.0
zod: 4.1.12
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)
transitivePeerDependencies:
- 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:
'@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)
......@@ -24714,7 +24714,7 @@ snapshots:
'@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)
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
next-themes: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
postcss-selector-parser: 7.1.1
......@@ -24725,7 +24725,7 @@ snapshots:
tailwind-merge: 3.5.0
optionalDependencies:
'@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
transitivePeerDependencies:
- '@oramacloud/client'
......@@ -25844,7 +25844,7 @@ snapshots:
js-base64@3.7.8: {}
js-cookie@3.0.5: {}
js-cookie@3.0.7: {}
js-tokens@10.0.0: {}
......@@ -27467,7 +27467,7 @@ snapshots:
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:
'@next/env': 15.5.18
'@swc/helpers': 0.5.15
......@@ -27518,7 +27518,7 @@ snapshots:
- '@babel/core'
- 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:
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)
......@@ -36,6 +36,25 @@ import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
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 = {
[ChatStatusEnum.loading]: {
bg: 'myGray.100',
......@@ -217,18 +236,7 @@ const ChatItem = (props: Props) => {
if (chat.obj === ChatRoleEnum.AI) {
// Remove empty text node
const filterList = chat.value.filter((item, i) => {
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 filterList = chat.value.filter((item) => !shouldFilterAiValue(item));
const groupedValues: 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