From 2fff38827ae0e61bad951bfe36a5c40ca9e86aac Mon Sep 17 00:00:00 2001 From: yehorkardash Date: Wed, 22 Oct 2025 16:39:08 +0300 Subject: [PATCH] fix(OpenAI Node): Don't include function calls when conversation id is used (#21047) --- .../actions/text/response.operation.test.ts | 102 ++++++++++++++++++ .../v2/actions/text/response.operation.ts | 16 ++- 2 files changed, 108 insertions(+), 10 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/v2/actions/text/response.operation.test.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/v2/actions/text/response.operation.test.ts index d64921187dc..7fa02f5093c 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/v2/actions/text/response.operation.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/v2/actions/text/response.operation.test.ts @@ -522,6 +522,108 @@ describe('OpenAI Response Operation', () => { expect(mockTool.invoke).toHaveBeenCalledWith('test input'); expect(mockApiRequest).toHaveBeenCalledTimes(2); + expect(mockApiRequest).toHaveBeenNthCalledWith(2, 'POST', '/responses', { + body: { + model: 'gpt-4o', + input: [ + { + type: 'reasoning', + content: 'I need to use the test tool to get information', + }, + { + type: 'function_call', + call_id: 'call_123', + name: 'test_tool', + arguments: JSON.stringify({ input: 'test input' }), + }, + { + call_id: 'call_123', + output: 'Tool response', + type: 'function_call_output', + }, + ], + tools: [{ name: 'test_tool', type: 'function', parameters: {}, strict: false }], + }, + }); + expect(result).toEqual([ + { + json: finalResponse, + pairedItem: { item: 0 }, + }, + ]); + }); + + it('should not include function_call or reasoning items in the request if there is a conversation', async () => { + const mockTool = { + name: 'test_tool', + invoke: jest.fn().mockResolvedValue('Tool response'), + schema: { + typeName: 'ZodObject', + _def: { typeName: 'ZodObject', shape: () => ({}) }, + parse: jest.fn(), + safeParse: jest.fn(), + }, + call: jest.fn(), + description: 'Test tool', + returnDirect: false, + } as any; + + const initialResponse = { + id: 'resp_123', + status: 'completed', + output: [ + { + type: 'reasoning', + content: 'I need to use the test tool to get information', + }, + { + type: 'function_call', + call_id: 'call_123', + name: 'test_tool', + arguments: JSON.stringify({ input: 'test input' }), + }, + ], + }; + + const finalResponse = { + id: 'resp_123', + status: 'completed', + output: [ + { + type: 'message', + role: 'assistant', + content: [{ type: 'output_text', text: 'Final response' }], + }, + ], + }; + + mockGetConnectedTools.mockResolvedValue([mockTool]); + mockCreateRequest.mockResolvedValue({ + model: 'gpt-4o', + input: [], + tools: [{ name: 'test_tool', type: 'function', parameters: {}, strict: false }], + conversation: 'conv_123', + }); + mockApiRequest.mockResolvedValueOnce(initialResponse).mockResolvedValueOnce(finalResponse); + + const result = await execute.call(mockExecuteFunctions, 0); + + expect(mockTool.invoke).toHaveBeenCalledWith('test input'); + expect(mockApiRequest).toHaveBeenCalledTimes(2); + expect(mockApiRequest).toHaveBeenNthCalledWith(2, 'POST', '/responses', { + body: { + model: 'gpt-4o', + input: [ + { + call_id: 'call_123', + output: 'Tool response', + type: 'function_call_output', + }, + ], + tools: [{ name: 'test_tool', type: 'function', parameters: {}, strict: false }], + conversation: 'conv_123', + }, + }); expect(result).toEqual([ { json: finalResponse, diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts index ad4c4478700..8bfb1039169 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/v2/actions/text/response.operation.ts @@ -706,7 +706,6 @@ export async function execute(this: IExecuteFunctions, i: number): Promise toolCalls.some((item) => item.type === 'function_call'); - const answeredToolCalls = new Set(); let currentIteration = 1; // make sure there's actually a function call to answer while (toolCalls.length && hasFunctionCall()) { @@ -714,14 +713,13 @@ export async function execute(this: IExecuteFunctions, i: number): Promise