n8n-nodes-tembory 1.0.12 → 1.0.13

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Node de memoria operacional da Tembory para agentes de IA no n8n.
4
4
 
5
- Versao atual: `1.0.12`.
5
+ Versao atual: `1.0.13`.
6
6
 
7
7
  O Tembory entrega contexto rico para o AI Agent sem depender apenas do historico textual da conversa. Ele combina memoria semantica, working memory, decision state, fatos estaveis do lead, historico de tools, estado operacional, action ledger, timeline de entidades, compressao de memoria, grafo, mensagens recentes e diagnosticos.
8
8
 
@@ -10,6 +10,8 @@ Nos presets de producao, o contexto e organizado como um pacote acionavel: secoe
10
10
 
11
11
  Quando um modelo barato esta conectado ao Tembory, os presets de producao usam esse SLM para gerar um resumo ativo do contexto. Em modo `auto`, ele organiza tanto o estado atual (`working_memory`, `tool_history`, `operational_state`, `decision_state`) quanto memorias vetoriais recuperadas. Assim o primeiro turno ja pode receber highlights uteis mesmo antes de haver resultados do banco vetorial.
12
12
 
13
+ O resumo do SLM usa limites conservadores e cache curto por input para evitar chamadas duplicadas quando o AI Agent carrega a memoria mais de uma vez no mesmo turno.
14
+
13
15
  ## Smoke tecnico
14
16
 
15
17
  Antes de publicar uma versao, rode:
@@ -862,13 +862,15 @@ const getMemoryStore = (ctx) => {
862
862
  data.tembory.workingMemory = data.tembory.workingMemory || {};
863
863
  data.tembory.decisionState = data.tembory.decisionState || {};
864
864
  data.tembory.memoryCompression = data.tembory.memoryCompression || {};
865
+ data.tembory.connectedModelSummaryCache = data.tembory.connectedModelSummaryCache || {};
865
866
  return data.tembory;
866
867
  }
867
868
  catch {
868
- global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {} };
869
+ global.__temboryMemory = global.__temboryMemory || { toolHistory: {}, recentMessages: {}, profileFacts: {}, workingMemory: {}, decisionState: {}, memoryCompression: {}, connectedModelSummaryCache: {} };
869
870
  global.__temboryMemory.workingMemory = global.__temboryMemory.workingMemory || {};
870
871
  global.__temboryMemory.decisionState = global.__temboryMemory.decisionState || {};
871
872
  global.__temboryMemory.memoryCompression = global.__temboryMemory.memoryCompression || {};
873
+ global.__temboryMemory.connectedModelSummaryCache = global.__temboryMemory.connectedModelSummaryCache || {};
872
874
  return global.__temboryMemory;
873
875
  }
874
876
  };
@@ -998,8 +1000,8 @@ const applyOperationalPreset = (advanced = {}) => {
998
1000
  recentMessagesLastN: 2,
999
1001
  vectorMemoryMaxChars: 220,
1000
1002
  contextMaxChars: 6000,
1001
- connectedModelSummaryMaxChars: 700,
1002
- connectedModelSummaryInputMaxChars: 2200,
1003
+ connectedModelSummaryMaxChars: 450,
1004
+ connectedModelSummaryInputMaxChars: 1500,
1003
1005
  },
1004
1006
  audit: {
1005
1007
  summarySource: 'auto',
@@ -1608,7 +1610,7 @@ const buildConnectedModelSummaryInput = ({ query, profileFacts, workingMemory, d
1608
1610
  const hasVectorSignal = includeVectors && Array.isArray(payload.vector_memories) && payload.vector_memories.length > 0;
1609
1611
  if (!hasActiveSignal && !hasVectorSignal)
1610
1612
  return '';
1611
- return truncate(safeStringify(payload), Number(adv.connectedModelSummaryInputMaxChars || 3000));
1613
+ return truncate(safeStringify(payload), Number(adv.connectedModelSummaryInputMaxChars || 2000));
1612
1614
  };
1613
1615
  const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput, adv = {}) => {
1614
1616
  if (!connectedLanguageModel || typeof connectedLanguageModel.invoke !== 'function' || !summaryInput)
@@ -1616,10 +1618,10 @@ const invokeConnectedModelSummary = async (connectedLanguageModel, summaryInput,
1616
1618
  const response = await connectedLanguageModel.invoke([
1617
1619
  toBaseMessage({
1618
1620
  role: 'user',
1619
- content: `Organize the Tembory memory context for the next agent turn. Return concise Portuguese bullets only. Preserve IDs, dates, tool names, next action, do-not-repeat instructions, and contradictions. Do not invent facts.\n\nContext:\n${summaryInput}`,
1621
+ content: `Organize the Tembory memory context for the next agent turn. Return only 3 to 6 short Portuguese bullets. No JSON, no markdown table, no field-by-field dump. Preserve important IDs, dates, tool names, next action, do-not-repeat instructions, and contradictions. Do not invent facts.\n\nContext:\n${summaryInput}`,
1620
1622
  }),
1621
1623
  ]);
1622
- return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 900));
1624
+ return cleanModelSummaryText(response, Number(adv.connectedModelSummaryMaxChars || 700));
1623
1625
  };
1624
1626
  const contextSizeOfMessages = (messages = []) => {
1625
1627
  const perMessage = (messages || []).map((message, index) => {
@@ -2784,7 +2786,26 @@ class Mem0Memory {
2784
2786
  highlights,
2785
2787
  adv,
2786
2788
  });
2787
- connectedModelSummary = await invokeConnectedModelSummary(connectedLanguageModel, summaryInput, adv);
2789
+ const cacheKey = summaryInput ? stableHash({
2790
+ key,
2791
+ source: adv.summarySource || 'auto',
2792
+ input: summaryInput,
2793
+ max: adv.connectedModelSummaryMaxChars || 700,
2794
+ }) : '';
2795
+ const cached = cacheKey ? store.connectedModelSummaryCache[cacheKey] : null;
2796
+ if (cached && cached.summary && Date.now() - Number(cached.at || 0) < Number(adv.connectedModelSummaryCacheTTLSeconds || 60) * 1000) {
2797
+ connectedModelSummary = cached.summary;
2798
+ connectedAi.languageModelSummaryCached = true;
2799
+ }
2800
+ else {
2801
+ connectedModelSummary = await invokeConnectedModelSummary(connectedLanguageModel, summaryInput, adv);
2802
+ if (cacheKey && connectedModelSummary) {
2803
+ store.connectedModelSummaryCache[cacheKey] = { summary: connectedModelSummary, at: Date.now() };
2804
+ const keys = Object.keys(store.connectedModelSummaryCache);
2805
+ for (const oldKey of keys.slice(0, Math.max(0, keys.length - Number(adv.connectedModelSummaryCacheMaxItems || 50))))
2806
+ delete store.connectedModelSummaryCache[oldKey];
2807
+ }
2808
+ }
2788
2809
  connectedAi.languageModelSummary = Boolean(connectedModelSummary);
2789
2810
  }
2790
2811
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-tembory",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
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",