From f057cfb46af198566935d811ba294e596c9ab5ec Mon Sep 17 00:00:00 2001 From: jeanpaul Date: Mon, 10 Feb 2025 15:41:13 +0100 Subject: [PATCH] fix(OpenAI Node): Limit chat history to context window when using memory (#13137) --- .../actions/assistant/message.operation.ts | 4 +- .../nodes/vendors/OpenAi/helpers/utils.ts | 6 +++ .../nodes/vendors/OpenAi/test/utils.test.ts | 46 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/utils.test.ts diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts index bc22ac948f..8e62b1802f 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/message.operation.ts @@ -22,7 +22,7 @@ import { promptTypeOptions } from '@utils/descriptions'; import { getConnectedTools } from '@utils/helpers'; import { getTracingConfig } from '@utils/tracing'; -import { formatToOpenAIAssistantTool } from '../../helpers/utils'; +import { formatToOpenAIAssistantTool, getChatMessages } from '../../helpers/utils'; import { assistantRLC } from '../descriptions'; const properties: INodeProperties[] = [ @@ -252,7 +252,7 @@ export async function execute(this: IExecuteFunctions, i: number): Promise { + return (await memory.loadMemoryVariables({}))[memory.memoryKey] as BaseMessage[]; +} diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/utils.test.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/utils.test.ts new file mode 100644 index 0000000000..1ab07ff177 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/test/utils.test.ts @@ -0,0 +1,46 @@ +import { AIMessage, HumanMessage } from '@langchain/core/messages'; +import { BufferWindowMemory } from 'langchain/memory'; + +import { getChatMessages } from '../helpers/utils'; + +describe('OpenAI message history', () => { + it('should only get a limited number of messages', async () => { + const memory = new BufferWindowMemory({ + returnMessages: true, + k: 2, + }); + expect(await getChatMessages(memory)).toEqual([]); + + await memory.saveContext( + [new HumanMessage({ content: 'human 1' })], + [new AIMessage({ content: 'ai 1' })], + ); + // `k` means turns, but `getChatMessages` returns messages, so a Human and an AI message. + expect((await getChatMessages(memory)).length).toEqual(2); + + await memory.saveContext( + [new HumanMessage({ content: 'human 2' })], + [new AIMessage({ content: 'ai 2' })], + ); + expect((await getChatMessages(memory)).length).toEqual(4); + expect((await getChatMessages(memory)).map((msg) => msg.content)).toEqual([ + 'human 1', + 'ai 1', + 'human 2', + 'ai 2', + ]); + + // We expect this to be trimmed... + await memory.saveContext( + [new HumanMessage({ content: 'human 3' })], + [new AIMessage({ content: 'ai 3' })], + ); + expect((await getChatMessages(memory)).length).toEqual(4); + expect((await getChatMessages(memory)).map((msg) => msg.content)).toEqual([ + 'human 2', + 'ai 2', + 'human 3', + 'ai 3', + ]); + }); +});