n8n-nodes-tembory 1.0.8 → 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.embedQuery(String(text || '')),
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.embedQuery(text),
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];
@@ -1444,11 +1469,15 @@ const wrapElefaiMemory = (memory, ctx, memoryKey) => new Proxy(memory, {
1444
1469
  ]);
1445
1470
  try {
1446
1471
  const response = await target.loadMemoryVariables(values);
1447
- const chatHistory = snapshotJson(response[memoryKey] || response.chatHistory || []);
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 || []);
1448
1476
  ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiMemory, index, [
1449
1477
  [{
1450
1478
  json: {
1451
1479
  action: 'loadMemoryVariables',
1480
+ cached: cacheHit || undefined,
1452
1481
  chatHistory,
1453
1482
  context: response.elefaiBrainContext,
1454
1483
  contextText: response.elefaiBrainContextText,
@@ -1903,6 +1932,7 @@ class Mem0Memory {
1903
1932
  async supplyData(itemIndex) {
1904
1933
  const memoryKey = this.getNodeParameter('memoryKey', itemIndex);
1905
1934
  let currentMessages = [];
1935
+ const loadCache = new Map();
1906
1936
  const memory = {
1907
1937
  memoryKeys: [memoryKey],
1908
1938
  inputKey: 'input',
@@ -1933,7 +1963,38 @@ class Mem0Memory {
1933
1963
  },
1934
1964
  },
1935
1965
  loadMemoryVariables: async (inputValues = {}) => {
1936
- const result = await Mem0Memory.prototype.loadMemoryVariablesForItem.call(this, itemIndex, inputValues);
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
+ }
1937
1998
  const chatHistory = (result.response[memoryKey] || []).map(toBaseMessage);
1938
1999
  const extra = { ...(result.response || {}) };
1939
2000
  delete extra[memoryKey];
@@ -1945,6 +2006,7 @@ class Mem0Memory {
1945
2006
  };
1946
2007
  },
1947
2008
  saveContext: async (inputValues = {}, outputValues = {}) => {
2009
+ loadCache.clear();
1948
2010
  const input = pickText(inputValues, ['input', 'chatInput', 'text', 'query', 'question']);
1949
2011
  const output = pickText(outputValues, ['output', 'response', 'text', 'answer']);
1950
2012
  if (input)
@@ -2227,7 +2289,7 @@ class Mem0Memory {
2227
2289
  }
2228
2290
  if (connectedEmbedding && typeof connectedEmbedding.embedQuery === 'function' && String(query || '').trim()) {
2229
2291
  try {
2230
- await connectedEmbedding.embedQuery(String(query));
2292
+ await embedQueryCached(connectedEmbedding, String(query));
2231
2293
  connectedAi.embeddingQuery = true;
2232
2294
  }
2233
2295
  catch (error) {
@@ -2467,10 +2529,10 @@ class Mem0Memory {
2467
2529
  catch { }
2468
2530
  }
2469
2531
  let connectedModelSummary = '';
2470
- if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && (vectorMemories.length || String(query || '').trim()) && adv.includeSummary !== false) {
2532
+ if (connectedLanguageModel && typeof connectedLanguageModel.invoke === 'function' && vectorMemories.length && adv.includeSummary !== false) {
2471
2533
  try {
2472
2534
  const facts = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map((memory) => contextMemoryText(memory, 500)).filter(Boolean).join('\n') || '(no vector memories found)';
2473
- if (facts || String(query || '').trim()) {
2535
+ if (facts) {
2474
2536
  const response = await connectedLanguageModel.invoke([
2475
2537
  toBaseMessage({
2476
2538
  role: 'user',
@@ -2788,6 +2850,7 @@ exports.__private = {
2788
2850
  isNoisyProfileValue,
2789
2851
  approxTokenCount,
2790
2852
  contextSizeOfMessages,
2853
+ embedQueryCached,
2791
2854
  compactToolResult,
2792
2855
  compactToolHistoryForAgent,
2793
2856
  compactOperationalStateForAgent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.8",
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",