From 81dfc589f4c6ccb280299c6d497538f5f74ff535 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Fri, 24 Apr 2026 18:34:55 +0800 Subject: [PATCH] fix: messages adapt test (#6813) * doc * doc * doc --- .../docs/self-host/upgrading/4-14/41414.mdx | 4 +- document/data/doc-last-modified.json | 8 +- packages/global/core/chat/adapt.ts | 2 +- test/cases/global/core/chat/adapt.test.ts | 152 ++++++++++++++++++ 4 files changed, 160 insertions(+), 6 deletions(-) diff --git a/document/content/docs/self-host/upgrading/4-14/41414.mdx b/document/content/docs/self-host/upgrading/4-14/41414.mdx index 66fb75a3f6..c68660a85e 100644 --- a/document/content/docs/self-host/upgrading/4-14/41414.mdx +++ b/document/content/docs/self-host/upgrading/4-14/41414.mdx @@ -8,6 +8,7 @@ description: 'FastGPT V4.14.14 更新说明' ### 1. 更新镜像 tag - 更新 fastgpt-app(fastgpt 主服务) 镜像 tag: v4.14.14 +- 更新 fastgpt-pro(fastgpt 商业版) 镜像 tag: v4.14.14 ## 🐛 修复 @@ -16,4 +17,5 @@ description: 'FastGPT V4.14.14 更新说明' 1. 个人微信发布渠道,优化轮询策略(拉取与回复解耦),避免数据量超大时出现阻塞。 2. 新增环境变量 `WECHAT_CHANNEL_CONCURRENCY`(默认 1000)用于控制微信渠道 poll worker 并发数,建议 ≥ online channel 峰值。 -3. 完善内网地址检测。 \ No newline at end of file +3. 完善内网地址检测。 +4. 兼容 deepseek 工具调用+思考模式,避免接口出现 400 错误。 \ No newline at end of file diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 51d521cb5e..b1aa0d3c56 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -225,12 +225,12 @@ "document/content/docs/self-host/upgrading/4-14/41410.en.mdx": "2026-03-31T23:15:29+08:00", "document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-04-18T20:47:39+08:00", "document/content/docs/self-host/upgrading/4-14/41411.en.mdx": "2026-04-21T23:04:26+08:00", - "document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-20T20:18:35+08:00", + "document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-24T17:13:58+08:00", "document/content/docs/self-host/upgrading/4-14/41412.en.mdx": "2026-04-21T23:04:26+08:00", "document/content/docs/self-host/upgrading/4-14/41412.mdx": "2026-04-21T23:04:26+08:00", "document/content/docs/self-host/upgrading/4-14/41413.en.mdx": "2026-04-21T23:04:26+08:00", "document/content/docs/self-host/upgrading/4-14/41413.mdx": "2026-04-21T23:04:26+08:00", - "document/content/docs/self-host/upgrading/4-14/41414.mdx": "2026-04-22T23:35:11+08:00", + "document/content/docs/self-host/upgrading/4-14/41414.mdx": "2026-04-24T18:21:37+08:00", "document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00", @@ -251,7 +251,7 @@ "document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00", "document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00", "document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-04-07T21:01:52+08:00", - "document/content/docs/self-host/upgrading/4-15/4150.mdx": "2026-04-22T14:36:14+08:00", + "document/content/docs/self-host/upgrading/4-15/4150.mdx": "2026-04-23T18:02:28+08:00", "document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00", @@ -426,4 +426,4 @@ "document/content/docs/use-cases/external-integration/wecom.mdx": "2025-12-10T20:07:05+08:00", "document/content/docs/use-cases/index.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/use-cases/index.mdx": "2025-07-24T14:23:04+08:00" -} +} \ No newline at end of file diff --git a/packages/global/core/chat/adapt.ts b/packages/global/core/chat/adapt.ts index 7e7c2e85ca..a01d579083 100644 --- a/packages/global/core/chat/adapt.ts +++ b/packages/global/core/chat/adapt.ts @@ -171,7 +171,7 @@ export const chats2GPTMessages = ({ typeof lastResult?.content === 'string' ) { lastResult.content += value.text.content; - } else if (lastResult.reasoning_content) { + } else if (lastResult?.reasoning_content) { lastResult.content = value.text.content; } else { aiResults.push({ diff --git a/test/cases/global/core/chat/adapt.test.ts b/test/cases/global/core/chat/adapt.test.ts index 2dd23f557f..53415fc768 100644 --- a/test/cases/global/core/chat/adapt.test.ts +++ b/test/cases/global/core/chat/adapt.test.ts @@ -444,6 +444,124 @@ describe('chats2GPTMessages', () => { // Plan should be skipped when reserveTool is false expect(result).toHaveLength(2); }); + + it('should convert AI message with reasoning only', () => { + const messages: ChatItemMiniType[] = [ + { + obj: ChatRoleEnum.AI, + value: [{ reasoning: { content: 'Let me think...' } }] + } + ]; + + const result = chats2GPTMessages({ messages, reserveId: false }); + + expect(result).toHaveLength(1); + expect(result[0].role).toBe(ChatCompletionRequestMessageRoleEnum.Assistant); + expect((result[0] as any).reasoning_content).toBe('Let me think...'); + expect((result[0] as any).content).toBeUndefined(); + }); + + it('should merge reasoning + text into a single assistant message', () => { + const messages: ChatItemMiniType[] = [ + { + obj: ChatRoleEnum.AI, + value: [ + { reasoning: { content: 'Let me think...' } }, + { text: { content: 'Final answer' } } + ] + } + ]; + + const result = chats2GPTMessages({ messages, reserveId: false }); + + expect(result).toHaveLength(1); + expect(result[0].role).toBe(ChatCompletionRequestMessageRoleEnum.Assistant); + expect((result[0] as any).reasoning_content).toBe('Let me think...'); + expect((result[0] as any).content).toBe('Final answer'); + }); + + it('should merge reasoning + tool_calls into a single assistant message', () => { + const messages: ChatItemMiniType[] = [ + { + obj: ChatRoleEnum.AI, + value: [ + { reasoning: { content: 'Need to call a tool' } }, + { + tool: { + id: 'tool-1', + toolName: 'Search', + toolAvatar: '', + functionName: 'search_web', + params: '{"q":"x"}', + response: '{}' + } + } + ] + } + ]; + + const result = chats2GPTMessages({ messages, reserveId: false, reserveTool: true }); + + // 1 merged assistant (reasoning + tool_calls) + 1 tool response + expect(result).toHaveLength(2); + expect(result[0].role).toBe(ChatCompletionRequestMessageRoleEnum.Assistant); + expect((result[0] as any).reasoning_content).toBe('Need to call a tool'); + expect((result[0] as any).tool_calls).toHaveLength(1); + expect((result[0] as any).tool_calls[0].function.name).toBe('search_web'); + expect(result[1].role).toBe(ChatCompletionRequestMessageRoleEnum.Tool); + }); + + it('should keep tool_calls separate when no reasoning precedes them', () => { + const messages: ChatItemMiniType[] = [ + { + obj: ChatRoleEnum.AI, + value: [ + { text: { content: 'Calling tool' } }, + { + tool: { + id: 'tool-1', + toolName: 'Search', + toolAvatar: '', + functionName: 'search_web', + params: '{}', + response: '{}' + } + } + ] + } + ]; + + const result = chats2GPTMessages({ messages, reserveId: false, reserveTool: true }); + + // text assistant + tool_calls assistant + tool response + expect(result).toHaveLength(3); + expect((result[0] as any).content).toBe('Calling tool'); + expect((result[0] as any).tool_calls).toBeUndefined(); + expect((result[1] as any).tool_calls).toHaveLength(1); + expect(result[2].role).toBe(ChatCompletionRequestMessageRoleEnum.Tool); + }); + + it('should handle multiple reasoning values producing separate assistant entries', () => { + const messages: ChatItemMiniType[] = [ + { + obj: ChatRoleEnum.AI, + value: [ + { reasoning: { content: 'Step 1 thinking' } }, + { text: { content: 'Intermediate answer' } }, + { reasoning: { content: 'Step 2 thinking' } }, + { text: { content: 'Final answer' } } + ] + } + ]; + + const result = chats2GPTMessages({ messages, reserveId: false }); + + expect(result).toHaveLength(2); + expect((result[0] as any).reasoning_content).toBe('Step 1 thinking'); + expect((result[0] as any).content).toBe('Intermediate answer'); + expect((result[1] as any).reasoning_content).toBe('Step 2 thinking'); + expect((result[1] as any).content).toBe('Final answer'); + }); }); describe('GPTMessages2Chats', () => { @@ -530,6 +648,40 @@ describe('GPTMessages2Chats', () => { expect(result[0].value[1].text?.content).toBe('Final answer'); }); + it('should drop reasoning when reserveReason is false', () => { + const messages: ChatCompletionMessageParam[] = [ + { + role: ChatCompletionRequestMessageRoleEnum.Assistant, + content: 'Final answer', + reasoning_content: 'Let me think about this...' + } + ]; + + const result = GPTMessages2Chats({ messages, reserveReason: false }); + + expect(result).toHaveLength(1); + expect(result[0].value).toHaveLength(1); + expect(result[0].value[0].text?.content).toBe('Final answer'); + expect((result[0].value[0] as any).reasoning).toBeUndefined(); + }); + + it('should keep only reasoning when assistant message has no content', () => { + const messages: ChatCompletionMessageParam[] = [ + { + role: ChatCompletionRequestMessageRoleEnum.Assistant, + content: '', + reasoning_content: 'Thinking only' + } + ]; + + const result = GPTMessages2Chats({ messages }); + + expect(result).toHaveLength(1); + expect(result[0].value).toHaveLength(1); + const value = result[0].value[0] as { reasoning?: { content: string } }; + expect(value.reasoning?.content).toBe('Thinking only'); + }); + it('should merge messages with same dataId', () => { const messages: ChatCompletionMessageParam[] = [ {