n8n-nodes-tembory 1.0.34 → 1.0.36
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.
|
@@ -577,6 +577,7 @@ const deriveEntityTimeline = (profileFacts = {}, graph = [], recentMessages = []
|
|
|
577
577
|
return pruneByLimit(deduped.sort((a, b) => String(a.at || '').localeCompare(String(b.at || ''))), maxItems || 24);
|
|
578
578
|
};
|
|
579
579
|
const RECENT_MESSAGE_MARKER = '__tembory_recent_message_v1__';
|
|
580
|
+
const CONVERSATION_LEDGER_MARKER = '__tembory_conversation_ledger_v1__';
|
|
580
581
|
const TOOL_HISTORY_MARKER = '__tembory_tool_history_v1__';
|
|
581
582
|
const TOOL_LEDGER_MARKER = '__tembory_tool_ledger_v1__';
|
|
582
583
|
const encodeRecentMessage = (recent, threadId) => `${RECENT_MESSAGE_MARKER}${safeStringify({
|
|
@@ -585,6 +586,20 @@ const encodeRecentMessage = (recent, threadId) => `${RECENT_MESSAGE_MARKER}${saf
|
|
|
585
586
|
at: recent.at || nowIso(),
|
|
586
587
|
thread_id: threadId,
|
|
587
588
|
})}`;
|
|
589
|
+
const encodeConversationLedger = (messages = [], threadId) => {
|
|
590
|
+
const chronological = sortConversationChronological(messages || []);
|
|
591
|
+
const userMessages = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
|
|
592
|
+
const readable = `Conversation transcript ledger. Use the chronological messages array to answer conversation recall, including what the user said, message order, and the user message immediately before the current request. Total messages: ${chronological.length}. User messages: ${userMessages.length}. `;
|
|
593
|
+
return `${readable}${CONVERSATION_LEDGER_MARKER}${safeStringify({
|
|
594
|
+
thread_id: threadId,
|
|
595
|
+
generated_at: nowIso(),
|
|
596
|
+
messages: chronological.map((msg) => ({
|
|
597
|
+
role: msg.role || 'user',
|
|
598
|
+
content: msg.content || '',
|
|
599
|
+
at: msg.at || nowIso(),
|
|
600
|
+
})),
|
|
601
|
+
})}`;
|
|
602
|
+
};
|
|
588
603
|
const encodeToolCall = (tool, threadId) => `${TOOL_HISTORY_MARKER}${safeStringify({
|
|
589
604
|
id: tool.id || tool.callId || '',
|
|
590
605
|
turn_id: tool.turnId || '',
|
|
@@ -684,6 +699,27 @@ const parseToolLedgerMarker = (text) => {
|
|
|
684
699
|
return [];
|
|
685
700
|
}
|
|
686
701
|
};
|
|
702
|
+
const parseConversationLedgerMarker = (text) => {
|
|
703
|
+
if (!text || typeof text !== 'string')
|
|
704
|
+
return [];
|
|
705
|
+
const markerIndex = text.indexOf(CONVERSATION_LEDGER_MARKER);
|
|
706
|
+
if (markerIndex < 0)
|
|
707
|
+
return [];
|
|
708
|
+
try {
|
|
709
|
+
const parsed = JSON.parse(text.slice(markerIndex + CONVERSATION_LEDGER_MARKER.length));
|
|
710
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
711
|
+
return messages
|
|
712
|
+
.filter((msg) => msg && msg.content)
|
|
713
|
+
.map((msg) => ({
|
|
714
|
+
role: msg.role || 'user',
|
|
715
|
+
content: truncate(msg.content, 2000),
|
|
716
|
+
at: msg.at || parsed.generated_at || nowIso(),
|
|
717
|
+
}));
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
return [];
|
|
721
|
+
}
|
|
722
|
+
};
|
|
687
723
|
const recentMessageFromMemory = (item) => {
|
|
688
724
|
const meta = metadataOf(item);
|
|
689
725
|
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
@@ -700,6 +736,15 @@ const recentMessageFromMemory = (item) => {
|
|
|
700
736
|
at: meta.at || item.created_at || item.createdAt || nowIso(),
|
|
701
737
|
};
|
|
702
738
|
};
|
|
739
|
+
const recentMessagesFromMemory = (item) => {
|
|
740
|
+
const meta = metadataOf(item);
|
|
741
|
+
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
742
|
+
const ledger = parseConversationLedgerMarker(content);
|
|
743
|
+
if (ledger.length)
|
|
744
|
+
return ledger;
|
|
745
|
+
const single = recentMessageFromMemory(item);
|
|
746
|
+
return single ? [single] : [];
|
|
747
|
+
};
|
|
703
748
|
const toolHistoryFromMemory = (item) => {
|
|
704
749
|
const meta = metadataOf(item);
|
|
705
750
|
const content = meta.content || item.memory || item.text || item.value || item.content || item.data;
|
|
@@ -1022,7 +1067,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1022
1067
|
topK: 8,
|
|
1023
1068
|
lastN: 12,
|
|
1024
1069
|
toolHistoryLastN: 15,
|
|
1025
|
-
recentMessagesLastN:
|
|
1070
|
+
recentMessagesLastN: 50,
|
|
1026
1071
|
},
|
|
1027
1072
|
productionBalanced: {
|
|
1028
1073
|
summarySource: 'auto',
|
|
@@ -1055,7 +1100,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1055
1100
|
topK: 6,
|
|
1056
1101
|
lastN: 8,
|
|
1057
1102
|
toolHistoryLastN: 10,
|
|
1058
|
-
recentMessagesLastN:
|
|
1103
|
+
recentMessagesLastN: 50,
|
|
1059
1104
|
vectorMemoryMaxChars: 360,
|
|
1060
1105
|
contextMaxChars: 10000,
|
|
1061
1106
|
connectedModelSummaryMaxChars: 1200,
|
|
@@ -1092,7 +1137,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1092
1137
|
topK: 4,
|
|
1093
1138
|
lastN: 4,
|
|
1094
1139
|
toolHistoryLastN: 5,
|
|
1095
|
-
recentMessagesLastN:
|
|
1140
|
+
recentMessagesLastN: 50,
|
|
1096
1141
|
vectorMemoryMaxChars: 260,
|
|
1097
1142
|
contextMaxChars: 7000,
|
|
1098
1143
|
connectedModelSummaryMaxChars: 1000,
|
|
@@ -1130,7 +1175,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1130
1175
|
lastN: 4,
|
|
1131
1176
|
maxReturn: 6,
|
|
1132
1177
|
toolHistoryLastN: 6,
|
|
1133
|
-
recentMessagesLastN:
|
|
1178
|
+
recentMessagesLastN: 50,
|
|
1134
1179
|
vectorMemoryMaxChars: 220,
|
|
1135
1180
|
contextMaxChars: 7000,
|
|
1136
1181
|
connectedModelSummaryMaxChars: 1200,
|
|
@@ -1160,7 +1205,7 @@ const applyOperationalPreset = (advanced = {}) => {
|
|
|
1160
1205
|
topK: 10,
|
|
1161
1206
|
lastN: 20,
|
|
1162
1207
|
toolHistoryLastN: 20,
|
|
1163
|
-
recentMessagesLastN:
|
|
1208
|
+
recentMessagesLastN: 50,
|
|
1164
1209
|
payloadFormat: 'auditJson',
|
|
1165
1210
|
},
|
|
1166
1211
|
};
|
|
@@ -1328,13 +1373,10 @@ const previousUserFallbackFromWorkingMemory = (query = '', workingMemory = {}) =
|
|
|
1328
1373
|
const firstUserMessageFromConversation = (recentMessages = []) => [...(recentMessages || [])]
|
|
1329
1374
|
.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')))
|
|
1330
1375
|
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime())[0] || null;
|
|
1331
|
-
const buildConversationFrame = ({ query = '', recentMessages = []
|
|
1376
|
+
const buildConversationFrame = ({ query = '', recentMessages = [] }) => {
|
|
1332
1377
|
const normalizedQuery = normalizeIntentText(query).trim();
|
|
1333
1378
|
const users = recentUserMessages(recentMessages);
|
|
1334
1379
|
const currentUser = users.find((msg) => normalizeIntentText(msg.content).trim() === normalizedQuery) || (query ? { role: 'user', content: query, at: nowIso() } : null);
|
|
1335
|
-
const previousUser = previousUserMessageForQuery(query, recentMessages);
|
|
1336
|
-
const firstUser = firstUserMessageFromConversation(recentMessages);
|
|
1337
|
-
const previousUserMessage = previousUser ? truncate(previousUser.content, 500) : previousUserFallbackFromWorkingMemory(query, workingMemory);
|
|
1338
1380
|
const chronological = (recentMessages || []).map((msg) => ({
|
|
1339
1381
|
role: msg.role,
|
|
1340
1382
|
content: truncate(msg.content, 500),
|
|
@@ -1343,8 +1385,6 @@ const buildConversationFrame = ({ query = '', recentMessages = [], workingMemory
|
|
|
1343
1385
|
const userMessagesChronological = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
|
|
1344
1386
|
const frame = cleanContextValue({
|
|
1345
1387
|
current_user_message: currentUser ? truncate(currentUser.content, 500) : truncate(query, 500),
|
|
1346
|
-
first_user_message: firstUser ? truncate(firstUser.content, 500) : null,
|
|
1347
|
-
previous_user_message: previousUserMessage,
|
|
1348
1388
|
conversation_history_chronological: chronological,
|
|
1349
1389
|
recent_messages_chronological: chronological,
|
|
1350
1390
|
all_user_messages_chronological: userMessagesChronological,
|
|
@@ -1352,10 +1392,8 @@ const buildConversationFrame = ({ query = '', recentMessages = [], workingMemory
|
|
|
1352
1392
|
.filter((msg) => normalizeIntentText(msg.content).trim() !== normalizedQuery)
|
|
1353
1393
|
.slice(0, 5)
|
|
1354
1394
|
.map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at })),
|
|
1355
|
-
instruction: 'This is the authoritative short-term conversation frame. If the user asks about current/previous/last client messages or what they already said, answer from
|
|
1395
|
+
instruction: 'This is the authoritative short-term conversation frame. If the user asks about current/previous/last client messages or what they already said, answer from conversation_history_chronological/all_user_messages_chronological before using vector memories, summaries, operational state, or tool history. For the last message before the current one, use the user message immediately before current_user_message in all_user_messages_chronological.',
|
|
1356
1396
|
});
|
|
1357
|
-
if (!previousUserMessage)
|
|
1358
|
-
frame.previous_user_message = null;
|
|
1359
1397
|
return frame;
|
|
1360
1398
|
};
|
|
1361
1399
|
const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalState = {}, workingMemory = {} }) => {
|
|
@@ -1363,31 +1401,31 @@ const buildCurrentTurnFocus = ({ query = '', recentMessages = [], operationalSta
|
|
|
1363
1401
|
const operationalStatus = isOperationalStatusQuery(query);
|
|
1364
1402
|
if (!recall && !operationalStatus)
|
|
1365
1403
|
return null;
|
|
1366
|
-
const previousUser = previousUserMessageForQuery(query, recentMessages);
|
|
1367
|
-
const firstUser = firstUserMessageFromConversation(recentMessages);
|
|
1368
1404
|
const users = recentUserMessages(recentMessages)
|
|
1369
1405
|
.filter((msg) => normalizeIntentText(msg.content).trim() !== normalizeIntentText(query).trim())
|
|
1370
|
-
.slice(0,
|
|
1406
|
+
.slice(0, 8)
|
|
1371
1407
|
.map((msg) => ({ role: msg.role, content: truncate(msg.content, 500), at: msg.at }));
|
|
1372
1408
|
const toolState = (operationalState || {}).tool_state || {};
|
|
1373
|
-
const
|
|
1409
|
+
const chronological = (recentMessages || []).map((msg) => ({
|
|
1410
|
+
role: msg.role,
|
|
1411
|
+
content: truncate(msg.content, 500),
|
|
1412
|
+
at: msg.at,
|
|
1413
|
+
}));
|
|
1414
|
+
const userMessagesChronological = chronological.filter((msg) => /^(user|human)$/i.test(String(msg.role || '')));
|
|
1374
1415
|
const focus = cleanContextValue({
|
|
1375
1416
|
current_user_request: truncate(query, 500),
|
|
1376
1417
|
intent: recall ? 'conversation_recall' : 'tool_or_status_question',
|
|
1377
|
-
first_user_message: firstUser ? truncate(firstUser.content, 500) : null,
|
|
1378
|
-
previous_user_message: previousUserMessage,
|
|
1379
1418
|
recent_user_messages: users,
|
|
1419
|
+
all_user_messages_chronological: recall ? userMessagesChronological : undefined,
|
|
1380
1420
|
tool_status: operationalStatus ? {
|
|
1381
1421
|
executed_tools: toolState.names || [],
|
|
1382
1422
|
last_successful_tool: toolState.last_successful_tool || null,
|
|
1383
1423
|
failed_by_name: toolState.failed_by_name || {},
|
|
1384
1424
|
} : undefined,
|
|
1385
1425
|
instruction: recall
|
|
1386
|
-
? 'Answer the user meta-question from
|
|
1426
|
+
? 'Answer the user meta-question from conversation_history_chronological/all_user_messages_chronological. Do not call tools for recall-only questions and do not infer missing turns from vector memories or summaries.'
|
|
1387
1427
|
: 'Answer status questions from tool_state, tool_history and action_ledger. Do not call tools unless the agent prompt requires it.',
|
|
1388
1428
|
});
|
|
1389
|
-
if (!previousUserMessage)
|
|
1390
|
-
focus.previous_user_message = null;
|
|
1391
1429
|
return focus;
|
|
1392
1430
|
};
|
|
1393
1431
|
const buildActionDirective = ({ workingMemory = {}, operationalState = {} }) => {
|
|
@@ -1443,7 +1481,7 @@ const deriveNextExpectedAction = (intent, operationalState = {}) => {
|
|
|
1443
1481
|
if (intent === 'profile_update')
|
|
1444
1482
|
return 'save stable profile facts and continue the conversation';
|
|
1445
1483
|
if (intent === 'conversation_recall')
|
|
1446
|
-
return 'answer directly using
|
|
1484
|
+
return 'answer directly using conversation_history_chronological/all_user_messages_chronological; do not call tools for recall-only questions';
|
|
1447
1485
|
if (intent === 'operational_status_question')
|
|
1448
1486
|
return 'answer status questions from tool_state, tool_history and action_ledger; do not call tools unless the agent prompt requires it';
|
|
1449
1487
|
return 'answer using retrieved context and avoid unnecessary tool calls';
|
|
@@ -2427,7 +2465,7 @@ class Mem0Memory {
|
|
|
2427
2465
|
values: [
|
|
2428
2466
|
{ displayName: 'Incluir Profile Facts', name: 'includeProfileFacts', type: 'boolean', default: true },
|
|
2429
2467
|
{ displayName: 'Incluir Mensagens Recentes', name: 'includeRecentMessages', type: 'boolean', default: true },
|
|
2430
|
-
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default:
|
|
2468
|
+
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default: 50 },
|
|
2431
2469
|
{ displayName: 'Incluir Highlights Recentes', name: 'includeRecentHighlights', type: 'boolean', default: true },
|
|
2432
2470
|
{ displayName: 'Máximo de Highlights', name: 'recentHighlightsMaxItems', type: 'number', default: 6 },
|
|
2433
2471
|
{ displayName: 'Incluir Grafo', name: 'includeRelations', type: 'boolean', default: true },
|
|
@@ -2582,7 +2620,7 @@ class Mem0Memory {
|
|
|
2582
2620
|
{ displayName: 'Incluir Entity Timeline', name: 'includeEntityTimeline', type: 'boolean', default: true, description: 'Injeta uma timeline compacta de entidades, fatos de perfil, relações do grafo e eventos importantes da sessão.' },
|
|
2583
2621
|
{ displayName: 'Incluir Compressão de Memória', name: 'includeMemoryCompression', type: 'boolean', default: true, description: 'Injeta resumos compactos de turno, sessão, entidades e workflow para reduzir ruído.' },
|
|
2584
2622
|
{ displayName: 'Incluir Mensagens Recentes', name: 'includeRecentMessages', type: 'boolean', default: true },
|
|
2585
|
-
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default:
|
|
2623
|
+
{ displayName: 'Últimas N Mensagens', name: 'recentMessagesLastN', type: 'number', default: 50 },
|
|
2586
2624
|
{ displayName: 'Incluir Highlights Recentes', name: 'includeRecentHighlights', type: 'boolean', default: true },
|
|
2587
2625
|
{ displayName: 'Máximo de Highlights', name: 'recentHighlightsMaxItems', type: 'number', default: 6 },
|
|
2588
2626
|
],
|
|
@@ -2839,6 +2877,13 @@ class Mem0Memory {
|
|
|
2839
2877
|
source: 'n8n_connected_embedding',
|
|
2840
2878
|
}, ids));
|
|
2841
2879
|
}
|
|
2880
|
+
clientMemories.push(await createClientVectorMemory(connectedEmbedding, encodeConversationLedger(recentForTurn, threadId), {
|
|
2881
|
+
kind: 'conversation_ledger',
|
|
2882
|
+
thread_id: threadId,
|
|
2883
|
+
project: project || undefined,
|
|
2884
|
+
source: 'n8n_connected_embedding',
|
|
2885
|
+
generated_at: nowIso(),
|
|
2886
|
+
}, ids));
|
|
2842
2887
|
}
|
|
2843
2888
|
if (adv.persistToolFactsToMem0 && toolCalls.length) {
|
|
2844
2889
|
const facts = toolCalls.map((tool) => `Tool ${tool.name} input=${tool.input}${tool.result ? ` result=${tool.result}` : ''}`).join('\n');
|
|
@@ -2894,6 +2939,23 @@ class Mem0Memory {
|
|
|
2894
2939
|
user_id: body.user_id,
|
|
2895
2940
|
});
|
|
2896
2941
|
}
|
|
2942
|
+
await safePersistLegacyMemory(this, {
|
|
2943
|
+
messages: [{ role: 'system', content: encodeConversationLedger(recentForTurn, threadId) }],
|
|
2944
|
+
infer: false,
|
|
2945
|
+
user_id: body.user_id,
|
|
2946
|
+
agent_id: body.agent_id,
|
|
2947
|
+
run_id: body.run_id,
|
|
2948
|
+
metadata: {
|
|
2949
|
+
kind: 'conversation_ledger',
|
|
2950
|
+
thread_id: threadId,
|
|
2951
|
+
project: project || undefined,
|
|
2952
|
+
source: 'tembory_transcript',
|
|
2953
|
+
generated_at: nowIso(),
|
|
2954
|
+
},
|
|
2955
|
+
}, {
|
|
2956
|
+
kind: 'conversation_ledger',
|
|
2957
|
+
user_id: body.user_id,
|
|
2958
|
+
});
|
|
2897
2959
|
}
|
|
2898
2960
|
if (adv.includeToolHistory !== false && toolCalls.length) {
|
|
2899
2961
|
for (const tool of toolCalls) {
|
|
@@ -2962,6 +3024,20 @@ class Mem0Memory {
|
|
|
2962
3024
|
},
|
|
2963
3025
|
});
|
|
2964
3026
|
}
|
|
3027
|
+
await GenericFunctions_1.mem0ApiRequest.call(this, 'POST', '/v1/memories/', {
|
|
3028
|
+
messages: [{ role: 'system', content: encodeConversationLedger(recentForTurn, threadId) }],
|
|
3029
|
+
infer: false,
|
|
3030
|
+
user_id: body.user_id,
|
|
3031
|
+
agent_id: body.agent_id,
|
|
3032
|
+
run_id: body.run_id,
|
|
3033
|
+
metadata: {
|
|
3034
|
+
kind: 'conversation_ledger',
|
|
3035
|
+
thread_id: threadId,
|
|
3036
|
+
project: project || undefined,
|
|
3037
|
+
source: 'tembory_transcript',
|
|
3038
|
+
generated_at: nowIso(),
|
|
3039
|
+
},
|
|
3040
|
+
});
|
|
2965
3041
|
}
|
|
2966
3042
|
if (adv.includeToolHistory !== false && !adv.persistToolFactsToMem0 && toolCalls.length) {
|
|
2967
3043
|
for (const tool of toolCalls) {
|
|
@@ -3267,7 +3343,7 @@ class Mem0Memory {
|
|
|
3267
3343
|
try {
|
|
3268
3344
|
const transcriptProbe = [
|
|
3269
3345
|
'recent conversation transcript',
|
|
3270
|
-
'
|
|
3346
|
+
'chronological user messages earliest message latest message message immediately before current request full conversation',
|
|
3271
3347
|
query,
|
|
3272
3348
|
].filter(Boolean).join('\n');
|
|
3273
3349
|
const transcriptBody = {
|
|
@@ -3317,13 +3393,11 @@ class Mem0Memory {
|
|
|
3317
3393
|
}
|
|
3318
3394
|
if (adv.includeRecentMessages !== false) {
|
|
3319
3395
|
persistedRecentMessages = persistedMemoryItems
|
|
3320
|
-
.
|
|
3321
|
-
.filter(Boolean)
|
|
3396
|
+
.flatMap(recentMessagesFromMemory)
|
|
3322
3397
|
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
3323
3398
|
}
|
|
3324
3399
|
const vectorRecentMessages = adv.includeRecentMessages === false ? [] : vectorMemories
|
|
3325
|
-
.
|
|
3326
|
-
.filter(Boolean)
|
|
3400
|
+
.flatMap(recentMessagesFromMemory)
|
|
3327
3401
|
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
3328
3402
|
if (adv.includeToolHistory !== false) {
|
|
3329
3403
|
persistedToolHistory = persistedMemoryItems
|
|
@@ -3699,7 +3773,9 @@ exports.__private = {
|
|
|
3699
3773
|
toolHistoryFromMemory,
|
|
3700
3774
|
cleanAssistantTranscriptText,
|
|
3701
3775
|
buildActionDirective,
|
|
3776
|
+
buildConversationFrame,
|
|
3702
3777
|
recentMessageFromMemory,
|
|
3778
|
+
recentMessagesFromMemory,
|
|
3703
3779
|
previousUserFallbackFromWorkingMemory,
|
|
3704
3780
|
firstUserMessageFromConversation,
|
|
3705
3781
|
sortConversationChronological,
|
|
@@ -3710,9 +3786,11 @@ exports.__private = {
|
|
|
3710
3786
|
parseToolHistoryMarker,
|
|
3711
3787
|
parseToolLedgerMarker,
|
|
3712
3788
|
parseRecentMessageMarker,
|
|
3789
|
+
parseConversationLedgerMarker,
|
|
3713
3790
|
encodeToolCall,
|
|
3714
3791
|
encodeToolLedger,
|
|
3715
3792
|
encodeRecentMessage,
|
|
3793
|
+
encodeConversationLedger,
|
|
3716
3794
|
userKeyFrom,
|
|
3717
3795
|
applyOperationalPreset,
|
|
3718
3796
|
flattenAdvancedGroups,
|
package/package.json
CHANGED
|
@@ -42,8 +42,40 @@ function requiredToolFromText(text) {
|
|
|
42
42
|
return /"required_tool":"([^"]+)"/.exec(text)?.[1] || null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function
|
|
46
|
-
|
|
45
|
+
function conversationFrameFromText(text) {
|
|
46
|
+
const match = /## Conversation frame\n([\s\S]*?)(?:\n\n## |\n## |$)/.exec(text);
|
|
47
|
+
if (!match) return null;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(match[1].trim());
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function previousUserMessageFromText(text, currentMessage) {
|
|
56
|
+
const frame = conversationFrameFromText(text);
|
|
57
|
+
const userMessages = Array.isArray(frame?.all_user_messages_chronological)
|
|
58
|
+
? frame.all_user_messages_chronological
|
|
59
|
+
: [];
|
|
60
|
+
const normalizedCurrent = normalizeText(currentMessage).trim();
|
|
61
|
+
for (let index = userMessages.length - 1; index >= 0; index -= 1) {
|
|
62
|
+
const message = userMessages[index];
|
|
63
|
+
if (normalizeText(message?.content).trim() === normalizedCurrent) {
|
|
64
|
+
return userMessages[index - 1]?.content || '';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return userMessages.at(-2)?.content || '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function previousUserMessageFromState(messages, currentMessage) {
|
|
71
|
+
const userMessages = (messages || []).filter((message) => /^(user|human)$/i.test(String(message.role || '')));
|
|
72
|
+
const normalizedCurrent = normalizeText(currentMessage).trim();
|
|
73
|
+
for (let index = userMessages.length - 1; index >= 0; index -= 1) {
|
|
74
|
+
if (normalizeText(userMessages[index]?.content).trim() === normalizedCurrent) {
|
|
75
|
+
return userMessages[index - 1]?.content || '';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return userMessages.at(-2)?.content || '';
|
|
47
79
|
}
|
|
48
80
|
|
|
49
81
|
function normalizeText(value) {
|
|
@@ -105,7 +137,8 @@ function answerFor({ userMessage, toolCalls, state, contextText }) {
|
|
|
105
137
|
.join('\n');
|
|
106
138
|
}
|
|
107
139
|
if (/qual.*ultima mensagem antes/.test(normalized)) {
|
|
108
|
-
const previous = previousUserMessageFromText(contextText)
|
|
140
|
+
const previous = previousUserMessageFromText(contextText, userMessage)
|
|
141
|
+
|| previousUserMessageFromState(state.recentMessages, userMessage)
|
|
109
142
|
|| core.previousUserFallbackFromWorkingMemory(userMessage, state.workingMemory);
|
|
110
143
|
return previous ? `Sua última mensagem antes desta foi: "${previous}".` : 'Não encontrei mensagem anterior.';
|
|
111
144
|
}
|