Skip to content

Commit 5015290

Browse files
authored
fix(Chat Memory Manager Node): Fix simplifyMessages to not overwrite consecutive messages of same type (#16168)
1 parent cdab4c1 commit 5015290

File tree

2 files changed

+126
-16
lines changed

2 files changed

+126
-16
lines changed

packages/@n8n/nodes-langchain/nodes/memory/MemoryManager/MemoryManager.node.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
22
import type { BaseChatMemory } from '@langchain/community/memory/chat_memory';
3-
import { AIMessage, SystemMessage, HumanMessage, type BaseMessage } from '@langchain/core/messages';
3+
import type { MessageContent, BaseMessage } from '@langchain/core/messages';
4+
import { AIMessage, SystemMessage, HumanMessage } from '@langchain/core/messages';
45
import { NodeConnectionTypes } from 'n8n-workflow';
56
import type {
67
IDataObject,
@@ -17,24 +18,31 @@ interface MessageRecord {
1718
hideFromUI: boolean;
1819
}
1920

20-
function simplifyMessages(messages: BaseMessage[]) {
21-
const chunkedMessages = [];
22-
for (let i = 0; i < messages.length; i += 2) {
23-
chunkedMessages.push([messages[i], messages[i + 1]]);
24-
}
21+
export function simplifyMessages(messages: BaseMessage[]): Array<Record<string, MessageContent>> {
22+
if (messages.length === 0) return [];
2523

26-
const transformedMessages = chunkedMessages.map((exchange) => {
27-
const simplified = {
28-
[exchange[0]._getType()]: exchange[0].content,
29-
};
24+
const result: Array<Record<string, MessageContent>> = [];
25+
let index = 0;
3026

31-
if (exchange[1]) {
32-
simplified[exchange[1]._getType()] = exchange[1].content;
33-
}
27+
while (index < messages.length) {
28+
const currentGroup: Record<string, MessageContent> = {};
29+
30+
do {
31+
const message = messages[index];
32+
const messageType = message.getType();
33+
34+
if (messageType in currentGroup) {
35+
break;
36+
}
37+
38+
currentGroup[messageType] = message.content;
39+
index++;
40+
} while (index < messages.length);
41+
42+
result.push(currentGroup);
43+
}
3444

35-
return simplified;
36-
});
37-
return transformedMessages;
45+
return result;
3846
}
3947

4048
const prepareOutputSetup = (ctx: IExecuteFunctions, version: number, memory: BaseChatMemory) => {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { AIMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
2+
3+
import { simplifyMessages } from '../MemoryManager.node';
4+
5+
describe('simplifyMessages', () => {
6+
it('should handle single message', () => {
7+
const messages = [new HumanMessage('Hello')];
8+
const result = simplifyMessages(messages);
9+
expect(result).toEqual([{ human: 'Hello' }]);
10+
});
11+
12+
it('should group different message types together', () => {
13+
const messages = [
14+
new HumanMessage('Hello, how are you?'),
15+
new AIMessage("I'm doing well, thank you for asking! How about you?"),
16+
];
17+
const result = simplifyMessages(messages);
18+
expect(result).toEqual([
19+
{
20+
human: 'Hello, how are you?',
21+
ai: "I'm doing well, thank you for asking! How about you?",
22+
},
23+
]);
24+
});
25+
26+
it('should separate consecutive messages of same type into different groups', () => {
27+
const messages = [
28+
new HumanMessage('First human message'),
29+
new HumanMessage('Second human message'),
30+
new AIMessage('AI response'),
31+
];
32+
const result = simplifyMessages(messages);
33+
expect(result).toEqual([
34+
{ human: 'First human message' },
35+
{ human: 'Second human message', ai: 'AI response' },
36+
]);
37+
});
38+
39+
it('should handle three consecutive messages of same type', () => {
40+
const messages = [new HumanMessage('1'), new HumanMessage('2'), new HumanMessage('3')];
41+
const result = simplifyMessages(messages);
42+
expect(result).toEqual([{ human: '1' }, { human: '2' }, { human: '3' }]);
43+
});
44+
45+
it('should handle mixed message types with grouping', () => {
46+
const messages = [
47+
new SystemMessage('System message'),
48+
new HumanMessage('Hello'),
49+
new AIMessage('Hi there'),
50+
new HumanMessage('Another human message'),
51+
new AIMessage('Another AI message'),
52+
];
53+
const result = simplifyMessages(messages);
54+
expect(result).toEqual([
55+
{
56+
system: 'System message',
57+
human: 'Hello',
58+
ai: 'Hi there',
59+
},
60+
{
61+
human: 'Another human message',
62+
ai: 'Another AI message',
63+
},
64+
]);
65+
});
66+
67+
it('should handle system messages correctly', () => {
68+
const messages = [
69+
new SystemMessage('System instruction'),
70+
new HumanMessage('User question'),
71+
new AIMessage('AI response'),
72+
];
73+
const result = simplifyMessages(messages);
74+
expect(result).toEqual([
75+
{
76+
system: 'System instruction',
77+
human: 'User question',
78+
ai: 'AI response',
79+
},
80+
]);
81+
});
82+
83+
it('should handle alternating same types correctly', () => {
84+
const messages = [
85+
new HumanMessage('Human 1'),
86+
new AIMessage('AI 1'),
87+
new HumanMessage('Human 2'),
88+
new AIMessage('AI 2'),
89+
];
90+
const result = simplifyMessages(messages);
91+
expect(result).toEqual([
92+
{
93+
human: 'Human 1',
94+
ai: 'AI 1',
95+
},
96+
{
97+
human: 'Human 2',
98+
ai: 'AI 2',
99+
},
100+
]);
101+
});
102+
});

0 commit comments

Comments
 (0)