n8n-nodes-tembory 1.0.25 → 1.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/nodes/Mem0/Mem0Memory.node.js +39 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Node de memoria operacional da Tembory para agentes de IA no n8n.
|
|
4
4
|
|
|
5
|
-
Versao atual: `1.0.
|
|
5
|
+
Versao atual: `1.0.27`.
|
|
6
|
+
|
|
7
|
+
## 1.0.27
|
|
8
|
+
|
|
9
|
+
- Aumenta a janela deterministica de conversa para 30 mensagens por padrao em todos os presets de producao.
|
|
10
|
+
- Entrega `conversation_history_chronological` e `all_user_messages_chronological` no frame de conversa.
|
|
11
|
+
- Para conversas curtas/medias, o historico completo recente vira a fonte primaria; vetor e resumo ficam como complemento.
|
|
12
|
+
|
|
13
|
+
## 1.0.26
|
|
14
|
+
|
|
15
|
+
- Adiciona `first_user_message` ao frame de conversa para perguntas como "qual foi minha primeira mensagem?".
|
|
16
|
+
- Preserva a primeira mensagem do usuario mesmo quando a janela recente e podada.
|
|
17
|
+
- Amplia a busca vetorial auxiliar de transcript para recuperar mensagens iniciais da sessao.
|
|
6
18
|
|
|
7
19
|
## 1.0.25
|
|
8
20
|
|
|
@@ -925,7 +925,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
925
925
|
topK: 8,
|
|
926
926
|
lastN: 12,
|
|
927
927
|
toolHistoryLastN: 15,
|
|
928
|
-
recentMessagesLastN:
|
|
928
|
+
recentMessagesLastN: 30,
|
|
929
929
|
},
|
|
930
930
|
productionBalanced: {
|
|
931
931
|
summarySource: 'auto',
|
|
@@ -958,7 +958,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
958
958
|
topK: 6,
|
|
959
959
|
lastN: 8,
|
|
960
960
|
toolHistoryLastN: 10,
|
|
961
|
-
recentMessagesLastN:
|
|
961
|
+
recentMessagesLastN: 30,
|
|
962
962
|
vectorMemoryMaxChars: 360,
|
|
963
963
|
contextMaxChars: 10000,
|
|
964
964
|
connectedModelSummaryMaxChars: 1200,
|
|
@@ -995,7 +995,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
995
995
|
topK: 4,
|
|
996
996
|
lastN: 4,
|
|
997
997
|
toolHistoryLastN: 5,
|
|
998
|
-
recentMessagesLastN:
|
|
998
|
+
recentMessagesLastN: 30,
|
|
999
999
|
vectorMemoryMaxChars: 260,
|
|
1000
1000
|
contextMaxChars: 7000,
|
|
1001
1001
|
connectedModelSummaryMaxChars: 1000,
|
|
@@ -1033,7 +1033,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1033
1033
|
lastN: 4,
|
|
1034
1034
|
maxReturn: 6,
|
|
1035
1035
|
toolHistoryLastN: 6,
|
|
1036
|
-
recentMessagesLastN:
|
|
1036
|
+
recentMessagesLastN: 30,
|
|
1037
1037
|
vectorMemoryMaxChars: 220,
|
|
1038
1038
|
contextMaxChars: 7000,
|
|
1039
1039
|
connectedModelSummaryMaxChars: 1200,
|
|
@@ -1063,7 +1063,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1063
1063
|
topK: 10,
|
|
1064
1064
|
lastN: 20,
|
|
1065
1065
|
toolHistoryLastN: 20,
|
|
1066
|
-
recentMessagesLastN:
|
|
1066
|
+
recentMessagesLastN: 30,
|
|
1067
1067
|
payloadFormat: 'auditJson',
|
|
1068
1068
|
},
|
|
1069
1069
|
};
|
|
@@ -1116,6 +1116,18 @@ const appendCurrentUserMessage = (items = [], query = '') => {
|
|
|
1116
1116
|
return items || [];
|
|
1117
1117
|
return (items || []).concat([{ role: 'user', content: truncate(content, 2000), at: nowIso(), source: 'current_input' }]);
|
|
1118
1118
|
};
|
|
1119
|
+
const pruneConversationMessagesPreserveAnchors = (items = [], limit = 8) => {
|
|
1120
|
+
const normalizedLimit = Math.max(1, Number(limit) || 1);
|
|
1121
|
+
const chronological = [...(items || [])].sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
1122
|
+
const firstUser = chronological.find((msg) => /^(user|human)$/i.test(String(msg.role || '')));
|
|
1123
|
+
const tail = pruneByLimit(chronological, normalizedLimit);
|
|
1124
|
+
if (!firstUser)
|
|
1125
|
+
return tail;
|
|
1126
|
+
const keyOf = (msg) => `${msg.role}:${msg.at}:${msg.content}`;
|
|
1127
|
+
if (tail.some((msg) => keyOf(msg) === keyOf(firstUser)))
|
|
1128
|
+
return tail;
|
|
1129
|
+
return dedupeRecentMessages([firstUser].concat(tail));
|
|
1130
|
+
};
|
|
1119
1131
|
const dedupeToolHistory = (items) => {
|
|
1120
1132
|
const seen = new Set();
|
|
1121
1133
|
const out = [];
|
|
@@ -1203,26 +1215,34 @@ const previousUserFallbackFromWorkingMemory = (query = '', workingMemory = {}) =
|
|
|
1203
1215
|
return null;
|
|
1204
1216
|
return candidate;
|
|
1205
1217
|
};
|
|
1218
|
+
const firstUserMessageFromConversation = (recentMessages = []) => [...(recentMessages || [])]
|
|
1219
|
+
.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')))
|
|
1220
|
+
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime())[0] || null;
|
|
1206
1221
|
const buildConversationFrame = ({ query = '', recentMessages = [], workingMemory = {} }) => {
|
|
1207
1222
|
const normalizedQuery = normalizeIntentText(query).trim();
|
|
1208
1223
|
const users = recentUserMessages(recentMessages);
|
|
1209
1224
|
const currentUser = users.find((msg) => normalizeIntentText(msg.content).trim() === normalizedQuery) || (query ? { role: 'user', content: query, at: nowIso() } : null);
|
|
1210
1225
|
const previousUser = previousUserMessageForQuery(query, recentMessages);
|
|
1226
|
+
const firstUser = firstUserMessageFromConversation(recentMessages);
|
|
1211
1227
|
const previousUserMessage = previousUser ? truncate(previousUser.content, 500) : previousUserFallbackFromWorkingMemory(query, workingMemory);
|
|
1212
|
-
const chronological =
|
|
1228
|
+
const chronological = (recentMessages || []).map((msg) => ({
|
|
1213
1229
|
role: msg.role,
|
|
1214
1230
|
content: truncate(msg.content, 500),
|
|
1215
1231
|
at: msg.at,
|
|
1216
1232
|
}));
|
|
1233
|
+
const userMessagesChronological = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
|
|
1217
1234
|
const frame = cleanContextValue({
|
|
1218
1235
|
current_user_message: currentUser ? truncate(currentUser.content, 500) : truncate(query, 500),
|
|
1236
|
+
first_user_message: firstUser ? truncate(firstUser.content, 500) : null,
|
|
1219
1237
|
previous_user_message: previousUserMessage,
|
|
1238
|
+
conversation_history_chronological: chronological,
|
|
1220
1239
|
recent_messages_chronological: chronological,
|
|
1240
|
+
all_user_messages_chronological: userMessagesChronological,
|
|
1221
1241
|
recent_user_messages: users
|
|
1222
1242
|
.filter((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery)
|
|
1223
1243
|
.slice(0, 5)
|
|
1224
1244
|
.map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at })),
|
|
1225
|
-
instruction: 'This is the authoritative short-term conversation frame. If the user asks about
|
|
1245
|
+
instruction: 'This is the authoritative short-term conversation frame. If the user asks about first/current/previous/last client messages or what they already said, answer from first_user_message/current_user_message/previous_user_message/conversation_history_chronological/all_user_messages_chronological before using vector memories, summaries, operational state, or tool history.',
|
|
1226
1246
|
});
|
|
1227
1247
|
if (!previousUserMessage)
|
|
1228
1248
|
frame.previous_user_message = null;
|
|
@@ -1234,6 +1254,7 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
|
|
|
1234
1254
|
if (!recall && !agendaStatus)
|
|
1235
1255
|
return null;
|
|
1236
1256
|
const previousUser = previousUserMessageForQuery(query, recentMessages);
|
|
1257
|
+
const firstUser = firstUserMessageFromConversation(recentMessages);
|
|
1237
1258
|
const users = recentUserMessages(recentMessages)
|
|
1238
1259
|
.filter((msg) => normalizeIntentText(msg.content).trim() !== normalizeIntentText(query).trim())
|
|
1239
1260
|
.slice(0, 3)
|
|
@@ -1243,6 +1264,7 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
|
|
|
1243
1264
|
const focus = cleanContextValue({
|
|
1244
1265
|
current_user_request: truncate(query, 500),
|
|
1245
1266
|
intent: recall ? 'conversation_recall' : 'agenda_status_question',
|
|
1267
|
+
first_user_message: firstUser ? truncate(firstUser.content, 500) : null,
|
|
1246
1268
|
previous_user_message: previousUserMessage,
|
|
1247
1269
|
recent_user_messages: users,
|
|
1248
1270
|
agenda_status: agendaStatus ? {
|
|
@@ -1260,7 +1282,7 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
|
|
|
1260
1282
|
has_availability: Boolean(agenda.has_availability),
|
|
1261
1283
|
} : undefined,
|
|
1262
1284
|
instruction: recall
|
|
1263
|
-
? 'Answer the user meta-question from previous_user_message/recent_user_messages. Do not answer with agenda availability unless the user asks for availability.'
|
|
1285
|
+
? 'Answer the user meta-question from first_user_message/previous_user_message/recent_user_messages. Do not answer with agenda availability unless the user asks for availability.'
|
|
1264
1286
|
: 'Answer whether the appointment/reservation is already scheduled using agenda_status. Do not offer availability as the main answer unless there is no schedule/reservation state.',
|
|
1265
1287
|
});
|
|
1266
1288
|
if (!previousUserMessage)
|
|
@@ -1915,8 +1937,8 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
1915
1937
|
section: 'context_header',
|
|
1916
1938
|
title: 'Tembory context',
|
|
1917
1939
|
value: compactForAgent || compactStateSections
|
|
1918
|
-
? 'Read-only memory. Conversation frame is authoritative for current/previous client messages. Follow next_expected_action when present. Before calling downstream tools, verify required prior tool context in tool_history or operational_state. Do not repeat tools listed in do_not_repeat_tools.'
|
|
1919
|
-
: 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. The Conversation frame is authoritative for current, previous, and recent client messages. Treat next_expected_action as an instruction, not as a suggestion. If it says to call a tool now, call that tool instead of asking the user the same question again. If the user asks to continue, chooses a slot, says ok/sim, reserve, confirm, update, cancel, or performs any downstream action that depends on a prior tool result, first verify the required prior result in tool_history, recent_messages, or vector memories. If the required prior result is absent, do not call the downstream tool; ask for the missing context or call the appropriate prerequisite tool.',
|
|
1940
|
+
? 'Read-only memory. Conversation frame is authoritative for the full recent transcript, including first/current/previous client messages. Follow next_expected_action when present. Before calling downstream tools, verify required prior tool context in tool_history or operational_state. Do not repeat tools listed in do_not_repeat_tools.'
|
|
1941
|
+
: 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. The Conversation frame is authoritative for the full recent transcript, including first, current, previous, and recent client messages. Treat next_expected_action as an instruction, not as a suggestion. If it says to call a tool now, call that tool instead of asking the user the same question again. If the user asks to continue, chooses a slot, says ok/sim, reserve, confirm, update, cancel, or performs any downstream action that depends on a prior tool result, first verify the required prior result in tool_history, recent_messages, or vector memories. If the required prior result is absent, do not call the downstream tool; ask for the missing context or call the appropriate prerequisite tool.',
|
|
1920
1942
|
});
|
|
1921
1943
|
}
|
|
1922
1944
|
sections.push({
|
|
@@ -2339,7 +2361,7 @@ class Mem0Memory {
|
|
|
2339
2361
|
values: [
|
|
2340
2362
|
{ displayName: 'Incluir Profile Facts', name: 'includeProfileFacts', type: 'boolean', default: true },
|
|
2341
2363
|
{ displayName: 'Incluir Mensagens Recentes', name: 'includeRecentMessages', type: 'boolean', default: true },
|
|
2342
|
-
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default:
|
|
2364
|
+
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default: 30 },
|
|
2343
2365
|
{ displayName: 'Incluir Highlights Recentes', name: 'includeRecentHighlights', type: 'boolean', default: true },
|
|
2344
2366
|
{ displayName: 'Máximo de Highlights', name: 'recentHighlightsMaxItems', type: 'number', default: 6 },
|
|
2345
2367
|
{ displayName: 'Incluir Grafo', name: 'includeRelations', type: 'boolean', default: true },
|
|
@@ -2494,7 +2516,7 @@ class Mem0Memory {
|
|
|
2494
2516
|
{ displayName: 'Incluir Entity Timeline', name: 'includeEntityTimeline', type: 'boolean', default: true, description: 'Injeta uma timeline compacta de entidades, fatos de perfil, relações do grafo e eventos importantes da sessão.' },
|
|
2495
2517
|
{ displayName: 'Incluir Compressão de Memória', name: 'includeMemoryCompression', type: 'boolean', default: true, description: 'Injeta resumos compactos de turno, sessão, entidades e workflow para reduzir ruído.' },
|
|
2496
2518
|
{ displayName: 'Incluir Mensagens Recentes', name: 'includeRecentMessages', type: 'boolean', default: true },
|
|
2497
|
-
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default:
|
|
2519
|
+
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default: 30 },
|
|
2498
2520
|
{ displayName: 'Incluir Highlights Recentes', name: 'includeRecentHighlights', type: 'boolean', default: true },
|
|
2499
2521
|
{ displayName: 'Máximo de Highlights', name: 'recentHighlightsMaxItems', type: 'number', default: 6 },
|
|
2500
2522
|
],
|
|
@@ -3136,14 +3158,14 @@ class Mem0Memory {
|
|
|
3136
3158
|
try {
|
|
3137
3159
|
const transcriptProbe = [
|
|
3138
3160
|
'recent conversation transcript',
|
|
3139
|
-
'last user messages previous message',
|
|
3161
|
+
'first user message earliest message last user messages previous message full conversation',
|
|
3140
3162
|
query,
|
|
3141
3163
|
].filter(Boolean).join('\n');
|
|
3142
3164
|
const transcriptBody = {
|
|
3143
3165
|
user_id: key,
|
|
3144
3166
|
agent_id: adv.agentId ? String(adv.agentId) : undefined,
|
|
3145
3167
|
run_id: adv.runId ? String(adv.runId) : undefined,
|
|
3146
|
-
top_k: Math.max(Number(adv.recentMessagesLastN || 8) *
|
|
3168
|
+
top_k: Math.max(Number(adv.recentMessagesLastN || 8) * 6, 50),
|
|
3147
3169
|
filters: { kind: 'recent_message' },
|
|
3148
3170
|
};
|
|
3149
3171
|
const transcriptRes = await searchClientVectorMemories(this, connectedEmbedding, transcriptProbe, transcriptBody);
|
|
@@ -3201,7 +3223,7 @@ class Mem0Memory {
|
|
|
3201
3223
|
}
|
|
3202
3224
|
const storedRecentMessages = adv.includeRecentMessages === false ? [] : (store.recentMessages[key] || []).concat(persistedRecentMessages, vectorRecentMessages);
|
|
3203
3225
|
const allRecentMessages = dedupeRecentMessages(appendCurrentUserMessage(storedRecentMessages, query));
|
|
3204
|
-
const recentMessages =
|
|
3226
|
+
const recentMessages = pruneConversationMessagesPreserveAnchors(allRecentMessages, Math.max(Number(adv.recentMessagesLastN || 8), 8));
|
|
3205
3227
|
const toolHistoryFromRecentMessages = [];
|
|
3206
3228
|
if (adv.includeToolHistory !== false) {
|
|
3207
3229
|
for (const msg of recentMessages)
|
|
@@ -3568,6 +3590,8 @@ exports.__private = {
|
|
|
3568
3590
|
toolHistoryFromMemory,
|
|
3569
3591
|
recentMessageFromMemory,
|
|
3570
3592
|
previousUserFallbackFromWorkingMemory,
|
|
3593
|
+
firstUserMessageFromConversation,
|
|
3594
|
+
pruneConversationMessagesPreserveAnchors,
|
|
3571
3595
|
dedupeToolHistory,
|
|
3572
3596
|
applyToolHistoryWindow,
|
|
3573
3597
|
dedupeRecentMessages,
|
package/package.json
CHANGED