n8n-nodes-tembory 1.1.37 → 1.1.39
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/Tembory/TemboryMemory.node.js +79 -16
- 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.1.
|
|
5
|
+
Versao atual: `1.1.39`.
|
|
6
|
+
|
|
7
|
+
## 1.1.39
|
|
8
|
+
|
|
9
|
+
- Ajusta a semantica de repeticao de tools: `do_not_repeat_tools` fica restrito a turnos de recall, status operacional ou pedido explicito para nao chamar tools.
|
|
10
|
+
- Em turnos normais, o Agent recebe `repeat_tool_policy` e `avoid_repeating_tools_unless_needed`, deixando claro que tools anteriores sao evidencia reutilizavel, nao ferramentas proibidas.
|
|
11
|
+
- Compacta `decisionState.tool_decision_state` para evitar duplicar outputs longos de tools no contexto balanceado.
|
|
12
|
+
|
|
13
|
+
## 1.1.38
|
|
14
|
+
|
|
15
|
+
- Torna o resumo visual independente do tipo da mensagem LangChain, procurando o contexto Tembory em qualquer mensagem carregada.
|
|
16
|
+
- Enriquece eventos `chatHistory.addMessage(s)` com `savedMessages`, input/output resumidos e tool events quando esse for o caminho usado pelo Agent.
|
|
17
|
+
- Mantem a separacao entre contexto interno do Agent e output compacto para humano.
|
|
6
18
|
|
|
7
19
|
## 1.1.37
|
|
8
20
|
|
|
@@ -2046,6 +2046,8 @@ const buildTurnBriefForAgent = ({ query = '', recentMessages = [], toolHistory =
|
|
|
2046
2046
|
status: lastTool.status || (lastTool.ok === false ? 'failed' : 'ok'),
|
|
2047
2047
|
at: lastTool.at || lastTool.timestamp,
|
|
2048
2048
|
} : undefined,
|
|
2049
|
+
repeat_tool_policy: (decisionState || {}).repeat_tool_policy,
|
|
2050
|
+
avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
|
|
2049
2051
|
do_not_repeat_tools: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
|
|
2050
2052
|
guidance: turnBriefGuidanceForIntent(currentIntent),
|
|
2051
2053
|
});
|
|
@@ -2151,6 +2153,19 @@ const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
|
|
|
2151
2153
|
return false;
|
|
2152
2154
|
return ['general_message', 'profile_update', 'unknown'].includes(intent);
|
|
2153
2155
|
};
|
|
2156
|
+
const toolNameSuggestsSideEffect = (name = '') => /(?:^|[_\-. ])(book|booking|reserv\w*|agend\w*|confirm\w*|cancel\w*|cri\w*|create|update|upsert|set|send|enviar|registr\w*|cadastr\w*|charge|cobran\w*|pagamento|ticket)(?:$|[_\-. ])/i.test(String(name || ''));
|
|
2157
|
+
const deriveToolRepeatPolicy = ({ intent = '', query = '', executedTools = [], toolState = {} }) => {
|
|
2158
|
+
const tools = Array.from(new Set(executedTools.filter(Boolean)));
|
|
2159
|
+
const strictMemoryOnlyTurn = intent === 'conversation_recall' || intent === 'operational_status_question' || hasNoToolRequested(query);
|
|
2160
|
+
const sideEffectToolCandidates = tools.filter(toolNameSuggestsSideEffect);
|
|
2161
|
+
return cleanContextValue({
|
|
2162
|
+
mode: strictMemoryOnlyTurn ? 'answer_from_memory_when_possible' : 'conditional_reuse',
|
|
2163
|
+
avoid_repeating_tools_unless_needed: tools,
|
|
2164
|
+
legacy_do_not_repeat_applies: strictMemoryOnlyTurn,
|
|
2165
|
+
side_effect_tool_candidates: sideEffectToolCandidates,
|
|
2166
|
+
instruction: 'Prior tools are evidence, not forbidden tools. Reuse outputs when sufficient; call tools for fresh data, new scope, or current side effects.',
|
|
2167
|
+
});
|
|
2168
|
+
};
|
|
2154
2169
|
const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
|
|
2155
2170
|
const intent = inferUserIntent(query, recentMessages);
|
|
2156
2171
|
const chronological = sortConversationChronological(recentMessages || []);
|
|
@@ -2225,7 +2240,6 @@ const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [
|
|
|
2225
2240
|
};
|
|
2226
2241
|
const deriveDecisionState = ({ query = '', toolHistory = [], operationalState = {}, workingMemory = {} }) => {
|
|
2227
2242
|
const decisions = [];
|
|
2228
|
-
const doNotRepeatTools = [];
|
|
2229
2243
|
const intent = workingMemory.last_user_intent || inferUserIntent(query, []);
|
|
2230
2244
|
const now = nowIso();
|
|
2231
2245
|
const toolState = operationalState.tool_state || {};
|
|
@@ -2244,9 +2258,9 @@ const deriveDecisionState = ({ query = '', toolHistory = [], operationalState =
|
|
|
2244
2258
|
tool: extra.tool,
|
|
2245
2259
|
});
|
|
2246
2260
|
};
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2261
|
+
const executedTools = Array.from(new Set(Object.keys(toolState.counts_by_name || {})));
|
|
2262
|
+
const toolRepeatPolicy = deriveToolRepeatPolicy({ intent, query, executedTools, toolState });
|
|
2263
|
+
const doNotRepeatTools = toolRepeatPolicy.legacy_do_not_repeat_applies ? executedTools : [];
|
|
2250
2264
|
if (toolState.last_successful_tool)
|
|
2251
2265
|
pushDecision('last_successful_tool_recorded', `latest successful tool is ${toolState.last_successful_tool.name}`, 'tool_state records successful tool execution', 'tool_orchestration', { confidence: 0.9, tool: toolState.last_successful_tool.name });
|
|
2252
2266
|
for (const [name, count] of Object.entries(toolState.failed_by_name || {}))
|
|
@@ -2254,15 +2268,18 @@ const deriveDecisionState = ({ query = '', toolHistory = [], operationalState =
|
|
|
2254
2268
|
return {
|
|
2255
2269
|
active_decisions: decisions,
|
|
2256
2270
|
do_not_repeat_tools: Array.from(new Set(doNotRepeatTools)),
|
|
2271
|
+
repeat_tool_policy: toolRepeatPolicy,
|
|
2272
|
+
avoid_repeating_tools_unless_needed: toolRepeatPolicy.avoid_repeating_tools_unless_needed,
|
|
2257
2273
|
current_intent: intent,
|
|
2258
2274
|
tool_decision_state: {
|
|
2259
|
-
executed_tools:
|
|
2275
|
+
executed_tools: executedTools,
|
|
2260
2276
|
failed_tools: Object.keys(toolState.failed_by_name || {}),
|
|
2261
2277
|
last_successful_tool: toolState.last_successful_tool || null,
|
|
2262
2278
|
do_not_repeat_tools: Array.from(new Set(doNotRepeatTools)),
|
|
2279
|
+
repeat_tool_policy: toolRepeatPolicy,
|
|
2263
2280
|
evaluated_at: now,
|
|
2264
2281
|
},
|
|
2265
|
-
conflict_policy: 'prefer active decisions
|
|
2282
|
+
conflict_policy: 'prefer newer active decisions; ignore superseded or expired memories unless auditing',
|
|
2266
2283
|
decision_count: decisions.length,
|
|
2267
2284
|
latest_tool: toolHistory && toolHistory.length ? {
|
|
2268
2285
|
name: toolHistory[toolHistory.length - 1].name,
|
|
@@ -2580,6 +2597,26 @@ const compactWorkingMemoryForAgent = (memory = {}) => cleanContextValue({
|
|
|
2580
2597
|
next_expected_action: memory.next_expected_action,
|
|
2581
2598
|
updated_at: memory.updated_at,
|
|
2582
2599
|
});
|
|
2600
|
+
const compactToolDecisionStateForAgent = (toolDecisionState = {}) => {
|
|
2601
|
+
const lastTool = toolDecisionState.last_successful_tool || null;
|
|
2602
|
+
return cleanContextValue({
|
|
2603
|
+
executed_tools: (toolDecisionState.executed_tools || []).slice(0, 12),
|
|
2604
|
+
failed_tools: (toolDecisionState.failed_tools || []).slice(0, 12),
|
|
2605
|
+
last_successful_tool: lastTool ? cleanContextValue({
|
|
2606
|
+
name: lastTool.name || lastTool.tool_name || lastTool.tool,
|
|
2607
|
+
status: lastTool.status || (lastTool.ok === false ? 'failed' : 'ok'),
|
|
2608
|
+
at: lastTool.at || lastTool.timestamp,
|
|
2609
|
+
}) : undefined,
|
|
2610
|
+
do_not_repeat_tools: (toolDecisionState.do_not_repeat_tools || []).slice(0, 12),
|
|
2611
|
+
evaluated_at: toolDecisionState.evaluated_at,
|
|
2612
|
+
});
|
|
2613
|
+
};
|
|
2614
|
+
const compactRepeatToolPolicyForAgent = (policy = {}) => cleanContextValue({
|
|
2615
|
+
mode: policy.mode,
|
|
2616
|
+
legacy_do_not_repeat_applies: policy.legacy_do_not_repeat_applies,
|
|
2617
|
+
side_effect_tool_candidates: (policy.side_effect_tool_candidates || []).slice(0, 12),
|
|
2618
|
+
instruction: policy.instruction,
|
|
2619
|
+
});
|
|
2583
2620
|
const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
2584
2621
|
current_intent: state.current_intent,
|
|
2585
2622
|
active_decisions: (state.active_decisions || []).slice(-4).map((decision) => cleanContextValue({
|
|
@@ -2592,13 +2629,14 @@ const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
|
2592
2629
|
at: decision.at || decision.updated_at,
|
|
2593
2630
|
})),
|
|
2594
2631
|
do_not_repeat_tools: state.do_not_repeat_tools,
|
|
2595
|
-
|
|
2632
|
+
repeat_tool_policy: state.repeat_tool_policy ? compactRepeatToolPolicyForAgent(state.repeat_tool_policy) : undefined,
|
|
2633
|
+
avoid_repeating_tools_unless_needed: state.avoid_repeating_tools_unless_needed,
|
|
2634
|
+
tool_decision_state: state.tool_decision_state ? compactToolDecisionStateForAgent(state.tool_decision_state) : undefined,
|
|
2596
2635
|
latest_tool: state.latest_tool ? cleanContextValue({
|
|
2597
2636
|
name: state.latest_tool.name,
|
|
2598
2637
|
status: state.latest_tool.status || (state.latest_tool.ok === false ? 'failed' : 'ok'),
|
|
2599
2638
|
at: state.latest_tool.at,
|
|
2600
2639
|
}) : undefined,
|
|
2601
|
-
conflict_policy: state.conflict_policy,
|
|
2602
2640
|
});
|
|
2603
2641
|
const compactOperationalStateForAgent = (state = {}) => {
|
|
2604
2642
|
const counts = state.tool_counts || {};
|
|
@@ -3027,10 +3065,11 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
3027
3065
|
const type = messageTypeOf(message);
|
|
3028
3066
|
return type === 'system' || /system/i.test(String(message?.role || ''));
|
|
3029
3067
|
});
|
|
3030
|
-
|
|
3068
|
+
const contextMessage = firstSystem || list.find((message) => messageContentOf(message).includes('tembory.agent_context.v1')) || list[0];
|
|
3069
|
+
if (!contextMessage)
|
|
3031
3070
|
return summary;
|
|
3032
3071
|
try {
|
|
3033
|
-
const rawContent = messageContentOf(
|
|
3072
|
+
const rawContent = messageContentOf(contextMessage);
|
|
3034
3073
|
let parsed;
|
|
3035
3074
|
try {
|
|
3036
3075
|
parsed = JSON.parse(rawContent);
|
|
@@ -3137,6 +3176,12 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
3137
3176
|
decisionCount: parsed.decisionState?.decision_count,
|
|
3138
3177
|
latestTool: parsed.decisionState?.latest_tool,
|
|
3139
3178
|
doNotRepeatTools: parsed.decisionState?.do_not_repeat_tools,
|
|
3179
|
+
avoidRepeatingToolsUnlessNeeded: parsed.decisionState?.avoid_repeating_tools_unless_needed,
|
|
3180
|
+
repeatToolPolicy: parsed.decisionState?.repeat_tool_policy ? {
|
|
3181
|
+
mode: parsed.decisionState.repeat_tool_policy.mode,
|
|
3182
|
+
legacyDoNotRepeatApplies: parsed.decisionState.repeat_tool_policy.legacy_do_not_repeat_applies,
|
|
3183
|
+
sideEffectToolCandidates: parsed.decisionState.repeat_tool_policy.side_effect_tool_candidates,
|
|
3184
|
+
} : undefined,
|
|
3140
3185
|
});
|
|
3141
3186
|
summary.quality = parsed.contextHealth?.quality_score || parsed.contextQualityScore || undefined;
|
|
3142
3187
|
summary.debug = includeDebug ? cleanContextValue({
|
|
@@ -3150,7 +3195,7 @@ const summarizeMemoryMessagesForSideChannel = (messages = []) => {
|
|
|
3150
3195
|
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== undefined));
|
|
3151
3196
|
}
|
|
3152
3197
|
catch {
|
|
3153
|
-
const content = messageContentOf(
|
|
3198
|
+
const content = messageContentOf(contextMessage);
|
|
3154
3199
|
return cleanContextValue({
|
|
3155
3200
|
...summary,
|
|
3156
3201
|
contextChars: content.length || undefined,
|
|
@@ -3337,7 +3382,9 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
3337
3382
|
value: cleanContextValue({
|
|
3338
3383
|
current_intent: (decisionState || {}).current_intent || workingMemory.last_user_intent,
|
|
3339
3384
|
latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
|
|
3340
|
-
|
|
3385
|
+
repeat_tool_policy: (decisionState || {}).repeat_tool_policy,
|
|
3386
|
+
avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
|
|
3387
|
+
do_not_repeat_tools_legacy: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
|
|
3341
3388
|
instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
|
|
3342
3389
|
}),
|
|
3343
3390
|
});
|
|
@@ -3852,24 +3899,40 @@ class TemboryMemory {
|
|
|
3852
3899
|
addMessage: async (message) => {
|
|
3853
3900
|
currentMessages.push(message);
|
|
3854
3901
|
const saved = await TemboryMemory.prototype.saveMessagesForItem.call(this, itemIndex, [message]);
|
|
3855
|
-
recordMemoryEvent('chatHistory.addMessage', {
|
|
3902
|
+
recordMemoryEvent('chatHistory.addMessage', {
|
|
3903
|
+
saved: Boolean(saved && saved.saved),
|
|
3904
|
+
messages: 1,
|
|
3905
|
+
...summarizeSaveContextForSideChannel((saved && saved.input) || {}, (saved && saved.output) || {}, currentMessages),
|
|
3906
|
+
});
|
|
3856
3907
|
},
|
|
3857
3908
|
addUserMessage: async (message) => {
|
|
3858
3909
|
const baseMessage = toBaseMessage({ role: 'user', content: message });
|
|
3859
3910
|
currentMessages.push(baseMessage);
|
|
3860
3911
|
const saved = await TemboryMemory.prototype.saveMessagesForItem.call(this, itemIndex, [baseMessage]);
|
|
3861
|
-
recordMemoryEvent('chatHistory.addUserMessage', {
|
|
3912
|
+
recordMemoryEvent('chatHistory.addUserMessage', {
|
|
3913
|
+
saved: Boolean(saved && saved.saved),
|
|
3914
|
+
messages: 1,
|
|
3915
|
+
...summarizeSaveContextForSideChannel((saved && saved.input) || {}, (saved && saved.output) || {}, currentMessages),
|
|
3916
|
+
});
|
|
3862
3917
|
},
|
|
3863
3918
|
addAIChatMessage: async (message) => {
|
|
3864
3919
|
const baseMessage = toBaseMessage({ role: 'assistant', content: message });
|
|
3865
3920
|
currentMessages.push(baseMessage);
|
|
3866
3921
|
const saved = await TemboryMemory.prototype.saveMessagesForItem.call(this, itemIndex, [baseMessage]);
|
|
3867
|
-
recordMemoryEvent('chatHistory.addAIChatMessage', {
|
|
3922
|
+
recordMemoryEvent('chatHistory.addAIChatMessage', {
|
|
3923
|
+
saved: Boolean(saved && saved.saved),
|
|
3924
|
+
messages: 1,
|
|
3925
|
+
...summarizeSaveContextForSideChannel((saved && saved.input) || {}, (saved && saved.output) || {}, currentMessages),
|
|
3926
|
+
});
|
|
3868
3927
|
},
|
|
3869
3928
|
addMessages: async (messages) => {
|
|
3870
3929
|
currentMessages.push(...messages);
|
|
3871
3930
|
const saved = await TemboryMemory.prototype.saveMessagesForItem.call(this, itemIndex, messages);
|
|
3872
|
-
recordMemoryEvent('chatHistory.addMessages', {
|
|
3931
|
+
recordMemoryEvent('chatHistory.addMessages', {
|
|
3932
|
+
saved: Boolean(saved && saved.saved),
|
|
3933
|
+
messages: Array.isArray(messages) ? messages.length : 0,
|
|
3934
|
+
...summarizeSaveContextForSideChannel((saved && saved.input) || {}, (saved && saved.output) || {}, currentMessages),
|
|
3935
|
+
});
|
|
3873
3936
|
},
|
|
3874
3937
|
clear: async () => {
|
|
3875
3938
|
currentMessages = [];
|
package/package.json
CHANGED