Skip to content

Commit

Permalink
⚡ Implement Redis Chat Memory, update LC
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegIvaniv committed Sep 14, 2023
1 parent 04efefb commit a2bf75a
Show file tree
Hide file tree
Showing 7 changed files with 1,176 additions and 888 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import type { IExecuteFunctions, INodeType, INodeTypeDescription, SupplyData } from 'n8n-workflow';

import { OpenAI } from 'langchain';
import { OpenAI } from 'langchain/llms/openai';
import { logWrapper } from '../../../utils/logWrapper';

export class LmOpenAi implements INodeType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ class MemoryBufferSingleton {
}

public async getMemory(
memoryKey: string,
sessionKey: string,
memoryParams: BufferWindowMemoryInput,
): Promise<BufferWindowMemory> {
await this.cleanupStaleBuffers();

let memoryInstance = this.memoryBuffer.get(memoryKey);
let memoryInstance = this.memoryBuffer.get(sessionKey);
if (memoryInstance) {
memoryInstance.last_accessed = new Date();
} else {
Expand All @@ -40,7 +40,7 @@ class MemoryBufferSingleton {
created: new Date(),
last_accessed: new Date(),
};
this.memoryBuffer.set(memoryKey, memoryInstance);
this.memoryBuffer.set(sessionKey, memoryInstance);
}
return memoryInstance.buffer;
}
Expand Down Expand Up @@ -89,8 +89,8 @@ export class MemoryBufferWindow implements INodeType {
// default: 'input',
// },
{
displayName: 'Memory Key',
name: 'memoryKey',
displayName: 'Session Key',
name: 'sessionKey',
type: 'string',
default: 'chat_history',
description: 'The key to use to store the memory in the workflow data',
Expand All @@ -106,12 +106,12 @@ export class MemoryBufferWindow implements INodeType {
};

async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
const memoryKey = this.getNodeParameter('memoryKey', 0) as string;
const sessionKey = this.getNodeParameter('sessionKey', 0) as string;
const contextWindowLength = this.getNodeParameter('contextWindowLength', 0) as number;
const workflowId = this.getWorkflow().id;
const memoryInstance = MemoryBufferSingleton.getInstance();

const memory = await memoryInstance.getMemory(`${workflowId}__${memoryKey}`, {
const memory = await memoryInstance.getMemory(`${workflowId}__${sessionKey}`, {
k: contextWindowLength,
inputKey: 'input',
memoryKey: 'chat_history',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* eslint-disable n8n-nodes-base/node-dirname-against-convention */
import {
NodeOperationError,
type IExecuteFunctions,
type INodeType,
type INodeTypeDescription,
type SupplyData,
} from 'n8n-workflow';
import { BufferMemory } from 'langchain/memory';
import type { RedisChatMessageHistoryInput } from 'langchain/stores/message/redis';
import { RedisChatMessageHistory } from 'langchain/stores/message/redis';
import type { RedisClientOptions } from 'redis';
import { createClient } from 'redis';
import { logWrapper } from '../../../utils/logWrapper';

export class MemoryRedisChat implements INodeType {
description: INodeTypeDescription = {
displayName: 'Redis Chat Memory',
name: 'memoryRedisChat',
icon: 'file:redis.svg',
group: ['transform'],
version: 1,
description: 'Stores the chat history in Redis.',
defaults: {
name: 'Redis Chat Memory',
},
credentials: [
{
name: 'redis',
required: true,
},
],
codex: {
categories: ['AI'],
subcategories: {
AI: ['Memory'],
},
},
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
inputs: [],
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
outputs: ['memory'],
outputNames: ['Memory'],
properties: [
{
displayName: 'Session Key',
name: 'sessionKey',
type: 'string',
default: 'chat_history',
description: 'The key to use to store the memory in the workflow data',
},
{
displayName: 'Session Time To Live',
name: 'sessionTTL',
type: 'number',
default: 0,
description:
'For how long the session should be stored in seconds. If set to 0 it will not expire.',
},
],
};

async supplyData(this: IExecuteFunctions): Promise<SupplyData> {
const credentials = await this.getCredentials('redis');
const sessionKey = this.getNodeParameter('sessionKey', 0) as string;
const sessionTTL = this.getNodeParameter('sessionTTL', 0, 0) as number;

const redisOptions: RedisClientOptions = {
socket: {
host: credentials.host as string,
port: credentials.port as number,
},
database: credentials.database as number,
};

if (credentials.password) {
redisOptions.password = credentials.password as string;
}

const client = createClient({
...redisOptions,
});

client.on('error', async (error: Error) => {
await client.quit();
throw new NodeOperationError(this.getNode(), 'Redis Error: ' + error.message);
});

const redisChatConfig: RedisChatMessageHistoryInput = {
client,
sessionId: sessionKey,
};

if (sessionTTL > 0) {
redisChatConfig.sessionTTL = sessionTTL;
}
const redisChatHistory = new RedisChatMessageHistory(redisChatConfig);

const memory = new BufferMemory({
memoryKey: 'chat_history',
chatHistory: redisChatHistory,
returnMessages: true,
inputKey: 'input',
outputKey: 'output',
});

return {
response: logWrapper(memory, this),
};
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion packages/@n8n/nodes-langchain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"dist/nodes/llms/LMOpenHuggingFaceInference/LmOpenHuggingFaceInference.node.js",
"dist/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.js",
"dist/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.js",
"dist/nodes/memory/MemoryRedisChat/MemoryRedisChat.node.js",
"dist/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.js",
"dist/nodes/memory/MemoryXata/MemoryXata.node.js",
"dist/nodes/output_parser/OutputParserAutofixing/OutputParserAutofixing.node.js",
Expand Down Expand Up @@ -103,14 +104,15 @@
"html-to-text": "^9.0.5",
"ignore": "^5.2.4",
"json-schema-to-zod": "^1.1.1",
"langchain": "^0.0.136",
"langchain": "^0.0.149",
"lodash": "^4.17.21",
"mammoth": "^1.6.0",
"mssql": "^8.1.2",
"n8n-nodes-base": "workspace:*",
"n8n-workflow": "*",
"pdf-parse": "^1.1.1",
"pg": "^8.11.3",
"redis": "^4.6.8",
"sqlite3": "^5.1.6",
"temp": "^0.9.4",
"typeorm": "^0.3.17",
Expand Down
2 changes: 1 addition & 1 deletion packages/@n8n/nodes-langchain/utils/logWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BaseChatMessageHistory } from 'langchain/schema';
import { BaseChatModel } from 'langchain/chat_models/base';
import type { CallbackManagerForLLMRun } from 'langchain/callbacks';

import { Embeddings } from 'langchain/embeddings';
import { Embeddings } from 'langchain/embeddings/base';
import type { VectorStoreRetriever } from 'langchain/vectorstores/base';
import { VectorStore } from 'langchain/vectorstores/base';
import type { Document } from 'langchain/document';
Expand Down
Loading

0 comments on commit a2bf75a

Please # to comment.