UNPKG

n8n-nodes-zep-v3

Version:

Community node: Zep Memory (v3) - AI Agent Memory for n8n

203 lines 8.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ZepMemory = void 0; // Memory adapter compatible with Langchain's BaseChatMemory interface // n8n AI Agent nodes expect: loadMemoryVariables, saveContext, clear class ZepV3MemoryAdapter { constructor(http, baseUrl, apiKey, threadId) { this.http = http; this.baseUrl = baseUrl; this.apiKey = apiKey; this.threadId = threadId; this.memoryKey = 'chat_history'; this.returnMessages = true; this.inputKey = 'input'; this.outputKey = 'output'; } // Langchain interface: load memory variables async loadMemoryVariables(_values) { try { console.log('[Zep Memory] loadMemoryVariables - API Key length:', this.apiKey?.length || 'UNDEFINED'); console.log('[Zep Memory] loadMemoryVariables - About to make request to:', `${this.baseUrl}/api/v2/threads/${this.threadId}/messages`); // v3: GET /api/v2/threads/:threadId/messages const res = await this.http.helpers.httpRequest({ baseURL: this.baseUrl, url: `/api/v2/threads/${this.threadId}/messages`, method: 'GET', headers: { 'Authorization': `Api-Key ${this.apiKey}`, 'Content-Type': 'application/json', }, }); console.log('[Zep Memory] loadMemoryVariables - Request successful'); // Validate response structure if (!res || !Array.isArray(res.messages)) { console.warn('[Zep Memory] Invalid response format from getMessages, returning empty array'); return { [this.memoryKey]: [] }; } // Convert to Langchain message format const messages = res.messages .map((m) => { if (!m.role || !m.content) { console.warn('[Zep Memory] Message missing required fields (role/content), skipping'); return null; } // Return Langchain-compatible message object return { type: m.role === 'user' ? 'human' : 'ai', content: m.content, ...(m.name ? { name: m.name } : {}), }; }) .filter((m) => m !== null); return { [this.memoryKey]: messages }; } catch (error) { console.error('[Zep Memory] Error loading memory:', error); return { [this.memoryKey]: [] }; } } // Langchain interface: save conversation context async saveContext(inputValues, outputValues) { try { const input = inputValues[this.inputKey]; const output = outputValues[this.outputKey]; if (!input && !output) { console.warn('[Zep Memory] No input or output to save'); return; } const messages = []; if (input) { messages.push({ role: 'user', content: String(input) }); } if (output) { messages.push({ role: 'assistant', content: String(output) }); } // v3: POST /api/v2/threads/:threadId/messages const body = { messages }; await this.http.helpers.httpRequest({ baseURL: this.baseUrl, url: `/api/v2/threads/${this.threadId}/messages`, method: 'POST', headers: { 'Authorization': `Api-Key ${this.apiKey}`, 'Content-Type': 'application/json', }, body, }); console.log(`[Zep Memory] Saved ${messages.length} message(s) to thread ${this.threadId}`); } catch (error) { console.error('[Zep Memory] Error saving context:', error); throw error; } } // Langchain interface: clear memory async clear() { try { // v3: DELETE /api/v2/threads/:threadId await this.http.helpers.httpRequest({ baseURL: this.baseUrl, url: `/api/v2/threads/${this.threadId}`, method: 'DELETE', headers: { 'Authorization': `Api-Key ${this.apiKey}`, 'Content-Type': 'application/json', }, }); console.log(`[Zep Memory] Thread ${this.threadId} cleared successfully`); } catch (error) { console.error('[Zep Memory] Error clearing memory:', error); throw error; } } } class ZepMemory { constructor() { this.description = { displayName: 'Zep Memory (v3)', name: 'zepMemoryV3', icon: 'file:zep-logo.png', group: ['transform'], version: 1, subtitle: 'Threads & User Context', description: 'Use Zep v3 Threads as chat memory', defaults: { name: 'Zep Memory (v3)' }, // Sub-node: no data inputs, special AI output inputs: [], outputs: ['ai_memory'], credentials: [{ name: 'zepApiV3', required: true }], properties: [ { displayName: 'Thread ID', name: 'threadIdType', type: 'options', options: [ { name: 'Connected Chat Trigger Node', value: 'fromInput', description: 'Looks for an input field called "sessionId" from a connected Chat Trigger', }, { name: 'Define below', value: 'customKey', description: 'Use an expression to reference data in previous nodes or enter static text', }, ], default: 'fromInput', }, { displayName: 'Thread Key From Previous Node', name: 'threadKey', type: 'string', default: '={{ $json.sessionId }}', displayOptions: { show: { threadIdType: ['fromInput'], }, }, }, { displayName: 'Thread ID', name: 'threadKey', type: 'string', default: '', description: 'The thread ID to use to store the memory', displayOptions: { show: { threadIdType: ['customKey'], }, }, }, ], }; } async supplyData(itemIndex) { const credentials = await this.getCredentials('zepApiV3'); const baseUrl = credentials.baseUrl; const apiKey = credentials.apiKey; // Get threadId based on user selection const threadIdType = this.getNodeParameter('threadIdType', itemIndex); let threadId; if (threadIdType === 'fromInput') { const threadKey = this.getNodeParameter('threadKey', itemIndex, 'sessionId'); const inputData = this.getInputData(itemIndex); threadId = String(inputData?.[0]?.json?.[threadKey] || ''); } else { threadId = this.getNodeParameter('threadKey', itemIndex); } console.log(`[Zep Memory] Initializing memory adapter for thread: ${threadId}`); console.log(`[Zep Memory] Base URL: ${baseUrl}`); console.log(`[Zep Memory] API Key present: ${apiKey ? 'YES (length: ' + apiKey.length + ')' : 'NO - MISSING!'}`); // Expose an adapter instance on the special AI memory port const adapter = new ZepV3MemoryAdapter(this, baseUrl, apiKey, threadId); console.log(`[Zep Memory] Memory adapter ready`); return { response: adapter, }; } } exports.ZepMemory = ZepMemory; //# sourceMappingURL=ZepMemory.node.js.map