n8n-nodes-tembory 1.1.42 → 1.1.44
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 +14 -1
- package/dist/nodes/Tembory/TemboryMemory.node.js +86 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
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.44`.
|
|
6
|
+
|
|
7
|
+
## 1.1.44
|
|
8
|
+
|
|
9
|
+
- Adiciona `toolReuseGuard` no contexto compacto do agente para reduzir repeticao indevida de tools entre turnos.
|
|
10
|
+
- Registra chamadas bem sucedidas recentes com `input_hash` e classifica tools de leitura versus side effect de forma generica.
|
|
11
|
+
- Instrui o agente a consultar `tool_ledger` antes de nova tool e a nao repetir side effects sem pedido explicito do usuario.
|
|
12
|
+
- Remove duplicacoes do payload compacto para manter o contexto balanceado abaixo do limite de regressao.
|
|
13
|
+
|
|
14
|
+
## 1.1.43
|
|
15
|
+
|
|
16
|
+
- Adiciona `toolCapture` no payload visual de `saveContext`, deixando explicito quando nenhuma execucao de tool chegou para a memoria.
|
|
17
|
+
- Suporta tambem `intermediate_steps` em snake case, alem de `intermediateSteps`.
|
|
18
|
+
- Quando `toolCallsCaptured` for `0`, o payload informa que e necessario o AI Agent entregar `intermediateSteps`, `__temboryToolCalls` ou tool messages para a memoria conseguir salvar a tool.
|
|
6
19
|
|
|
7
20
|
## 1.1.42
|
|
8
21
|
|
|
@@ -1210,9 +1210,14 @@ const extractToolCalls = (outputValues = {}) => {
|
|
|
1210
1210
|
source: 'tool_object',
|
|
1211
1211
|
});
|
|
1212
1212
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1213
|
+
const intermediateSteps = Array.isArray(obj.intermediateSteps)
|
|
1214
|
+
? obj.intermediateSteps
|
|
1215
|
+
: Array.isArray(obj.intermediate_steps)
|
|
1216
|
+
? obj.intermediate_steps
|
|
1217
|
+
: undefined;
|
|
1218
|
+
if (Array.isArray(intermediateSteps)) {
|
|
1219
|
+
for (let index = 0; index < intermediateSteps.length; index++) {
|
|
1220
|
+
const step = intermediateSteps[index];
|
|
1216
1221
|
if (Array.isArray(step) && step.length >= 1) {
|
|
1217
1222
|
const action = step[0] || {};
|
|
1218
1223
|
push(action.tool || action.name, action.toolInput || action.input || action.args, step[1], true, {
|
|
@@ -2167,17 +2172,56 @@ const shouldCarryPreviousGoal = (intent = '', previousGoal = '') => {
|
|
|
2167
2172
|
return false;
|
|
2168
2173
|
return ['general_message', 'profile_update', 'unknown'].includes(intent);
|
|
2169
2174
|
};
|
|
2170
|
-
const toolNameSuggestsSideEffect = (name = '') =>
|
|
2171
|
-
const
|
|
2175
|
+
const toolNameSuggestsSideEffect = (name = '') => {
|
|
2176
|
+
const text = String(name || '');
|
|
2177
|
+
if (/(?:^|[_\-. ])(check|consult\w*|search|lookup|find|list|get|read|info|inform\w*|disponibilidade|availability)(?:$|[_\-. ])/i.test(text))
|
|
2178
|
+
return false;
|
|
2179
|
+
return /(?:^|[_\-. ])(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(text);
|
|
2180
|
+
};
|
|
2181
|
+
const compactToolPolicyPayload = (value, max = 420) => {
|
|
2182
|
+
const parsed = safeParseToolPayload(value);
|
|
2183
|
+
const cleaned = stripNoisyToolFields(parsed);
|
|
2184
|
+
return truncate(typeof cleaned === 'string' ? cleaned : safeStringify(cleaned), max) || undefined;
|
|
2185
|
+
};
|
|
2186
|
+
const compactSuccessfulToolCallsForPolicy = (toolHistory = [], toolState = {}, maxItems = 10) => {
|
|
2187
|
+
const fromHistory = (Array.isArray(toolHistory) ? toolHistory : [])
|
|
2188
|
+
.filter((tool) => tool && tool.ok !== false && (tool.name || tool.tool_name || tool.tool))
|
|
2189
|
+
.map((tool) => {
|
|
2190
|
+
const name = String(tool.name || tool.tool_name || tool.tool || 'unknown_tool');
|
|
2191
|
+
return cleanContextValue({
|
|
2192
|
+
name,
|
|
2193
|
+
at: tool.at || tool.timestamp,
|
|
2194
|
+
input_hash: stableHash({ name, input: safeParseToolPayload(tool.input) }),
|
|
2195
|
+
side_effect: toolNameSuggestsSideEffect(name),
|
|
2196
|
+
});
|
|
2197
|
+
});
|
|
2198
|
+
const fromToolState = Object.values((toolState || {}).latest_by_name || {}).map((tool) => {
|
|
2199
|
+
const name = String(tool.name || tool.tool || 'unknown_tool');
|
|
2200
|
+
const input = compactToolPolicyPayload(tool.input, 220);
|
|
2201
|
+
return cleanContextValue({
|
|
2202
|
+
name,
|
|
2203
|
+
at: tool.at || tool.timestamp,
|
|
2204
|
+
input_hash: stableHash({ name, input }),
|
|
2205
|
+
side_effect: toolNameSuggestsSideEffect(name),
|
|
2206
|
+
});
|
|
2207
|
+
});
|
|
2208
|
+
return pruneByLimit(fromHistory.length ? fromHistory : fromToolState, maxItems);
|
|
2209
|
+
};
|
|
2210
|
+
const deriveToolRepeatPolicy = ({ intent = '', query = '', executedTools = [], toolState = {}, toolHistory = [] }) => {
|
|
2172
2211
|
const tools = Array.from(new Set(executedTools.filter(Boolean)));
|
|
2173
2212
|
const strictMemoryOnlyTurn = intent === 'conversation_recall' || intent === 'operational_status_question' || hasNoToolRequested(query);
|
|
2174
2213
|
const sideEffectToolCandidates = tools.filter(toolNameSuggestsSideEffect);
|
|
2214
|
+
const successfulToolCalls = compactSuccessfulToolCallsForPolicy(toolHistory, toolState, 10);
|
|
2215
|
+
const referenceToolCandidates = tools.filter((tool) => !toolNameSuggestsSideEffect(tool));
|
|
2175
2216
|
return cleanContextValue({
|
|
2176
2217
|
mode: strictMemoryOnlyTurn ? 'answer_from_memory_when_possible' : 'conditional_reuse',
|
|
2177
2218
|
avoid_repeating_tools_unless_needed: tools,
|
|
2178
2219
|
legacy_do_not_repeat_applies: strictMemoryOnlyTurn,
|
|
2179
2220
|
side_effect_tool_candidates: sideEffectToolCandidates,
|
|
2180
|
-
|
|
2221
|
+
do_not_repeat_side_effect_tools_without_explicit_new_request: sideEffectToolCandidates,
|
|
2222
|
+
answer_from_memory_candidates: referenceToolCandidates,
|
|
2223
|
+
successful_tool_calls: successfulToolCalls,
|
|
2224
|
+
instruction: 'Check tool_ledger before any tool. If same tool/input already succeeded and user did not request new or fresh data, answer from prior result. Never repeat side-effect tools without an explicit new user request.',
|
|
2181
2225
|
});
|
|
2182
2226
|
};
|
|
2183
2227
|
const deriveWorkingMemory = ({ query = '', profileFacts = {}, recentMessages = [], toolHistory = [], operationalState = {}, previous = {} }) => {
|
|
@@ -2273,7 +2317,7 @@ const deriveDecisionState = ({ query = '', toolHistory = [], operationalState =
|
|
|
2273
2317
|
});
|
|
2274
2318
|
};
|
|
2275
2319
|
const executedTools = Array.from(new Set(Object.keys(toolState.counts_by_name || {})));
|
|
2276
|
-
const toolRepeatPolicy = deriveToolRepeatPolicy({ intent, query, executedTools, toolState });
|
|
2320
|
+
const toolRepeatPolicy = deriveToolRepeatPolicy({ intent, query, executedTools, toolState, toolHistory });
|
|
2277
2321
|
const doNotRepeatTools = toolRepeatPolicy.legacy_do_not_repeat_applies ? executedTools : [];
|
|
2278
2322
|
if (toolState.last_successful_tool)
|
|
2279
2323
|
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 });
|
|
@@ -2809,8 +2853,9 @@ const compactToolDecisionStateForAgent = (toolDecisionState = {}) => {
|
|
|
2809
2853
|
};
|
|
2810
2854
|
const compactRepeatToolPolicyForAgent = (policy = {}) => cleanContextValue({
|
|
2811
2855
|
mode: policy.mode,
|
|
2812
|
-
|
|
2813
|
-
|
|
2856
|
+
do_not_repeat_side_effect_tools: (policy.do_not_repeat_side_effect_tools_without_explicit_new_request || []).slice(0, 12),
|
|
2857
|
+
answer_from_memory_candidates: (policy.answer_from_memory_candidates || []).slice(0, 12),
|
|
2858
|
+
successful_tool_calls: (policy.successful_tool_calls || []).slice(-8),
|
|
2814
2859
|
instruction: policy.instruction,
|
|
2815
2860
|
});
|
|
2816
2861
|
const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
@@ -2825,9 +2870,6 @@ const compactDecisionStateForAgent = (state = {}) => cleanContextValue({
|
|
|
2825
2870
|
at: decision.at || decision.updated_at,
|
|
2826
2871
|
})),
|
|
2827
2872
|
do_not_repeat_tools: state.do_not_repeat_tools,
|
|
2828
|
-
repeat_tool_policy: state.repeat_tool_policy ? compactRepeatToolPolicyForAgent(state.repeat_tool_policy) : undefined,
|
|
2829
|
-
avoid_repeating_tools_unless_needed: state.avoid_repeating_tools_unless_needed,
|
|
2830
|
-
tool_decision_state: state.tool_decision_state ? compactToolDecisionStateForAgent(state.tool_decision_state) : undefined,
|
|
2831
2873
|
latest_tool: state.latest_tool ? cleanContextValue({
|
|
2832
2874
|
name: state.latest_tool.name,
|
|
2833
2875
|
status: state.latest_tool.status || (state.latest_tool.ok === false ? 'failed' : 'ok'),
|
|
@@ -3187,6 +3229,36 @@ const conversationTimelineFromMessagesForSideChannel = (messages = [], maxItems
|
|
|
3187
3229
|
preview: truncate(message.content, 180),
|
|
3188
3230
|
}));
|
|
3189
3231
|
};
|
|
3232
|
+
const hasToolCaptureSignal = (output = {}) => {
|
|
3233
|
+
let hasIntermediateSteps = false;
|
|
3234
|
+
let hasExplicitToolCalls = false;
|
|
3235
|
+
let hasToolObject = false;
|
|
3236
|
+
readDeep(output || {}, (obj) => {
|
|
3237
|
+
if (Array.isArray(obj.intermediateSteps) || Array.isArray(obj.intermediate_steps))
|
|
3238
|
+
hasIntermediateSteps = true;
|
|
3239
|
+
if (Array.isArray(obj.__temboryToolCalls) || Array.isArray(obj.toolCalls) || Array.isArray(obj.tool_calls))
|
|
3240
|
+
hasExplicitToolCalls = true;
|
|
3241
|
+
const name = obj.tool || obj.toolName || obj.name;
|
|
3242
|
+
const hasInput = obj.toolInput !== undefined || obj.input !== undefined || obj.args !== undefined;
|
|
3243
|
+
const hasResult = obj.observation !== undefined || obj.result !== undefined || obj.output !== undefined || obj.error !== undefined;
|
|
3244
|
+
if (name && hasInput && hasResult)
|
|
3245
|
+
hasToolObject = true;
|
|
3246
|
+
});
|
|
3247
|
+
return { hasIntermediateSteps, hasExplicitToolCalls, hasToolObject };
|
|
3248
|
+
};
|
|
3249
|
+
const summarizeToolCaptureForSideChannel = (output = {}, toolCalls = []) => {
|
|
3250
|
+
const signals = hasToolCaptureSignal(output);
|
|
3251
|
+
const sources = Array.from(new Set((toolCalls || []).map((tool) => tool.source).filter(Boolean)));
|
|
3252
|
+
return cleanContextValue({
|
|
3253
|
+
status: toolCalls.length ? 'captured' : 'no_tool_payload_received',
|
|
3254
|
+
captured: toolCalls.length,
|
|
3255
|
+
sources,
|
|
3256
|
+
signals,
|
|
3257
|
+
note: toolCalls.length
|
|
3258
|
+
? undefined
|
|
3259
|
+
: 'No tool execution data was received by memory in this saveContext call. If a tool ran, enable AI Agent Return Intermediate Steps or pass tool messages to memory.',
|
|
3260
|
+
});
|
|
3261
|
+
};
|
|
3190
3262
|
const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory = []) => {
|
|
3191
3263
|
const inputMessage = input?.input || input?.chatInput || input?.query || input?.question || input?.text || input?.message || '';
|
|
3192
3264
|
const outputMessage = output?.output || output?.response || output?.text || output?.message || output?.answer || '';
|
|
@@ -3204,6 +3276,7 @@ const summarizeSaveContextForSideChannel = (input = {}, output = {}, chatHistory
|
|
|
3204
3276
|
assistantOutput: outputMessage ? truncate(String(outputMessage), 700) : undefined,
|
|
3205
3277
|
toolCallsCaptured: toolCalls.length,
|
|
3206
3278
|
toolNames: toolEvents.map((tool) => tool.name).filter(Boolean),
|
|
3279
|
+
toolCapture: summarizeToolCaptureForSideChannel(output, toolCalls),
|
|
3207
3280
|
toolLog: buildToolLogForSideChannel(toolEvents, toolCalls.length, SIDE_CHANNEL_SAVE_TOOL_EVENT_MAX),
|
|
3208
3281
|
toolEvents,
|
|
3209
3282
|
}));
|
|
@@ -3664,7 +3737,6 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
3664
3737
|
value: cleanContextValue({
|
|
3665
3738
|
current_intent: (decisionState || {}).current_intent || workingMemory.last_user_intent,
|
|
3666
3739
|
latest_tool: ((decisionState || {}).latest_tool || (operationalState || {}).last_tool || undefined),
|
|
3667
|
-
repeat_tool_policy: (decisionState || {}).repeat_tool_policy,
|
|
3668
3740
|
avoid_repeating_tools_unless_needed: ((decisionState || {}).avoid_repeating_tools_unless_needed || []).slice(0, 12),
|
|
3669
3741
|
do_not_repeat_tools_legacy: ((decisionState || {}).do_not_repeat_tools || []).slice(0, 12),
|
|
3670
3742
|
instruction: actionDirective || workingMemory.next_expected_action || 'Continue according to the agent prompt.',
|
|
@@ -3721,6 +3793,7 @@ const buildContextMessages = ({ payloadFormat, query, userId, profileFacts, work
|
|
|
3721
3793
|
state: minimalState,
|
|
3722
3794
|
workingMemory: sectionValue('working_memory'),
|
|
3723
3795
|
decisionState: sectionValue('decision_state'),
|
|
3796
|
+
toolReuseGuard: compactRepeatToolPolicyForAgent((decisionState || {}).repeat_tool_policy || {}),
|
|
3724
3797
|
operationalState: sectionValue('operational_state'),
|
|
3725
3798
|
actionLedger: sectionValue('action_ledger'),
|
|
3726
3799
|
memoryCompression: sectionValue('memory_compression'),
|
package/package.json
CHANGED