From d9e6fcda56917cc869c12d49566d6bbcc7c70f67 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Oct 2023 14:43:18 +0100 Subject: [PATCH 1/3] add redis backed chat message fix --- .../credentials/RedisApi.credential.ts | 43 +++++++++++++ .../RedisBackedChatMemory.ts | 63 ++++++++++++++----- 2 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 packages/components/credentials/RedisApi.credential.ts diff --git a/packages/components/credentials/RedisApi.credential.ts b/packages/components/credentials/RedisApi.credential.ts new file mode 100644 index 00000000..f65f3f44 --- /dev/null +++ b/packages/components/credentials/RedisApi.credential.ts @@ -0,0 +1,43 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class RedisApi implements INodeCredential { + label: string + name: string + version: number + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Redis API' + this.name = 'redisApi' + this.version = 1.0 + this.inputs = [ + { + label: 'Redis Host', + name: 'redisHost', + type: 'string', + default: '127.0.0.1' + }, + { + label: 'Port', + name: 'redisPort', + type: 'number', + default: '6789' + }, + { + label: 'User', + name: 'redisUser', + type: 'string', + placeholder: '' + }, + { + label: 'Password', + name: 'redisPwd', + type: 'password', + placeholder: '' + } + ] + } +} + +module.exports = { credClass: RedisApi } diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index f10f25ce..5d72336e 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -1,9 +1,9 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { getBaseClasses } from '../../../src/utils' -import { ICommonObject } from '../../../src' +import { INode, INodeData, INodeParams, ICommonObject } from '../../../src/Interface' +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' -import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/redis' -import { createClient } from 'redis' +import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/ioredis' +import { mapStoredMessageToChatMessage, BaseMessage } from 'langchain/schema' +import { Redis } from 'ioredis' class RedisBackedChatMemory_Memory implements INode { label: string @@ -15,6 +15,7 @@ class RedisBackedChatMemory_Memory implements INode { category: string baseClasses: string[] inputs: INodeParams[] + credential: INodeParams constructor() { this.label = 'Redis-Backed Chat Memory' @@ -25,13 +26,14 @@ class RedisBackedChatMemory_Memory implements INode { this.category = 'Memory' this.description = 'Summarizes the conversation and stores the memory in Redis server' this.baseClasses = [this.type, ...getBaseClasses(BufferMemory)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + optional: true, + credentialNames: ['redisApi'] + } this.inputs = [ - { - label: 'Base URL', - name: 'baseURL', - type: 'string', - default: 'redis://localhost:6379' - }, { label: 'Session Id', name: 'sessionId', @@ -60,11 +62,11 @@ class RedisBackedChatMemory_Memory implements INode { } async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { - return initalizeRedis(nodeData, options) + return await initalizeRedis(nodeData, options) } async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const redis = initalizeRedis(nodeData, options) + const redis = await initalizeRedis(nodeData, options) const sessionId = nodeData.inputs?.sessionId as string const chatId = options?.chatId as string options.logger.info(`Clearing Redis memory session ${sessionId ? sessionId : chatId}`) @@ -73,17 +75,28 @@ class RedisBackedChatMemory_Memory implements INode { } } -const initalizeRedis = (nodeData: INodeData, options: ICommonObject): BufferMemory => { - const baseURL = nodeData.inputs?.baseURL as string +const initalizeRedis = async (nodeData: INodeData, options: ICommonObject): Promise => { const sessionId = nodeData.inputs?.sessionId as string const sessionTTL = nodeData.inputs?.sessionTTL as number const memoryKey = nodeData.inputs?.memoryKey as string const chatId = options?.chatId as string + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const username = getCredentialParam('redisUser', credentialData, nodeData) + const password = getCredentialParam('redisPwd', credentialData, nodeData) + const portStr = getCredentialParam('redisPort', credentialData, nodeData) + const host = getCredentialParam('redisHost', credentialData, nodeData) + let isSessionIdUsingChatMessageId = false if (!sessionId && chatId) isSessionIdUsingChatMessageId = true - const redisClient = createClient({ url: baseURL }) + const redisClient = new Redis({ + port: portStr ? parseInt(portStr) : 6379, + host, + username, + password + }) + let obj: RedisChatMessageHistoryInput = { sessionId: sessionId ? sessionId : chatId, client: redisClient @@ -98,6 +111,24 @@ const initalizeRedis = (nodeData: INodeData, options: ICommonObject): BufferMemo const redisChatMessageHistory = new RedisChatMessageHistory(obj) + redisChatMessageHistory.getMessages = async (): Promise => { + const rawStoredMessages = await redisClient.lrange(sessionId ? sessionId : chatId, 0, -1) + const orderedMessages = rawStoredMessages.reverse().map((message) => JSON.parse(message)) + return orderedMessages.map(mapStoredMessageToChatMessage) + } + + redisChatMessageHistory.addMessage = async (message: BaseMessage): Promise => { + const messageToAdd = [message].map((msg) => msg.toDict()) + await redisClient.lpush(sessionId ? sessionId : chatId, JSON.stringify(messageToAdd[0])) + if (sessionTTL) { + await redisClient.expire(sessionId ? sessionId : chatId, sessionTTL) + } + } + + redisChatMessageHistory.clear = async (): Promise => { + await redisClient.del(sessionId ? sessionId : chatId) + } + const memory = new BufferMemoryExtended({ memoryKey, chatHistory: redisChatMessageHistory, From eb02163e0e338081ffc613088d08e942fe3858c9 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 23 Oct 2023 14:45:05 +0100 Subject: [PATCH 2/3] update version --- .../nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index 5d72336e..f192fd12 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -20,7 +20,7 @@ class RedisBackedChatMemory_Memory implements INode { constructor() { this.label = 'Redis-Backed Chat Memory' this.name = 'RedisBackedChatMemory' - this.version = 1.0 + this.version = 2.0 this.type = 'RedisBackedChatMemory' this.icon = 'redis.svg' this.category = 'Memory' From 2653cb62da9d51b877d63ed126d97f489f145d99 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 25 Oct 2023 12:39:35 +0100 Subject: [PATCH 3/3] update credentials --- .../credentials/RedisApi.credential.ts | 43 ------------------- .../RedisBackedChatMemory.ts | 43 +++++++++++-------- 2 files changed, 25 insertions(+), 61 deletions(-) delete mode 100644 packages/components/credentials/RedisApi.credential.ts diff --git a/packages/components/credentials/RedisApi.credential.ts b/packages/components/credentials/RedisApi.credential.ts deleted file mode 100644 index f65f3f44..00000000 --- a/packages/components/credentials/RedisApi.credential.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { INodeParams, INodeCredential } from '../src/Interface' - -class RedisApi implements INodeCredential { - label: string - name: string - version: number - description: string - inputs: INodeParams[] - - constructor() { - this.label = 'Redis API' - this.name = 'redisApi' - this.version = 1.0 - this.inputs = [ - { - label: 'Redis Host', - name: 'redisHost', - type: 'string', - default: '127.0.0.1' - }, - { - label: 'Port', - name: 'redisPort', - type: 'number', - default: '6789' - }, - { - label: 'User', - name: 'redisUser', - type: 'string', - placeholder: '' - }, - { - label: 'Password', - name: 'redisPwd', - type: 'password', - placeholder: '' - } - ] - } -} - -module.exports = { credClass: RedisApi } diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index f192fd12..3e3697d1 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -31,7 +31,7 @@ class RedisBackedChatMemory_Memory implements INode { name: 'credential', type: 'credential', optional: true, - credentialNames: ['redisApi'] + credentialNames: ['redisCacheApi', 'redisCacheUrlApi'] } this.inputs = [ { @@ -81,25 +81,32 @@ const initalizeRedis = async (nodeData: INodeData, options: ICommonObject): Prom const memoryKey = nodeData.inputs?.memoryKey as string const chatId = options?.chatId as string - const credentialData = await getCredentialData(nodeData.credential ?? '', options) - const username = getCredentialParam('redisUser', credentialData, nodeData) - const password = getCredentialParam('redisPwd', credentialData, nodeData) - const portStr = getCredentialParam('redisPort', credentialData, nodeData) - const host = getCredentialParam('redisHost', credentialData, nodeData) - let isSessionIdUsingChatMessageId = false if (!sessionId && chatId) isSessionIdUsingChatMessageId = true - const redisClient = new Redis({ - port: portStr ? parseInt(portStr) : 6379, - host, - username, - password - }) + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const redisUrl = getCredentialParam('redisUrl', credentialData, nodeData) + + let client: Redis + if (!redisUrl || redisUrl === '') { + const username = getCredentialParam('redisCacheUser', credentialData, nodeData) + const password = getCredentialParam('redisCachePwd', credentialData, nodeData) + const portStr = getCredentialParam('redisCachePort', credentialData, nodeData) + const host = getCredentialParam('redisCacheHost', credentialData, nodeData) + + client = new Redis({ + port: portStr ? parseInt(portStr) : 6379, + host, + username, + password + }) + } else { + client = new Redis(redisUrl) + } let obj: RedisChatMessageHistoryInput = { sessionId: sessionId ? sessionId : chatId, - client: redisClient + client } if (sessionTTL) { @@ -112,21 +119,21 @@ const initalizeRedis = async (nodeData: INodeData, options: ICommonObject): Prom const redisChatMessageHistory = new RedisChatMessageHistory(obj) redisChatMessageHistory.getMessages = async (): Promise => { - const rawStoredMessages = await redisClient.lrange(sessionId ? sessionId : chatId, 0, -1) + const rawStoredMessages = await client.lrange(sessionId ? sessionId : chatId, 0, -1) const orderedMessages = rawStoredMessages.reverse().map((message) => JSON.parse(message)) return orderedMessages.map(mapStoredMessageToChatMessage) } redisChatMessageHistory.addMessage = async (message: BaseMessage): Promise => { const messageToAdd = [message].map((msg) => msg.toDict()) - await redisClient.lpush(sessionId ? sessionId : chatId, JSON.stringify(messageToAdd[0])) + await client.lpush(sessionId ? sessionId : chatId, JSON.stringify(messageToAdd[0])) if (sessionTTL) { - await redisClient.expire(sessionId ? sessionId : chatId, sessionTTL) + await client.expire(sessionId ? sessionId : chatId, sessionTTL) } } redisChatMessageHistory.clear = async (): Promise => { - await redisClient.del(sessionId ? sessionId : chatId) + await client.del(sessionId ? sessionId : chatId) } const memory = new BufferMemoryExtended({