UNPKG

n8n-nodes-roundrobin

Version:

n8n node to store and retrieve messages in a round-robin fashion, particularly for LLM conversation loops with multiple personas

288 lines 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RecallMemory = void 0; const n8n_workflow_1 = require("n8n-workflow"); const ExternalStorage_1 = require("./ExternalStorage"); class RecallMemory { constructor() { this.description = { displayName: 'RoundRobin: Memory Recall', name: 'roundRobinRecall', icon: 'file:recallmemory.svg', group: ['transform'], version: 1, subtitle: 'Recall conversation memory', description: 'Recall and inspect conversation memory from RoundRobin nodes', defaults: { name: 'Memory Recall', color: '#ff9900', }, inputs: ['main'], outputs: ['main'], properties: [ { displayName: 'Recall Options', name: 'recallOptions', type: 'notice', default: 'Recall the raw memory state of the conversation to inspect or debug its contents.', }, { displayName: 'Binary Output Property', name: 'binaryOutputProperty', type: 'string', default: 'data', description: 'Name of the binary property to which to write the data', placeholder: 'data', }, { displayName: 'Storage ID', name: 'storageId', type: 'string', displayOptions: { show: { useCustomStorageId: [true], }, }, default: '', description: 'Optional custom ID to use for the conversation storage', placeholder: 'my-conversation-id', hint: 'Use this to override the default behavior of using the workflow ID' }, { displayName: 'Use Custom Storage ID', name: 'useCustomStorageId', type: 'boolean', default: false, description: 'Whether to use a custom storage ID instead of the workflow ID' }, { displayName: 'Include Messages', name: 'includeMessages', type: 'boolean', default: true, description: 'Whether to include message history in the recall output', }, { displayName: 'Include Roles', name: 'includeRoles', type: 'boolean', default: true, description: 'Whether to include role definitions in the recall output', }, { displayName: 'Include Metadata', name: 'includeMetadata', type: 'boolean', default: true, description: 'Whether to include conversation metadata in the recall output', }, { displayName: 'Memory Selection', name: 'memorySelectionOptions', type: 'notice', default: 'Select which conversation memory to retrieve.', }, { displayName: 'Conversation Memory', name: 'memoryId', type: 'options', default: '', required: true, description: 'Choose which conversation memory to recall', options: [ { name: 'Current Workflow Conversation', value: 'current', description: 'Default conversation for current workflow', }, { name: 'Meeting Notes', value: 'meeting-notes', description: 'Meeting notes conversation', }, { name: 'Support Chat', value: 'support-chat', description: 'Support chat conversation', }, { name: 'Interview', value: 'interview', description: 'Interview conversation', }, { name: 'Customer Service', value: 'customer-service', description: 'Customer service conversation', }, { name: 'Lobby Discussion', value: 'lobby-discussion', description: 'Lobby discussion conversation', }, { name: 'Strategy Planning', value: 'strategy-planning', description: 'Strategy planning conversation', }, { name: 'Product Review', value: 'product-review', description: 'Product review conversation', }, { name: 'Team Chat', value: 'team-chat', description: 'Team chat conversation', }, { name: 'Custom ID', value: 'custom', description: 'Use a custom ID', } ] }, { displayName: 'Custom Memory ID', name: 'customMemoryId', type: 'string', default: '', displayOptions: { show: { memoryId: ['custom'], }, }, description: 'Enter a custom memory ID to recall', placeholder: 'my-conversation-id', }, ], }; } async execute() { var _a, _b; const items = this.getInputData(); const returnData = []; try { const nodeName = this.getNode().name; let workflowId = (_a = this.getWorkflow()) === null || _a === void 0 ? void 0 : _a.id; console.log(`[Execution] Raw Workflow ID: ${workflowId}`); if (!workflowId) { console.warn(`[Execution] Warning: Workflow ID is undefined. Falling back to node name ('${nodeName}') for storage key.`); workflowId = nodeName; } else { workflowId = String(workflowId); } const useCustomStorageId = this.getNodeParameter('useCustomStorageId', 0, false); const userStorageId = useCustomStorageId ? this.getNodeParameter('storageId', 0, '') : ''; if (userStorageId) { console.log(`[Execution] Using user-provided Storage ID: "${userStorageId}" instead of workflow ID`); workflowId = userStorageId; } console.log(`[Execution] Using effective ID for storage: ${workflowId}`); const includeMessages = this.getNodeParameter('includeMessages', 0, true); const includeRoles = this.getNodeParameter('includeRoles', 0, true); const includeMetadata = this.getNodeParameter('includeMetadata', 0, true); const binaryOutputProperty = this.getNodeParameter('binaryOutputProperty', 0, 'data'); const memoryIdOption = this.getNodeParameter('memoryId', 0); let memoryId = workflowId; if (memoryIdOption === 'custom') { const customMemoryId = this.getNodeParameter('customMemoryId', 0, ''); if (customMemoryId) { memoryId = customMemoryId; } } else if (memoryIdOption !== 'current') { memoryId = memoryIdOption; } console.log(`[Memory Recall] Recalling memory with ID: ${memoryId}`); const memoryStorageManager = (0, ExternalStorage_1.createStorageManager)(this, 'binary', memoryId); if (!((_b = items[0]) === null || _b === void 0 ? void 0 : _b.binary)) { console.log('[Memory Recall] No binary data found for recall operation'); returnData.push({ json: { status: 'warning', message: 'No memory data found. Nothing to recall.', memoryId, messageCount: 0, roundCount: 0, lastUpdated: new Date().toISOString(), }, }); return [returnData]; } const possibleBinaryProperties = ['data', binaryOutputProperty]; let loadedData = null; let usedProperty = ''; for (const property of possibleBinaryProperties) { if (items[0].binary && items[0].binary[property]) { try { console.log(`[Memory Recall] Trying to load data from binary property: ${property}`); const binaryDataObj = {}; binaryDataObj[property] = items[0].binary[property]; loadedData = await memoryStorageManager.loadFromBinary(binaryDataObj); usedProperty = property; break; } catch (error) { console.log(`[Memory Recall] Failed to load from property ${property}: ${error.message}`); } } } if (!loadedData) { console.log(`[Memory Recall] Could not find valid binary data for recall operation`); returnData.push({ json: { status: 'error', message: 'No valid binary data found. Please connect this node to a previous RoundRobin node.', availableBinaryProperties: Object.keys(items[0].binary || {}), memoryId, }, }); return [returnData]; } console.log(`[Memory Recall] Successfully loaded memory data from property: ${usedProperty}`); if (!loadedData || typeof loadedData !== 'object') { throw new Error('Invalid binary data structure'); } const rawData = loadedData; const memory = {}; if (includeMessages) { memory.messages = rawData.messages || []; } if (includeRoles) { memory.roles = rawData.roles || []; } if (includeMetadata) { memory.spotCount = rawData.spotCount || 0; memory.roundCount = rawData.roundCount || 0; memory.maxRounds = rawData.maxRounds || 0; memory.lastUpdated = rawData.lastUpdated ? new Date(rawData.lastUpdated).toISOString() : new Date().toISOString(); memory.title = rawData.title || ''; memory.context = rawData.context || ''; memory.talkingPoints = rawData.talkingPoints || []; memory.isNewConversation = rawData.isNewConversation || false; } returnData.push({ json: { status: 'success', operation: 'recall', timestamp: new Date().toISOString(), memoryId, conversation_id: rawData.conversationId || memoryId, memory, }, binary: items[0].binary, }); return [returnData]; } catch (error) { if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof Error ? error.message : 'An unknown error occurred'); } } } exports.RecallMemory = RecallMemory; //# sourceMappingURL=RecallMemory.node.js.map