From 786dd8a11c3af6bb6cd122637d44978df600073b Mon Sep 17 00:00:00 2001 From: Ayush Jain Date: Wed, 4 Oct 2023 00:16:12 +0530 Subject: [PATCH 1/7] Added Upstash Redis Memory support --- .../UpstashRedisBackedChatMemory.ts | 131 ++++++++++++++++++ .../UpstashRedisBackedChatMemory/upstash.svg | 12 ++ packages/components/package.json | 3 +- 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts create mode 100644 packages/components/nodes/memory/UpstashRedisBackedChatMemory/upstash.svg diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts new file mode 100644 index 00000000000..e0b5e20e976 --- /dev/null +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -0,0 +1,131 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { ICommonObject } from '../../../src' +import { BufferMemory, BufferMemoryInput } from 'langchain/memory' +import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from "langchain/stores/message/upstash_redis"; +import { Redis, RedisConfigNodejs } from '@upstash/redis'; + +class UpstashRedisBackedChatMemory_Memory implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Upstash Redis-Backed Chat Memory' + this.name = 'UpstashRedisBackedChatMemory' + this.version = 1.0 + this.type = 'UpstashRedisBackedChatMemory' + this.icon = 'upstash.svg' + this.category = 'Memory' + this.description = 'Summarizes the conversation and stores the memory in upstash Redis server' + this.baseClasses = [this.type, ...getBaseClasses(BufferMemory)] + this.inputs = [ + { + label: 'Base URL', + name: 'baseURL', + type: 'string', + default: 'redis://localhost:6379' + }, + { + label: 'Token', + name: 'token', + type: 'string', + default: '********' + }, + { + label: 'Session Id', + name: 'sessionId', + type: 'string', + description: 'If not specified, the first CHAT_MESSAGE_ID will be used as sessionId', + default: '', + additionalParams: true, + optional: true + }, + { + label: 'Session Timeouts', + name: 'sessionTTL', + type: 'number', + description: 'Omit this parameter to make sessions never expire', + additionalParams: true, + optional: true + }, + { + label: 'Memory Key', + name: 'memoryKey', + type: 'string', + default: 'chat_history', + additionalParams: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + return initalizeUpstashRedis(nodeData, options) + } + + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const redis = initalizeUpstashRedis(nodeData, options) + const sessionId = nodeData.inputs?.sessionId as string + const chatId = options?.chatId as string + options.logger.info(`Clearing Upstash Redis memory session ${sessionId ? sessionId : chatId}`) + await redis.clear() + options.logger.info(`Successfully cleared Upstash Redis memory session ${sessionId ? sessionId : chatId}`) + } +} + +const initalizeUpstashRedis = (nodeData: INodeData, options: ICommonObject): BufferMemory => { + const baseURL = nodeData.inputs?.baseURL as string + 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 token = nodeData.inputs?.token as string + + let isSessionIdUsingChatMessageId = false + if (!sessionId && chatId) isSessionIdUsingChatMessageId = true + + const upstashRedisConfig = ({ url: baseURL, token }) as RedisConfigNodejs; + const redisClient = new Redis(upstashRedisConfig) + let obj: UpstashRedisChatMessageHistoryInput = { + sessionId: sessionId ? sessionId : chatId, + client: redisClient + } + + if (sessionTTL) { + obj = { + ...obj, + sessionTTL + } + } + + const redisChatMessageHistory = new UpstashRedisChatMessageHistory(obj) + + const memory = new BufferMemoryExtended({ + memoryKey, + chatHistory: redisChatMessageHistory, + returnMessages: true, + isSessionIdUsingChatMessageId + }) + return memory +} + +interface BufferMemoryExtendedInput { + isSessionIdUsingChatMessageId: boolean +} + +class BufferMemoryExtended extends BufferMemory { + isSessionIdUsingChatMessageId? = false + + constructor(fields: BufferMemoryInput & Partial) { + super(fields) + this.isSessionIdUsingChatMessageId = fields.isSessionIdUsingChatMessageId + } +} + +module.exports = { nodeClass: UpstashRedisBackedChatMemory_Memory } diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/upstash.svg b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/upstash.svg new file mode 100644 index 00000000000..a0fb96a79e7 --- /dev/null +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/upstash.svg @@ -0,0 +1,12 @@ + + + upstash + + + + + + + + + diff --git a/packages/components/package.json b/packages/components/package.json index 93609106295..77ada2ad56a 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -64,7 +64,8 @@ "srt-parser-2": "^1.2.3", "vm2": "^3.9.19", "weaviate-ts-client": "^1.1.0", - "ws": "^8.9.0" + "ws": "^8.9.0", + "@upstash/redis": "^1.22.1" }, "devDependencies": { "@types/gulp": "4.0.9", From 086944dc077752fb30eb8d887450af34ca398098 Mon Sep 17 00:00:00 2001 From: Ayush Jain Date: Thu, 5 Oct 2023 18:29:39 +0530 Subject: [PATCH 2/7] Fixed linting errors --- .../UpstashRedisBackedChatMemory.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index e0b5e20e976..64b14e2cf8f 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -2,8 +2,8 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' import { getBaseClasses } from '../../../src/utils' import { ICommonObject } from '../../../src' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' -import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from "langchain/stores/message/upstash_redis"; -import { Redis, RedisConfigNodejs } from '@upstash/redis'; +import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from "langchain/stores/message/upstash_redis" +import { Redis, RedisConfigNodejs } from '@upstash/redis' class UpstashRedisBackedChatMemory_Memory implements INode { label: string @@ -90,7 +90,7 @@ const initalizeUpstashRedis = (nodeData: INodeData, options: ICommonObject): Buf let isSessionIdUsingChatMessageId = false if (!sessionId && chatId) isSessionIdUsingChatMessageId = true - const upstashRedisConfig = ({ url: baseURL, token }) as RedisConfigNodejs; + const upstashRedisConfig = ({ url: baseURL, token }) as RedisConfigNodejs const redisClient = new Redis(upstashRedisConfig) let obj: UpstashRedisChatMessageHistoryInput = { sessionId: sessionId ? sessionId : chatId, From 9bae1c33f71070fe8406a78ad1b4a343402b3b33 Mon Sep 17 00:00:00 2001 From: Ayush Jain Date: Fri, 6 Oct 2023 23:23:21 +0530 Subject: [PATCH 3/7] Review changes --- .../UpstashRedisMemoryApi.credential.ts | 26 ++++++++++++++++ .../UpstashRedisBackedChatMemory.ts | 30 +++++++++++-------- 2 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 packages/components/credentials/UpstashRedisMemoryApi.credential.ts diff --git a/packages/components/credentials/UpstashRedisMemoryApi.credential.ts b/packages/components/credentials/UpstashRedisMemoryApi.credential.ts new file mode 100644 index 00000000000..9c0f99aabab --- /dev/null +++ b/packages/components/credentials/UpstashRedisMemoryApi.credential.ts @@ -0,0 +1,26 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class UpstashRedisMemoryApi implements INodeCredential { + label: string + name: string + version: number + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Upstash Redis Memory API' + this.name = 'upstashRedisMemoryApi' + this.version = 1.0 + this.description = + 'Refer to official guide on how to create redis instance and get password on upstash' + this.inputs = [ + { + label: 'Upstash Redis Password', + name: 'upstashRedisPassword', + type: 'password' + } + ] + } +} + +module.exports = { credClass: UpstashRedisMemoryApi } diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index 64b14e2cf8f..98983ecbca5 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -1,8 +1,8 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { getBaseClasses } from '../../../src/utils' +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { ICommonObject } from '../../../src' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' -import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from "langchain/stores/message/upstash_redis" +import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from 'langchain/stores/message/upstash_redis' import { Redis, RedisConfigNodejs } from '@upstash/redis' class UpstashRedisBackedChatMemory_Memory implements INode { @@ -14,6 +14,7 @@ class UpstashRedisBackedChatMemory_Memory implements INode { icon: string category: string baseClasses: string[] + credential: INodeParams inputs: INodeParams[] constructor() { @@ -25,18 +26,19 @@ class UpstashRedisBackedChatMemory_Memory implements INode { this.category = 'Memory' this.description = 'Summarizes the conversation and stores the memory in upstash Redis server' this.baseClasses = [this.type, ...getBaseClasses(BufferMemory)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + description: 'Configure password authentication on your upstash redis instance', + credentialNames: ['upstashRedisMemoryApi'] + } this.inputs = [ { label: 'Base URL', name: 'baseURL', type: 'string', - default: 'redis://localhost:6379' - }, - { - label: 'Token', - name: 'token', - type: 'string', - default: '********' + default: 'https://.upstash.io' }, { label: 'Session Id', @@ -70,7 +72,7 @@ class UpstashRedisBackedChatMemory_Memory implements INode { } async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const redis = initalizeUpstashRedis(nodeData, options) + const redis = await initalizeUpstashRedis(nodeData, options) const sessionId = nodeData.inputs?.sessionId as string const chatId = options?.chatId as string options.logger.info(`Clearing Upstash Redis memory session ${sessionId ? sessionId : chatId}`) @@ -79,18 +81,20 @@ class UpstashRedisBackedChatMemory_Memory implements INode { } } -const initalizeUpstashRedis = (nodeData: INodeData, options: ICommonObject): BufferMemory => { +const initalizeUpstashRedis = async (nodeData: INodeData, options: ICommonObject): Promise => { const baseURL = nodeData.inputs?.baseURL as string 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 token = nodeData.inputs?.token as string let isSessionIdUsingChatMessageId = false if (!sessionId && chatId) isSessionIdUsingChatMessageId = true - const upstashRedisConfig = ({ url: baseURL, token }) as RedisConfigNodejs + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const upstashRedisPassword = getCredentialParam('upstashRedisPassword', credentialData, nodeData) + + const upstashRedisConfig = { url: baseURL, token: upstashRedisPassword } as RedisConfigNodejs const redisClient = new Redis(upstashRedisConfig) let obj: UpstashRedisChatMessageHistoryInput = { sessionId: sessionId ? sessionId : chatId, From 424e71312873fc88023fb7bc3e91bcb607f4a06b Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Sun, 8 Oct 2023 01:52:19 +0100 Subject: [PATCH 4/7] Update UpstashRedisBackedChatMemory.ts code cleanup --- .../UpstashRedisBackedChatMemory.ts | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index 98983ecbca5..00d7697ad6b 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -2,8 +2,7 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { ICommonObject } from '../../../src' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' -import { UpstashRedisChatMessageHistory, UpstashRedisChatMessageHistoryInput } from 'langchain/stores/message/upstash_redis' -import { Redis, RedisConfigNodejs } from '@upstash/redis' +import { UpstashRedisChatMessageHistory } from 'langchain/stores/message/upstash_redis' class UpstashRedisBackedChatMemory_Memory implements INode { label: string @@ -35,10 +34,10 @@ class UpstashRedisBackedChatMemory_Memory implements INode { } this.inputs = [ { - label: 'Base URL', + label: 'Upstash Redis REST URL', name: 'baseURL', type: 'string', - default: 'https://.upstash.io' + placeholder: 'https://.upstash.io' }, { label: 'Session Id', @@ -56,13 +55,6 @@ class UpstashRedisBackedChatMemory_Memory implements INode { description: 'Omit this parameter to make sessions never expire', additionalParams: true, optional: true - }, - { - label: 'Memory Key', - name: 'memoryKey', - type: 'string', - default: 'chat_history', - additionalParams: true } ] } @@ -84,38 +76,29 @@ class UpstashRedisBackedChatMemory_Memory implements INode { const initalizeUpstashRedis = async (nodeData: INodeData, options: ICommonObject): Promise => { const baseURL = nodeData.inputs?.baseURL as string const sessionId = nodeData.inputs?.sessionId as string - const sessionTTL = nodeData.inputs?.sessionTTL as number - const memoryKey = nodeData.inputs?.memoryKey as string + const sessionTTL = nodeData.inputs?.sessionTTL as string const chatId = options?.chatId as string let isSessionIdUsingChatMessageId = false if (!sessionId && chatId) isSessionIdUsingChatMessageId = true const credentialData = await getCredentialData(nodeData.credential ?? '', options) - const upstashRedisPassword = getCredentialParam('upstashRedisPassword', credentialData, nodeData) + const upstashRestToken = getCredentialParam('upstashRestToken', credentialData, nodeData) - const upstashRedisConfig = { url: baseURL, token: upstashRedisPassword } as RedisConfigNodejs - const redisClient = new Redis(upstashRedisConfig) - let obj: UpstashRedisChatMessageHistoryInput = { + const redisChatMessageHistory = new UpstashRedisChatMessageHistory({ sessionId: sessionId ? sessionId : chatId, - client: redisClient - } - - if (sessionTTL) { - obj = { - ...obj, - sessionTTL + sessionTTL: sessionTTL ? parseInt(sessionTTL, 10) : undefined, + config: { + url: baseURL, + token: upstashRestToken } - } - - const redisChatMessageHistory = new UpstashRedisChatMessageHistory(obj) + }) const memory = new BufferMemoryExtended({ - memoryKey, chatHistory: redisChatMessageHistory, - returnMessages: true, isSessionIdUsingChatMessageId }) + return memory } From e2355555ed16e87be60d697378f9485e031d22ec Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Sun, 8 Oct 2023 01:52:48 +0100 Subject: [PATCH 5/7] Update UpstashRedisMemoryApi.credential.ts rename token label --- .../credentials/UpstashRedisMemoryApi.credential.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/credentials/UpstashRedisMemoryApi.credential.ts b/packages/components/credentials/UpstashRedisMemoryApi.credential.ts index 9c0f99aabab..8d3e9528640 100644 --- a/packages/components/credentials/UpstashRedisMemoryApi.credential.ts +++ b/packages/components/credentials/UpstashRedisMemoryApi.credential.ts @@ -12,11 +12,11 @@ class UpstashRedisMemoryApi implements INodeCredential { this.name = 'upstashRedisMemoryApi' this.version = 1.0 this.description = - 'Refer to official guide on how to create redis instance and get password on upstash' + 'Refer to official guide on how to create redis instance and get redis REST Token' this.inputs = [ { - label: 'Upstash Redis Password', - name: 'upstashRedisPassword', + label: 'Upstash Redis REST Token', + name: 'upstashRestToken', type: 'password' } ] From 7c020c4b3ea97c8fef4b79e4d3a083d1bca7cf9f Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Sun, 8 Oct 2023 01:53:58 +0100 Subject: [PATCH 6/7] Update package.json --- packages/components/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 77ada2ad56a..cf5a5f17a53 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -28,6 +28,7 @@ "@supabase/supabase-js": "^2.29.0", "@types/js-yaml": "^4.0.5", "@types/jsdom": "^21.1.1", + "@upstash/redis": "^1.22.1", "@zilliz/milvus2-sdk-node": "^2.2.24", "apify-client": "^2.7.1", "axios": "^0.27.2", @@ -64,8 +65,7 @@ "srt-parser-2": "^1.2.3", "vm2": "^3.9.19", "weaviate-ts-client": "^1.1.0", - "ws": "^8.9.0", - "@upstash/redis": "^1.22.1" + "ws": "^8.9.0" }, "devDependencies": { "@types/gulp": "4.0.9", From 8b555e02f80b7deedb8d54f658a390e8e431feaf Mon Sep 17 00:00:00 2001 From: Henry Heng Date: Sun, 8 Oct 2023 01:55:45 +0100 Subject: [PATCH 7/7] Update UpstashRedisBackedChatMemory.ts camelCase node name --- .../UpstashRedisBackedChatMemory.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index 00d7697ad6b..6b5fdf66090 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -18,12 +18,12 @@ class UpstashRedisBackedChatMemory_Memory implements INode { constructor() { this.label = 'Upstash Redis-Backed Chat Memory' - this.name = 'UpstashRedisBackedChatMemory' + this.name = 'upstashRedisBackedChatMemory' this.version = 1.0 this.type = 'UpstashRedisBackedChatMemory' this.icon = 'upstash.svg' this.category = 'Memory' - this.description = 'Summarizes the conversation and stores the memory in upstash Redis server' + this.description = 'Summarizes the conversation and stores the memory in Upstash Redis server' this.baseClasses = [this.type, ...getBaseClasses(BufferMemory)] this.credential = { label: 'Connect Credential',