n8n-nodes-tembory 1.0.7 → 1.0.9
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.
|
@@ -43,7 +43,7 @@ const getConnectedEmbedding = async (ctx, itemIndex) => {
|
|
|
43
43
|
};
|
|
44
44
|
const createClientVectorMemory = async (embedding, text, metadata, ids = {}) => ({
|
|
45
45
|
text: truncate(text, 4000),
|
|
46
|
-
embedding: await embedding
|
|
46
|
+
embedding: await embedQueryCached(embedding, String(text || '')),
|
|
47
47
|
user_id: ids.user_id,
|
|
48
48
|
agent_id: ids.agent_id,
|
|
49
49
|
run_id: ids.run_id,
|
|
@@ -67,7 +67,7 @@ const searchClientVectorMemories = async (ctx, embedding, query, body = {}) => {
|
|
|
67
67
|
return GenericFunctions_1.mem0ApiRequest.call(ctx, 'POST', '/elefai/v1/vector-memories/search', {
|
|
68
68
|
...body,
|
|
69
69
|
query: text,
|
|
70
|
-
embedding: await embedding
|
|
70
|
+
embedding: await embedQueryCached(embedding, text),
|
|
71
71
|
});
|
|
72
72
|
};
|
|
73
73
|
const stableStringify = (value) => {
|
|
@@ -87,6 +87,31 @@ const hashString = (value) => {
|
|
|
87
87
|
return (hash >>> 0).toString(36);
|
|
88
88
|
};
|
|
89
89
|
const stableHash = (value) => hashString(typeof value === 'string' ? value : stableStringify(value));
|
|
90
|
+
const embeddingCacheByModel = new WeakMap();
|
|
91
|
+
const MAX_EMBEDDING_CACHE_ITEMS = 500;
|
|
92
|
+
const embedQueryCached = async (embedding, text) => {
|
|
93
|
+
const query = String(text || '');
|
|
94
|
+
if (!embedding || typeof embedding.embedQuery !== 'function')
|
|
95
|
+
return undefined;
|
|
96
|
+
let cache = embeddingCacheByModel.get(embedding);
|
|
97
|
+
if (!cache) {
|
|
98
|
+
cache = new Map();
|
|
99
|
+
embeddingCacheByModel.set(embedding, cache);
|
|
100
|
+
}
|
|
101
|
+
const key = stableHash(query);
|
|
102
|
+
if (cache.has(key))
|
|
103
|
+
return cache.get(key);
|
|
104
|
+
const promise = Promise.resolve(embedding.embedQuery(query)).catch((error) => {
|
|
105
|
+
cache.delete(key);
|
|
106
|
+
throw error;
|
|
107
|
+
});
|
|
108
|
+
cache.set(key, promise);
|
|
109
|
+
if (cache.size > MAX_EMBEDDING_CACHE_ITEMS) {
|
|
110
|
+
const firstKey = cache.keys().next().value;
|
|
111
|
+
cache.delete(firstKey);
|
|
112
|
+
}
|
|
113
|
+
return promise;
|
|
114
|
+
};
|
|
90
115
|
const pickText = (values, preferredKeys) => {
|
|
91
116
|
for (const key of preferredKeys) {
|
|
92
117
|
const value = values === null || values === void 0 ? void 0 : values[key];
|
|
@@ -928,6 +953,34 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
928
953
|
toolHistoryLastN: 5,
|
|
929
954
|
recentMessagesLastN: 4,
|
|
930
955
|
},
|
|
956
|
+
productionNano: {
|
|
957
|
+
compactForAgent: true,
|
|
958
|
+
includeContextHeader: true,
|
|
959
|
+
includeSummary: true,
|
|
960
|
+
includeScores: false,
|
|
961
|
+
includeDiagnostics: false,
|
|
962
|
+
includeRelations: false,
|
|
963
|
+
includeToolHistory: true,
|
|
964
|
+
includeToolResults: true,
|
|
965
|
+
persistToolFactsToMem0: false,
|
|
966
|
+
includeToolHistorySemanticFallback: false,
|
|
967
|
+
includeProfileFacts: true,
|
|
968
|
+
includeOperationalState: true,
|
|
969
|
+
includeActionLedger: false,
|
|
970
|
+
includeEntityTimeline: false,
|
|
971
|
+
includeWorkingMemory: true,
|
|
972
|
+
includeDecisionState: true,
|
|
973
|
+
includeMemoryCompression: true,
|
|
974
|
+
includeRecentMessages: false,
|
|
975
|
+
includeRecentHighlights: false,
|
|
976
|
+
topK: 4,
|
|
977
|
+
lastN: 4,
|
|
978
|
+
maxReturn: 6,
|
|
979
|
+
toolHistoryLastN: 6,
|
|
980
|
+
recentMessagesLastN: 2,
|
|
981
|
+
vectorMemoryMaxChars: 220,
|
|
982
|
+
contextMaxChars: 6000,
|
|
983
|
+
},
|
|
931
984
|
audit: {
|
|
932
985
|
includeContextHeader: true,
|
|
933
986
|
includeSummary: true,
|
|
@@ -1308,6 +1361,105 @@ const contextMemoryText = (memory, max = 700) => {
|
|
|
1308
1361
|
}
|
|
1309
1362
|
return truncate(text, max);
|
|
1310
1363
|
};
|
|
1364
|
+
const approxTokenCount = (text) => Math.ceil(String(text || '').length / 4);
|
|
1365
|
+
const importantJsonFields = ['reservation_id', 'confirmation_id', 'selected_from_message', 'status', 'next_step', 'available_slots', 'timezone', 'error'];
|
|
1366
|
+
const pickImportantFields = (value) => {
|
|
1367
|
+
if (value === null || value === undefined)
|
|
1368
|
+
return {};
|
|
1369
|
+
const out = {};
|
|
1370
|
+
const visit = (item) => {
|
|
1371
|
+
if (!item || typeof item !== 'object')
|
|
1372
|
+
return;
|
|
1373
|
+
if (Array.isArray(item)) {
|
|
1374
|
+
item.slice(0, 3).forEach(visit);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
for (const key of importantJsonFields) {
|
|
1378
|
+
if (item[key] !== undefined && out[key] === undefined)
|
|
1379
|
+
out[key] = item[key];
|
|
1380
|
+
}
|
|
1381
|
+
if (item.args)
|
|
1382
|
+
visit(item.args);
|
|
1383
|
+
if (item.result)
|
|
1384
|
+
visit(item.result);
|
|
1385
|
+
};
|
|
1386
|
+
visit(value);
|
|
1387
|
+
return out;
|
|
1388
|
+
};
|
|
1389
|
+
const compactToolResult = (result, max = 360) => {
|
|
1390
|
+
const text = String(result || '');
|
|
1391
|
+
if (!text.trim())
|
|
1392
|
+
return undefined;
|
|
1393
|
+
try {
|
|
1394
|
+
const parsed = JSON.parse(text);
|
|
1395
|
+
const picked = pickImportantFields(parsed);
|
|
1396
|
+
if (Object.keys(picked).length)
|
|
1397
|
+
return truncate(safeStringify(picked), max);
|
|
1398
|
+
}
|
|
1399
|
+
catch { }
|
|
1400
|
+
const picked = {};
|
|
1401
|
+
for (const key of importantJsonFields) {
|
|
1402
|
+
const re = new RegExp(`["']?${key}["']?\\s*[:=]\\s*["']?([^"',}\\]]+)`, 'i');
|
|
1403
|
+
const match = re.exec(text);
|
|
1404
|
+
if (match)
|
|
1405
|
+
picked[key] = match[1];
|
|
1406
|
+
}
|
|
1407
|
+
if (Object.keys(picked).length)
|
|
1408
|
+
return truncate(safeStringify(picked), max);
|
|
1409
|
+
return truncate(text, max);
|
|
1410
|
+
};
|
|
1411
|
+
const compactToolHistoryForAgent = (toolHistory = [], maxItems = 6, includeResults = true) => pruneByLimit(toolHistory || [], maxItems).map((tool) => ({
|
|
1412
|
+
name: tool.name,
|
|
1413
|
+
status: tool.ok === false ? 'failed' : 'ok',
|
|
1414
|
+
at: tool.at,
|
|
1415
|
+
input: truncate(String(tool.input || ''), 180) || undefined,
|
|
1416
|
+
result: includeResults ? compactToolResult(tool.result, 360) : undefined,
|
|
1417
|
+
}));
|
|
1418
|
+
const compactOperationalStateForAgent = (state = {}) => {
|
|
1419
|
+
const agenda = state.agenda_state || {};
|
|
1420
|
+
return {
|
|
1421
|
+
last_tool: state.last_tool || null,
|
|
1422
|
+
tool_counts: state.tool_counts || {},
|
|
1423
|
+
agenda_state: {
|
|
1424
|
+
has_availability: Boolean(agenda.has_availability),
|
|
1425
|
+
has_pre_reservation: Boolean(agenda.has_pre_reservation),
|
|
1426
|
+
has_confirmation: Boolean(agenda.has_confirmation),
|
|
1427
|
+
has_pending_pre_reservation: Boolean(agenda.has_pending_pre_reservation),
|
|
1428
|
+
latest_availability: compactToolResult(agenda.latest_availability_result),
|
|
1429
|
+
latest_pre_reservation: compactToolResult(agenda.latest_pre_reservation_result),
|
|
1430
|
+
latest_confirmation: compactToolResult(agenda.latest_confirmation_result),
|
|
1431
|
+
},
|
|
1432
|
+
blocked_without_context: state.blocked_without_context || [],
|
|
1433
|
+
guidance: (state.guidance || []).slice(0, 3),
|
|
1434
|
+
};
|
|
1435
|
+
};
|
|
1436
|
+
const compactMemoryCompressionForAgent = (compression = {}) => ({
|
|
1437
|
+
turn_summary: (compression.turn_summary || []).slice(-3),
|
|
1438
|
+
session_tools: (((compression.session_summary || {}).tools) || []).slice(-6),
|
|
1439
|
+
agenda_state: ((compression.session_summary || {}).agenda_state) ? {
|
|
1440
|
+
has_availability: Boolean(compression.session_summary.agenda_state.has_availability),
|
|
1441
|
+
has_pre_reservation: Boolean(compression.session_summary.agenda_state.has_pre_reservation),
|
|
1442
|
+
has_confirmation: Boolean(compression.session_summary.agenda_state.has_confirmation),
|
|
1443
|
+
has_pending_pre_reservation: Boolean(compression.session_summary.agenda_state.has_pending_pre_reservation),
|
|
1444
|
+
} : undefined,
|
|
1445
|
+
active_memory_count: ((compression.workflow_summary || {}).active_memory_count) || 0,
|
|
1446
|
+
});
|
|
1447
|
+
const compactVectorMemoriesForAgent = (vectorMemories = [], toolHistory = [], maxItems = 3) => {
|
|
1448
|
+
const hasStructuredTools = Array.isArray(toolHistory) && toolHistory.length > 0;
|
|
1449
|
+
return (vectorMemories || [])
|
|
1450
|
+
.map((memory) => contextMemoryText(memory, 220))
|
|
1451
|
+
.filter(Boolean)
|
|
1452
|
+
.filter((text) => !hasStructuredTools || !/^\[tool_events_extracted\]/i.test(text))
|
|
1453
|
+
.slice(0, maxItems);
|
|
1454
|
+
};
|
|
1455
|
+
const contextSizeOfMessages = (messages = []) => {
|
|
1456
|
+
const perMessage = (messages || []).map((message, index) => {
|
|
1457
|
+
const content = String(message.content || '');
|
|
1458
|
+
return { index, role: message.role || 'system', chars: content.length, approx_tokens: approxTokenCount(content) };
|
|
1459
|
+
});
|
|
1460
|
+
const chars = perMessage.reduce((sum, item) => sum + item.chars, 0);
|
|
1461
|
+
return { chars, approx_tokens: approxTokenCount((messages || []).map((m) => m.content || '').join('\n')), messages: perMessage };
|
|
1462
|
+
};
|
|
1311
1463
|
const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
|
|
1312
1464
|
get(target, prop) {
|
|
1313
1465
|
if (prop === 'loadMemoryVariables') {
|
|
@@ -1317,11 +1469,15 @@ const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
|
|
|
1317
1469
|
]);
|
|
1318
1470
|
try {
|
|
1319
1471
|
const response = await target.loadMemoryVariables(values);
|
|
1320
|
-
const
|
|
1472
|
+
const cacheHit = Boolean(response.elefaiBrainDiagnostics && response.elefaiBrainDiagnostics.cacheHit);
|
|
1473
|
+
const chatHistory = cacheHit
|
|
1474
|
+
? [{ cached: true, messages: Array.isArray(response[memoryKey] || response.chatHistory) ? (response[memoryKey] || response.chatHistory).length : 0 }]
|
|
1475
|
+
: snapshotJson(response[memoryKey] || response.chatHistory || []);
|
|
1321
1476
|
ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiMemory, index, [
|
|
1322
1477
|
[{
|
|
1323
1478
|
json: {
|
|
1324
1479
|
action: 'loadMemoryVariables',
|
|
1480
|
+
cached: cacheHit || undefined,
|
|
1325
1481
|
chatHistory,
|
|
1326
1482
|
context: response.elefaiBrainContext,
|
|
1327
1483
|
contextText: response.elefaiBrainContextText,
|
|
@@ -1377,14 +1533,81 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
1377
1533
|
const includeHeader = adv.includeContextHeader !== false;
|
|
1378
1534
|
const includeSummary = adv.includeSummary !== false;
|
|
1379
1535
|
const includeScores = adv.includeScores !== false;
|
|
1536
|
+
const compactForAgent = Boolean(adv.compactForAgent);
|
|
1380
1537
|
const sections = [];
|
|
1381
1538
|
if (includeHeader) {
|
|
1382
1539
|
sections.push({
|
|
1383
1540
|
section: 'context_header',
|
|
1384
1541
|
title: 'Tembory context',
|
|
1385
|
-
value:
|
|
1542
|
+
value: compactForAgent
|
|
1543
|
+
? 'Read-only memory. 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.'
|
|
1544
|
+
: 'Use this context as read-only memory. Prefer it over guessing. Do not mention internal section names to the user. 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.',
|
|
1386
1545
|
});
|
|
1387
1546
|
}
|
|
1547
|
+
if (compactForAgent) {
|
|
1548
|
+
if (includeSummary) {
|
|
1549
|
+
const summary = compactVectorMemoriesForAgent(vectorMemories, toolHistory, Number(adv.summaryMaxFacts || 3));
|
|
1550
|
+
sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no non-tool vector memories to summarize' });
|
|
1551
|
+
}
|
|
1552
|
+
sections.push({
|
|
1553
|
+
section: 'working_memory',
|
|
1554
|
+
title: 'Working memory',
|
|
1555
|
+
value: adv.includeWorkingMemory === false ? null : (workingMemory || {}),
|
|
1556
|
+
why_null: adv.includeWorkingMemory === false ? 'working memory disabled' : undefined,
|
|
1557
|
+
});
|
|
1558
|
+
sections.push({
|
|
1559
|
+
section: 'decision_state',
|
|
1560
|
+
title: 'Decision state',
|
|
1561
|
+
value: adv.includeDecisionState === false ? null : (decisionState || {}),
|
|
1562
|
+
why_null: adv.includeDecisionState === false ? 'decision state disabled' : undefined,
|
|
1563
|
+
});
|
|
1564
|
+
sections.push({
|
|
1565
|
+
section: 'operational_state',
|
|
1566
|
+
title: 'Operational state',
|
|
1567
|
+
value: adv.includeOperationalState === false ? null : compactOperationalStateForAgent(operationalState || {}),
|
|
1568
|
+
why_null: adv.includeOperationalState === false ? 'operational state disabled' : undefined,
|
|
1569
|
+
});
|
|
1570
|
+
sections.push({
|
|
1571
|
+
section: 'tool_history',
|
|
1572
|
+
title: 'Tool history',
|
|
1573
|
+
value: {
|
|
1574
|
+
enabled: adv.includeToolHistory !== false,
|
|
1575
|
+
items: adv.includeToolHistory === false ? [] : compactToolHistoryForAgent(toolHistory, adv.toolHistoryLastN || 6, adv.includeToolResults !== false),
|
|
1576
|
+
},
|
|
1577
|
+
});
|
|
1578
|
+
sections.push({
|
|
1579
|
+
section: 'profile_facts',
|
|
1580
|
+
title: 'Profile facts',
|
|
1581
|
+
value: adv.includeProfileFacts === false ? null : renderProfileFacts(profileFacts),
|
|
1582
|
+
why_null: adv.includeProfileFacts === false ? 'profile facts disabled' : undefined,
|
|
1583
|
+
});
|
|
1584
|
+
sections.push({
|
|
1585
|
+
section: 'memory_compression',
|
|
1586
|
+
title: 'Memory compression',
|
|
1587
|
+
value: adv.includeMemoryCompression === false ? null : compactMemoryCompressionForAgent(memoryCompression || {}),
|
|
1588
|
+
why_null: adv.includeMemoryCompression === false ? 'memory compression disabled' : undefined,
|
|
1589
|
+
});
|
|
1590
|
+
const audit = {
|
|
1591
|
+
kind: 'elefai.brain.context.v1',
|
|
1592
|
+
meta: { user_id: userId, payloadFormat, query, compact_for_agent: true },
|
|
1593
|
+
sections,
|
|
1594
|
+
diagnostics,
|
|
1595
|
+
};
|
|
1596
|
+
const renderCompactSection = (section) => {
|
|
1597
|
+
if (section.value === null || section.value === undefined)
|
|
1598
|
+
return `## ${section.title}\n${section.why_null || 'empty'}`;
|
|
1599
|
+
if (typeof section.value === 'string')
|
|
1600
|
+
return `## ${section.title}\n${section.value}`;
|
|
1601
|
+
if (Array.isArray(section.value))
|
|
1602
|
+
return `## ${section.title}\n${section.value.length ? section.value.map((item) => typeof item === 'string' ? `- ${item}` : `- ${truncate(safeStringify(item), 420)}`).join('\n') : '(empty)'}`;
|
|
1603
|
+
return `## ${section.title}\n${truncate(safeStringify(section.value), 900)}`;
|
|
1604
|
+
};
|
|
1605
|
+
if (payloadFormat === 'auditJson')
|
|
1606
|
+
return [{ role: 'system', content: JSON.stringify(audit, null, 2) }];
|
|
1607
|
+
if (payloadFormat === 'auditBlocks')
|
|
1608
|
+
return sections.map((section) => ({ role: 'system', content: renderCompactSection(section) }));
|
|
1609
|
+
return [{ role: 'system', content: truncate(sections.map(renderCompactSection).join('\n\n'), Number(adv.contextMaxChars || 6000)) }];
|
|
1610
|
+
}
|
|
1388
1611
|
if (includeSummary) {
|
|
1389
1612
|
const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 360)).filter(Boolean);
|
|
1390
1613
|
sections.push({ section: 'summary', title: 'Summary', value: summary.length ? summary : null, why_null: summary.length ? undefined : 'no vector memories to summarize' });
|
|
@@ -1654,6 +1877,7 @@ class Mem0Memory {
|
|
|
1654
1877
|
{ name: 'Diagnóstico Completo', value: 'diagnostic' },
|
|
1655
1878
|
{ name: 'Produção Balanceada', value: 'productionBalanced' },
|
|
1656
1879
|
{ name: 'Produção Econômica', value: 'productionCheap' },
|
|
1880
|
+
{ name: 'Produção Nano/SLM', value: 'productionNano' },
|
|
1657
1881
|
{ name: 'Auditoria', value: 'audit' },
|
|
1658
1882
|
],
|
|
1659
1883
|
default: 'custom',
|
|
@@ -1673,6 +1897,8 @@ class Mem0Memory {
|
|
|
1673
1897
|
{ displayName: 'MMR (diversidade)', name: 'mmr', type: 'boolean', default: true, description: 'Aplicar diversidade de resultados (Maximal Marginal Relevance)', displayOptions: { show: { '/retrievalMode': ['hybrid'] } } },
|
|
1674
1898
|
{ displayName: 'MMR Lambda', name: 'mmrLambda', type: 'number', typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 }, default: 0.5, description: 'Equilíbrio entre relevância e diversidade no MMR', displayOptions: { show: { '/retrievalMode': ['hybrid'] } } },
|
|
1675
1899
|
{ displayName: 'Incluir Cabeçalho de Contexto', name: 'includeContextHeader', type: 'boolean', default: true },
|
|
1900
|
+
{ displayName: 'Compactar Contexto Para Agente', name: 'compactForAgent', type: 'boolean', default: false, description: 'Entrega ao agente apenas o estado acionável: working memory, decisões, estado operacional e histórico de tools compacto. Mantém detalhes completos no audit/debug.' },
|
|
1901
|
+
{ displayName: 'Máximo de Caracteres do Contexto', name: 'contextMaxChars', type: 'number', default: 6000, description: 'Limite final aplicado quando Compactar Contexto Para Agente estiver ativo.' },
|
|
1676
1902
|
{ displayName: 'Incluir Resumo', name: 'includeSummary', type: 'boolean', default: true },
|
|
1677
1903
|
{ displayName: 'Máximo de Fatos no Resumo', name: 'summaryMaxFacts', type: 'number', default: 4 },
|
|
1678
1904
|
{ displayName: 'Incluir Scores', name: 'includeScores', type: 'boolean', default: true },
|
|
@@ -1706,6 +1932,7 @@ class Mem0Memory {
|
|
|
1706
1932
|
async supplyData(itemIndex) {
|
|
1707
1933
|
const memoryKey = this.getNodeParameter('memoryKey', itemIndex);
|
|
1708
1934
|
let currentMessages = [];
|
|
1935
|
+
const loadCache = new Map();
|
|
1709
1936
|
const memory = {
|
|
1710
1937
|
memoryKeys: [memoryKey],
|
|
1711
1938
|
inputKey: 'input',
|
|
@@ -1736,7 +1963,38 @@ class Mem0Memory {
|
|
|
1736
1963
|
},
|
|
1737
1964
|
},
|
|
1738
1965
|
loadMemoryVariables: async (inputValues = {}) => {
|
|
1739
|
-
const
|
|
1966
|
+
const cacheKey = stableHash({ itemIndex, inputValues, memoryKey });
|
|
1967
|
+
let result;
|
|
1968
|
+
if (loadCache.has(cacheKey)) {
|
|
1969
|
+
result = snapshotJson(loadCache.get(cacheKey));
|
|
1970
|
+
result.response = result.response || {};
|
|
1971
|
+
result.response.elefaiBrainDiagnostics = {
|
|
1972
|
+
...(result.response.elefaiBrainDiagnostics || {}),
|
|
1973
|
+
cacheHit: true,
|
|
1974
|
+
cacheScope: 'supplyData.loadMemoryVariables',
|
|
1975
|
+
};
|
|
1976
|
+
if (result.response.elefaiBrainContext) {
|
|
1977
|
+
result.response.elefaiBrainContext = {
|
|
1978
|
+
kind: result.response.elefaiBrainContext.kind,
|
|
1979
|
+
userId: result.response.elefaiBrainContext.userId,
|
|
1980
|
+
project: result.response.elefaiBrainContext.project,
|
|
1981
|
+
query: result.response.elefaiBrainContext.query,
|
|
1982
|
+
retrievalMode: result.response.elefaiBrainContext.retrievalMode,
|
|
1983
|
+
payloadFormat: result.response.elefaiBrainContext.payloadFormat,
|
|
1984
|
+
contextQualityScore: result.response.elefaiBrainContext.contextQualityScore,
|
|
1985
|
+
cacheHit: true,
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
delete result.response.elefaiBrainContextText;
|
|
1989
|
+
}
|
|
1990
|
+
else {
|
|
1991
|
+
result = await Mem0Memory.prototype.loadMemoryVariablesForItem.call(this, itemIndex, inputValues);
|
|
1992
|
+
loadCache.set(cacheKey, snapshotJson(result));
|
|
1993
|
+
if (loadCache.size > 20) {
|
|
1994
|
+
const firstKey = loadCache.keys().next().value;
|
|
1995
|
+
loadCache.delete(firstKey);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1740
1998
|
const chatHistory = (result.response[memoryKey] || []).map(toBaseMessage);
|
|
1741
1999
|
const extra = { ...(result.response || {}) };
|
|
1742
2000
|
delete extra[memoryKey];
|
|
@@ -1748,6 +2006,7 @@ class Mem0Memory {
|
|
|
1748
2006
|
};
|
|
1749
2007
|
},
|
|
1750
2008
|
saveContext: async (inputValues = {}, outputValues = {}) => {
|
|
2009
|
+
loadCache.clear();
|
|
1751
2010
|
const input = pickText(inputValues, ['input', 'chatInput', 'text', 'query', 'question']);
|
|
1752
2011
|
const output = pickText(outputValues, ['output', 'response', 'text', 'answer']);
|
|
1753
2012
|
if (input)
|
|
@@ -2030,7 +2289,7 @@ class Mem0Memory {
|
|
|
2030
2289
|
}
|
|
2031
2290
|
if (connectedEmbedding && typeof connectedEmbedding.embedQuery === 'function' && String(query || '').trim()) {
|
|
2032
2291
|
try {
|
|
2033
|
-
await connectedEmbedding
|
|
2292
|
+
await embedQueryCached(connectedEmbedding, String(query));
|
|
2034
2293
|
connectedAi.embeddingQuery = true;
|
|
2035
2294
|
}
|
|
2036
2295
|
catch (error) {
|
|
@@ -2270,10 +2529,10 @@ class Mem0Memory {
|
|
|
2270
2529
|
catch { }
|
|
2271
2530
|
}
|
|
2272
2531
|
let connectedModelSummary = '';
|
|
2273
|
-
if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' &&
|
|
2532
|
+
if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && vectorMemories.length && adv.includeSummary !== false) {
|
|
2274
2533
|
try {
|
|
2275
2534
|
const facts = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 500)).filter(Boolean).join('\n') || '(no vector memories found)';
|
|
2276
|
-
if (facts
|
|
2535
|
+
if (facts) {
|
|
2277
2536
|
const response = await connectedLanguageModel.invoke([
|
|
2278
2537
|
toBaseMessage({
|
|
2279
2538
|
role: 'user',
|
|
@@ -2362,6 +2621,7 @@ class Mem0Memory {
|
|
|
2362
2621
|
diagnostics,
|
|
2363
2622
|
adv,
|
|
2364
2623
|
});
|
|
2624
|
+
diagnostics.contextSize = contextSizeOfMessages(payload);
|
|
2365
2625
|
const normalizedVectorMemories = vectorMemories.map((m) => {
|
|
2366
2626
|
const scoreMeta = scoreMetaOf(m);
|
|
2367
2627
|
const rawScore = scoreOf(m);
|
|
@@ -2404,6 +2664,7 @@ class Mem0Memory {
|
|
|
2404
2664
|
includeOperationalState: adv.includeOperationalState !== false,
|
|
2405
2665
|
includeActionLedger: adv.includeActionLedger !== false,
|
|
2406
2666
|
includeEntityTimeline: adv.includeEntityTimeline !== false,
|
|
2667
|
+
compactForAgent: Boolean(adv.compactForAgent),
|
|
2407
2668
|
includeWorkingMemory: adv.includeWorkingMemory !== false,
|
|
2408
2669
|
includeDecisionState: adv.includeDecisionState !== false,
|
|
2409
2670
|
includeMemoryCompression: adv.includeMemoryCompression !== false,
|
|
@@ -2587,4 +2848,10 @@ exports.__private = {
|
|
|
2587
2848
|
mergeProfileFacts,
|
|
2588
2849
|
renderProfileFacts,
|
|
2589
2850
|
isNoisyProfileValue,
|
|
2851
|
+
approxTokenCount,
|
|
2852
|
+
contextSizeOfMessages,
|
|
2853
|
+
embedQueryCached,
|
|
2854
|
+
compactToolResult,
|
|
2855
|
+
compactToolHistoryForAgent,
|
|
2856
|
+
compactOperationalStateForAgent,
|
|
2590
2857
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-tembory",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Tembory node for n8n AI Agents with profile, tools, timeline, graph and semantic memory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://tembory.com",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json",
|
|
25
25
|
"test": "node --test test/*.test.js",
|
|
26
26
|
"smoke:n8n:multiturn": "node scripts/smoke-n8n-multiturn-tools.js",
|
|
27
|
-
"
|
|
27
|
+
"simulate:agent-context": "node scripts/simulate-agent-context.js",
|
|
28
|
+
"prepublishOnly": "npm test"
|
|
28
29
|
},
|
|
29
30
|
"files": [
|
|
30
31
|
"dist",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const Module = require('node:module');
|
|
3
|
+
|
|
4
|
+
const originalLoad = Module._load;
|
|
5
|
+
Module._load = function patchedLoad(request, parent, isMain) {
|
|
6
|
+
if (request === 'n8n-workflow') {
|
|
7
|
+
return {
|
|
8
|
+
NodeConnectionTypes: {
|
|
9
|
+
AiMemory: 'ai_memory',
|
|
10
|
+
AiLanguageModel: 'ai_languageModel',
|
|
11
|
+
AiEmbedding: 'ai_embedding',
|
|
12
|
+
},
|
|
13
|
+
NodeApiError: class NodeApiError extends Error {},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (request === '@langchain/core/messages') {
|
|
17
|
+
return {
|
|
18
|
+
HumanMessage: class HumanMessage { constructor(content) { this.content = content; } _getType() { return 'human'; } },
|
|
19
|
+
AIMessage: class AIMessage { constructor(content) { this.content = content; } _getType() { return 'ai'; } },
|
|
20
|
+
SystemMessage: class SystemMessage { constructor(content) { this.content = content; } _getType() { return 'system'; } },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return originalLoad.call(this, request, parent, isMain);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const core = require('../dist/nodes/Mem0/Mem0Memory.node.js').__private;
|
|
27
|
+
|
|
28
|
+
const toolHistory = [
|
|
29
|
+
{
|
|
30
|
+
id: 'tool_ry3twg',
|
|
31
|
+
name: 'agenda_consultar_disponibilidade',
|
|
32
|
+
input: '',
|
|
33
|
+
ok: true,
|
|
34
|
+
at: '2026-05-14T05:34:12.460Z',
|
|
35
|
+
source: 'used_tools_text',
|
|
36
|
+
result: '[{"args":{"tool":"agenda_consultar_disponibilidade","available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30","timezone":"America/Sao_Paulo","next_step":"escolher_um_horario"}}]',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'tool_mu9qd2',
|
|
40
|
+
name: 'agenda_pre_reservar_horario',
|
|
41
|
+
input: '',
|
|
42
|
+
ok: true,
|
|
43
|
+
at: '2026-05-14T05:34:34.017Z',
|
|
44
|
+
source: 'used_tools_text',
|
|
45
|
+
result: '[{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-ELEFAI-LAB-001","selected_from_message":"dia 13","status":"pre_reserved","next_step":"confirmar_agendamento"}}]',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const vectorMemories = [
|
|
50
|
+
{
|
|
51
|
+
memory: '[Used tools: Tool: agenda_pre_reservar_horario, Input: , Result: [{"args":{"tool":"agenda_pre_reservar_horario","reservation_id":"RES-ELEFAI-LAB-001","selected_from_message":"dia 13","status":"pre_reserved","next_step":"confirmar_agendamento"}}]] Calling agenda_pre_reservar_horario with input: {"id":"call_bfHlltcjI1vCagRkav0fj8Ha"}\nPré-reserva feita para o dia 13.\nSe quiser, posso confirmar agora.',
|
|
52
|
+
created_at: '2026-05-14T05:34:35.373020Z',
|
|
53
|
+
elefaiScore: { semanticScore: 0.71, recencyScore: 0.99, hybridScore: 0.81, source: 'semantic' },
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
memory: '[Used tools: Tool: agenda_consultar_disponibilidade, Input: , Result: [{"args":{"tool":"agenda_consultar_disponibilidade","available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30","timezone":"America/Sao_Paulo","next_step":"escolher_um_horario"}}]] Temos estes horários vagos:\n- 12/05 às 09:00\n- 12/05 às 14:00\n- 13/05 às 10:30',
|
|
57
|
+
created_at: '2026-05-14T05:34:14.717988Z',
|
|
58
|
+
elefaiScore: { semanticScore: 0.7, recencyScore: 0.99, hybridScore: 0.8, source: 'semantic' },
|
|
59
|
+
},
|
|
60
|
+
{ memory: 'Oi! Como posso ajudar?', created_at: '2026-05-14T05:33:47.194630Z' },
|
|
61
|
+
{ memory: 'oi', created_at: '2026-05-14T05:33:47.194355Z' },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const operationalState = core.deriveOperationalState(toolHistory, {}, [], true);
|
|
65
|
+
const workingMemory = {
|
|
66
|
+
current_goal: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
|
|
67
|
+
current_task: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
|
|
68
|
+
last_user_intent: 'affirm',
|
|
69
|
+
last_user_message: 'ok',
|
|
70
|
+
next_expected_action: 'call agenda_confirmar_agendamento now; do not ask the same confirmation question again',
|
|
71
|
+
};
|
|
72
|
+
const decisionState = core.deriveDecisionState({ query: 'ok', toolHistory, operationalState, workingMemory });
|
|
73
|
+
const memoryCompression = core.deriveMemoryCompression({ toolHistory, operationalState, vectorMemories });
|
|
74
|
+
const common = {
|
|
75
|
+
payloadFormat: 'structured',
|
|
76
|
+
query: 'ok',
|
|
77
|
+
userId: 'd48292bd4c9f4eccb9120092e3acd073',
|
|
78
|
+
profileFacts: {},
|
|
79
|
+
workingMemory,
|
|
80
|
+
decisionState,
|
|
81
|
+
memoryCompression,
|
|
82
|
+
operationalState,
|
|
83
|
+
actionLedger: core.deriveActionLedger(toolHistory, 20, true),
|
|
84
|
+
entityTimeline: core.deriveEntityTimeline({}, [], [], toolHistory),
|
|
85
|
+
vectorMemories,
|
|
86
|
+
recentMessages: [],
|
|
87
|
+
toolHistory,
|
|
88
|
+
highlights: [
|
|
89
|
+
'tool agenda_consultar_disponibilidade: [{"args":{"available_slots":"2026-05-12T09:00,2026-05-12T14:00,2026-05-13T10:30"}}]',
|
|
90
|
+
'tool agenda_pre_reservar_horario: [{"args":{"reservation_id":"RES-ELEFAI-LAB-001","status":"pre_reserved"}}]',
|
|
91
|
+
],
|
|
92
|
+
graph: [],
|
|
93
|
+
diagnostics: {},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const modes = [
|
|
97
|
+
['productionBalanced', core.applyOperationalPreset({ operationPreset: 'productionBalanced' })],
|
|
98
|
+
['productionNano', core.applyOperationalPreset({ operationPreset: 'productionNano' })],
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const [name, adv] of modes) {
|
|
102
|
+
const messages = core.buildContextMessages({ ...common, adv });
|
|
103
|
+
const text = messages.map((message) => message.content).join('\n\n');
|
|
104
|
+
console.log(JSON.stringify({
|
|
105
|
+
mode: name,
|
|
106
|
+
size: core.contextSizeOfMessages(messages),
|
|
107
|
+
has_next_expected_action: /next_expected_action/.test(text),
|
|
108
|
+
has_do_not_repeat_tools: /do_not_repeat_tools/.test(text),
|
|
109
|
+
has_reservation_id: /RES-ELEFAI-LAB-001/.test(text),
|
|
110
|
+
has_available_slots: /available_slots/.test(text),
|
|
111
|
+
has_duplicate_calling_text: /Calling agenda_pre_reservar_horario/.test(text),
|
|
112
|
+
}, null, 2));
|
|
113
|
+
}
|