n8n-nodes-tembory 1.0.43 → 1.0.45
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/dist/nodes/Mem0/Mem0Memory.node.js +118 -72
- package/package.json +1 -1
|
@@ -632,6 +632,36 @@ const encodeToolLedger = (tools = [], threadId) => {
|
|
|
632
632
|
})),
|
|
633
633
|
})}`;
|
|
634
634
|
};
|
|
635
|
+
const encodeTurnArchive = ({ threadId, messages = [], tools = [], workingMemory = {}, decisionState = {}, memoryCompression = {}, operationalState = {} }) => {
|
|
636
|
+
const chronological = sortConversationChronological(messages || []);
|
|
637
|
+
const orderedTools = sortToolHistory(tools || []);
|
|
638
|
+
return `Tembory long-term turn archive. Use vector search only when short-term SLM state and recent transcript are insufficient. ${safeStringify({
|
|
639
|
+
marker: 'tembory_turn_archive_v1',
|
|
640
|
+
thread_id: threadId,
|
|
641
|
+
generated_at: nowIso(),
|
|
642
|
+
conversation: chronological.map((msg) => ({
|
|
643
|
+
role: msg.role || 'user',
|
|
644
|
+
content: msg.content || '',
|
|
645
|
+
at: msg.at || nowIso(),
|
|
646
|
+
})),
|
|
647
|
+
tools: orderedTools.map((tool, index) => ({
|
|
648
|
+
id: tool.id || tool.callId || tool.call_id || '',
|
|
649
|
+
turn_id: tool.turnId || tool.turn_id || '',
|
|
650
|
+
sequence: tool.sequence || index + 1,
|
|
651
|
+
name: tool.name || tool.tool || tool.toolName || '',
|
|
652
|
+
reason: tool.reason || tool.source || '',
|
|
653
|
+
input: tool.input || '',
|
|
654
|
+
ok: tool.ok !== false,
|
|
655
|
+
output: tool.result || '',
|
|
656
|
+
at: tool.at || nowIso(),
|
|
657
|
+
source: tool.source || 'n8n',
|
|
658
|
+
})),
|
|
659
|
+
working_memory: workingMemory,
|
|
660
|
+
decision_state: decisionState,
|
|
661
|
+
memory_compression: memoryCompression,
|
|
662
|
+
operational_state: operationalState,
|
|
663
|
+
})}`;
|
|
664
|
+
};
|
|
635
665
|
const parseRecentMessageMarker = (text) => {
|
|
636
666
|
if (!text || typeof text !== 'string' || !text.startsWith(RECENT_MESSAGE_MARKER))
|
|
637
667
|
return null;
|
|
@@ -720,6 +750,23 @@ const parseConversationLedgerMarker = (text) => {
|
|
|
720
750
|
return [];
|
|
721
751
|
}
|
|
722
752
|
};
|
|
753
|
+
const parseTurnArchive = (text) => {
|
|
754
|
+
if (!text || typeof text !== 'string')
|
|
755
|
+
return null;
|
|
756
|
+
const marker = '"marker":"tembory_turn_archive_v1"';
|
|
757
|
+
const markerIndex = text.indexOf(marker);
|
|
758
|
+
if (markerIndex < 0)
|
|
759
|
+
return null;
|
|
760
|
+
const jsonStart = text.lastIndexOf('{', markerIndex);
|
|
761
|
+
if (jsonStart < 0)
|
|
762
|
+
return null;
|
|
763
|
+
try {
|
|
764
|
+
return JSON.parse(text.slice(jsonStart));
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
723
770
|
const recentMessageFromMemory = (item) => {
|
|
724
771
|
const meta = metadataOf(item);
|
|
725
772
|
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
@@ -739,6 +786,16 @@ const recentMessageFromMemory = (item) => {
|
|
|
739
786
|
const recentMessagesFromMemory = (item) => {
|
|
740
787
|
const meta = metadataOf(item);
|
|
741
788
|
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
789
|
+
const archive = parseTurnArchive(content);
|
|
790
|
+
if (archive && Array.isArray(archive.conversation)) {
|
|
791
|
+
return archive.conversation
|
|
792
|
+
.filter((msg) => msg && msg.content)
|
|
793
|
+
.map((msg) => ({
|
|
794
|
+
role: msg.role || 'user',
|
|
795
|
+
content: truncate(msg.content, 2000),
|
|
796
|
+
at: msg.at || archive.generated_at || nowIso(),
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
742
799
|
const ledger = parseConversationLedgerMarker(content);
|
|
743
800
|
if (ledger.length)
|
|
744
801
|
return ledger;
|
|
@@ -803,6 +860,22 @@ const toolHistoryFromMemory = (item) => {
|
|
|
803
860
|
const explicitToolHistoryItemsFromMemory = (item) => {
|
|
804
861
|
const meta = metadataOf(item);
|
|
805
862
|
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
863
|
+
const archive = parseTurnArchive(content);
|
|
864
|
+
if (archive && Array.isArray(archive.tools)) {
|
|
865
|
+
return archive.tools
|
|
866
|
+
.filter((tool) => tool && tool.name)
|
|
867
|
+
.map((tool, index) => ({
|
|
868
|
+
id: tool.id || tool.call_id || tool.callId || '',
|
|
869
|
+
turnId: tool.turn_id || tool.turnId || '',
|
|
870
|
+
sequence: tool.sequence || index + 1,
|
|
871
|
+
name: String(tool.name),
|
|
872
|
+
input: tool.input === undefined ? '' : String(tool.input),
|
|
873
|
+
ok: tool.ok !== false,
|
|
874
|
+
result: truncate(tool.output || tool.result || '', 1000),
|
|
875
|
+
at: tool.at || archive.generated_at || nowIso(),
|
|
876
|
+
source: tool.source || 'turn_archive',
|
|
877
|
+
}));
|
|
878
|
+
}
|
|
806
879
|
const ledger = parseToolLedgerMarker(content);
|
|
807
880
|
if (ledger.length)
|
|
808
881
|
return ledger;
|
|
@@ -1125,7 +1198,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1125
1198
|
enableTransientSummaryCache: true,
|
|
1126
1199
|
transientSummaryCacheTTLSeconds: 300,
|
|
1127
1200
|
compactStateSections: true,
|
|
1128
|
-
useVectorMemory:
|
|
1201
|
+
useVectorMemory: false,
|
|
1129
1202
|
persistBackendMemories: true,
|
|
1130
1203
|
includeRecentMessageProbe: false,
|
|
1131
1204
|
includeToolLedgerProbe: false,
|
|
@@ -2598,7 +2671,7 @@ class Mem0Memory {
|
|
|
2598
2671
|
{ displayName: 'Máximo de Relações', name: 'maxRelations', type: 'number', default: 10 },
|
|
2599
2672
|
{ displayName: 'Incluir Action Ledger', name: 'includeActionLedger', type: 'boolean', default: true },
|
|
2600
2673
|
{ displayName: 'Incluir Entity Timeline', name: 'includeEntityTimeline', type: 'boolean', default: false },
|
|
2601
|
-
{ displayName: 'Usar Memória Vetorial no Caminho Quente', name: 'useVectorMemory', type: 'boolean', default:
|
|
2674
|
+
{ displayName: 'Usar Memória Vetorial no Caminho Quente', name: 'useVectorMemory', type: 'boolean', default: false },
|
|
2602
2675
|
{ displayName: 'Persistir Memórias no Backend', name: 'persistBackendMemories', type: 'boolean', default: true },
|
|
2603
2676
|
{ displayName: 'Probe Vetorial de Mensagens Recentes', name: 'includeRecentMessageProbe', type: 'boolean', default: false },
|
|
2604
2677
|
{ displayName: 'Probe Vetorial de Tool Ledger', name: 'includeToolLedgerProbe', type: 'boolean', default: false },
|
|
@@ -2695,7 +2768,7 @@ class Mem0Memory {
|
|
|
2695
2768
|
{ displayName: 'App ID', name: 'appId', type: 'string', default: '' },
|
|
2696
2769
|
{ displayName: 'Run ID', name: 'runId', type: 'string', default: '' },
|
|
2697
2770
|
{ displayName: 'Top K', name: 'topK', type: 'number', typeOptions: { minValue: 1 }, default: 25, description: 'Quantidade de memórias a recuperar (modos semânticos e limites de recentes)' },
|
|
2698
|
-
{ displayName: 'Usar Memória Vetorial no Caminho Quente', name: 'useVectorMemory', type: 'boolean', default:
|
|
2771
|
+
{ displayName: 'Usar Memória Vetorial no Caminho Quente', name: 'useVectorMemory', type: 'boolean', default: false, description: 'Quando ligado, usa busca BYO sob demanda para recuperar histórico longo. O padrão mantém o contexto quente em transcript + SLM e apenas arquiva o turno no vetor.' },
|
|
2699
2772
|
{ displayName: 'Persistir Memórias no Backend', name: 'persistBackendMemories', type: 'boolean', default: true, description: 'Quando desligado, mantém o estado no workflow/static data para reduzir latência.' },
|
|
2700
2773
|
{ displayName: 'Rerank', name: 'rerank', type: 'boolean', default: true, description: 'Reordenação por relevância (modos semânticos)', displayOptions: { show: { '/retrievalMode': ['semantic', 'semanticV2', 'hybrid'] } } },
|
|
2701
2774
|
{ displayName: 'Fields (lista separada por vírgula)', name: 'fields', type: 'string', default: '', description: 'Campos específicos a retornar da API (modos semânticos)', displayOptions: { show: { '/retrievalMode': ['semantic', 'semanticV2', 'hybrid'] } } },
|
|
@@ -3009,69 +3082,27 @@ class Mem0Memory {
|
|
|
3009
3082
|
const connectedEmbedding = await getConnectedEmbedding(this, itemIndex);
|
|
3010
3083
|
if (connectedEmbedding) {
|
|
3011
3084
|
const ids = { user_id: body.user_id, agent_id: body.agent_id, run_id: body.run_id };
|
|
3012
|
-
const
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
for (const recent of recentForMem0) {
|
|
3025
|
-
clientMemories.push(await createClientVectorMemory(connectedEmbedding, encodeRecentMessage(recent, threadId), {
|
|
3026
|
-
kind: 'recent_message',
|
|
3027
|
-
role: recent.role,
|
|
3028
|
-
content: recent.content,
|
|
3029
|
-
at: recent.at,
|
|
3030
|
-
thread_id: threadId,
|
|
3031
|
-
project: project || undefined,
|
|
3032
|
-
source: 'n8n_connected_embedding',
|
|
3033
|
-
}, ids));
|
|
3034
|
-
}
|
|
3035
|
-
clientMemories.push(await createClientVectorMemory(connectedEmbedding, encodeConversationLedger(recentForTurn, threadId), {
|
|
3036
|
-
kind: 'conversation_ledger',
|
|
3037
|
-
thread_id: threadId,
|
|
3038
|
-
project: project || undefined,
|
|
3039
|
-
source: 'n8n_connected_embedding',
|
|
3040
|
-
generated_at: nowIso(),
|
|
3041
|
-
}, ids));
|
|
3042
|
-
}
|
|
3043
|
-
if (adv.persistToolFactsToMem0 && toolCalls.length) {
|
|
3044
|
-
const facts = toolCalls.map((tool) => `Tool ${tool.name} input=${tool.input}${tool.result ? ` result=${tool.result}` : ''}`).join('\n');
|
|
3045
|
-
clientMemories.push(await createClientVectorMemory(connectedEmbedding, `Tool facts (read-only):\n${truncate(facts, 2000)}`, {
|
|
3046
|
-
kind: 'tool_facts',
|
|
3047
|
-
source: 'n8n_connected_embedding',
|
|
3048
|
-
}, ids));
|
|
3049
|
-
}
|
|
3050
|
-
if (adv.includeToolHistory !== false && toolCalls.length) {
|
|
3051
|
-
for (const tool of toolCalls) {
|
|
3052
|
-
clientMemories.push(await createClientVectorMemory(connectedEmbedding, encodeToolCall(tool, threadId), {
|
|
3053
|
-
kind: 'tool_history',
|
|
3054
|
-
id: tool.id,
|
|
3055
|
-
sequence: tool.sequence,
|
|
3056
|
-
turn_id: tool.turnId,
|
|
3057
|
-
name: tool.name,
|
|
3058
|
-
input: tool.input,
|
|
3059
|
-
ok: tool.ok,
|
|
3060
|
-
result: tool.result,
|
|
3061
|
-
at: tool.at,
|
|
3062
|
-
source: tool.source || 'n8n_connected_embedding',
|
|
3063
|
-
thread_id: threadId,
|
|
3064
|
-
}, ids));
|
|
3065
|
-
}
|
|
3066
|
-
clientMemories.push(await createClientVectorMemory(connectedEmbedding, encodeToolLedger(toolHistoryForTurn, threadId), {
|
|
3067
|
-
kind: 'tool_ledger',
|
|
3085
|
+
const archiveText = encodeTurnArchive({
|
|
3086
|
+
threadId,
|
|
3087
|
+
messages: recentForTurn,
|
|
3088
|
+
tools: toolHistoryForTurn,
|
|
3089
|
+
workingMemory: workingMemoryForTurn,
|
|
3090
|
+
decisionState: decisionStateForTurn,
|
|
3091
|
+
memoryCompression: compressionForTurn,
|
|
3092
|
+
operationalState: operationalStateForTurn,
|
|
3093
|
+
});
|
|
3094
|
+
await saveClientVectorMemories(this, [
|
|
3095
|
+
await createClientVectorMemory(connectedEmbedding, archiveText, {
|
|
3096
|
+
kind: 'turn_archive',
|
|
3068
3097
|
thread_id: threadId,
|
|
3069
3098
|
project: project || undefined,
|
|
3070
3099
|
source: 'n8n_connected_embedding',
|
|
3071
3100
|
generated_at: nowIso(),
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3101
|
+
message_count: recentForTurn.length,
|
|
3102
|
+
tool_count: toolHistoryForTurn.length,
|
|
3103
|
+
latest_tool: toolHistoryForTurn.at(-1)?.name || null,
|
|
3104
|
+
}, ids),
|
|
3105
|
+
], ids);
|
|
3075
3106
|
if (adv.includeRecentMessages !== false && recentForMem0.length) {
|
|
3076
3107
|
for (const recent of recentForMem0) {
|
|
3077
3108
|
await safePersistLegacyMemory(this, {
|
|
@@ -3296,7 +3327,7 @@ class Mem0Memory {
|
|
|
3296
3327
|
catch (error) {
|
|
3297
3328
|
connectedAi.errors.push(`languageModel: ${error.message || String(error)}`);
|
|
3298
3329
|
}
|
|
3299
|
-
if (vectorMemoryEnabled) {
|
|
3330
|
+
if (vectorMemoryEnabled || backendPersistenceEnabled) {
|
|
3300
3331
|
try {
|
|
3301
3332
|
connectedEmbedding = await this.getInputConnectionData(n8n_workflow_1.NodeConnectionTypes.AiEmbedding, itemIndex);
|
|
3302
3333
|
connectedAi.embedding = true;
|
|
@@ -3305,15 +3336,8 @@ class Mem0Memory {
|
|
|
3305
3336
|
connectedAi.errors.push(`embedding: ${error.message || String(error)}`);
|
|
3306
3337
|
}
|
|
3307
3338
|
}
|
|
3308
|
-
if (connectedEmbedding && typeof connectedEmbedding.embedQuery === 'function' && String(query || '').trim())
|
|
3309
|
-
|
|
3310
|
-
await embedQueryCached(connectedEmbedding, String(query));
|
|
3311
|
-
connectedAi.embeddingQuery = true;
|
|
3312
|
-
}
|
|
3313
|
-
catch (error) {
|
|
3314
|
-
connectedAi.errors.push(`embedding.embedQuery: ${error.message || String(error)}`);
|
|
3315
|
-
}
|
|
3316
|
-
}
|
|
3339
|
+
if (connectedEmbedding && typeof connectedEmbedding.embedQuery === 'function' && String(query || '').trim())
|
|
3340
|
+
connectedAi.embeddingQuery = vectorMemoryEnabled;
|
|
3317
3341
|
try {
|
|
3318
3342
|
this.logger?.debug('Tembory loading memory variables before model invocation', {
|
|
3319
3343
|
itemIndex,
|
|
@@ -3325,6 +3349,28 @@ class Mem0Memory {
|
|
|
3325
3349
|
catch { }
|
|
3326
3350
|
let payload;
|
|
3327
3351
|
let vectorMemories = [];
|
|
3352
|
+
if (!vectorMemoryEnabled && backendPersistenceEnabled && connectedEmbedding && String(query || '').trim()) {
|
|
3353
|
+
try {
|
|
3354
|
+
const archiveBody = {
|
|
3355
|
+
user_id: key,
|
|
3356
|
+
agent_id: adv.agentId ? String(adv.agentId) : undefined,
|
|
3357
|
+
run_id: adv.runId ? String(adv.runId) : undefined,
|
|
3358
|
+
top_k: Math.max(Number(adv.lastN || 0), Number(adv.maxReturn || 12), 12),
|
|
3359
|
+
filters: { kind: 'turn_archive' },
|
|
3360
|
+
};
|
|
3361
|
+
const archiveRes = await searchClientVectorMemories(this, connectedEmbedding, `latest turn archive for thread ${threadId}\n${query}`, archiveBody);
|
|
3362
|
+
vectorMemories = normalizeResults(archiveRes).map((memory) => withTemboryScore(memory, {
|
|
3363
|
+
semanticScore: scoreOf(memory),
|
|
3364
|
+
source: 'turn_archive_lookup',
|
|
3365
|
+
}));
|
|
3366
|
+
connectedAi.embedding = true;
|
|
3367
|
+
connectedAi.embeddingQuery = true;
|
|
3368
|
+
connectedAi.turnArchiveLookup = true;
|
|
3369
|
+
}
|
|
3370
|
+
catch (error) {
|
|
3371
|
+
connectedAi.errors.push(`turnArchiveLookup: ${error.message || String(error)}`);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3328
3374
|
if (vectorMemoryEnabled && (retrievalMode === 'semantic' || retrievalMode === 'semanticV2' || retrievalMode === 'hybrid')) {
|
|
3329
3375
|
const body = { query: String(query || '') };
|
|
3330
3376
|
// IDs
|
package/package.json
CHANGED