clear session memory

This commit is contained in:
Henry 2023-07-11 16:29:30 +01:00
parent 16440aec37
commit 6827a13e37
7 changed files with 178 additions and 101 deletions

View File

@ -65,37 +65,47 @@ class DynamoDb_Memory implements INode {
} }
] ]
} }
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> { async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const tableName = nodeData.inputs?.tableName as string return initalizeDynamoDB(nodeData, options)
const partitionKey = nodeData.inputs?.partitionKey as string }
const sessionId = nodeData.inputs?.sessionId as string
const region = nodeData.inputs?.region as string
const accessKey = nodeData.inputs?.accessKey as string
const secretAccessKey = nodeData.inputs?.secretAccessKey as string
const memoryKey = nodeData.inputs?.memoryKey as string
const chatId = options.chatId async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const dynamodbMemory = initalizeDynamoDB(nodeData, options)
const dynamoDb = new DynamoDBChatMessageHistory({ dynamodbMemory.clear()
tableName,
partitionKey,
sessionId: sessionId ? sessionId : chatId,
config: {
region,
credentials: {
accessKeyId: accessKey,
secretAccessKey
}
}
})
const memory = new BufferMemory({
memoryKey,
chatHistory: dynamoDb,
returnMessages: true
})
return memory
} }
} }
const initalizeDynamoDB = (nodeData: INodeData, options: ICommonObject): BufferMemory => {
const tableName = nodeData.inputs?.tableName as string
const partitionKey = nodeData.inputs?.partitionKey as string
const sessionId = nodeData.inputs?.sessionId as string
const region = nodeData.inputs?.region as string
const accessKey = nodeData.inputs?.accessKey as string
const secretAccessKey = nodeData.inputs?.secretAccessKey as string
const memoryKey = nodeData.inputs?.memoryKey as string
const chatId = options.chatId
const dynamoDb = new DynamoDBChatMessageHistory({
tableName,
partitionKey,
sessionId: sessionId ? sessionId : chatId,
config: {
region,
credentials: {
accessKeyId: accessKey,
secretAccessKey
}
}
})
const memory = new BufferMemory({
memoryKey,
chatHistory: dynamoDb,
returnMessages: true
})
return memory
}
module.exports = { nodeClass: DynamoDb_Memory } module.exports = { nodeClass: DynamoDb_Memory }

View File

@ -64,35 +64,44 @@ class MotorMemory_Memory implements INode {
} }
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> { async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const memoryKey = nodeData.inputs?.memoryKey as string return initalizeMotorhead(nodeData, options)
const baseURL = nodeData.inputs?.baseURL as string }
const sessionId = nodeData.inputs?.sessionId as string
const apiKey = nodeData.inputs?.apiKey as string
const clientId = nodeData.inputs?.clientId as string
const chatId = options?.chatId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const motorhead = initalizeMotorhead(nodeData, options)
let obj: MotorheadMemoryInput = { motorhead.clear()
returnMessages: true,
sessionId: sessionId ? sessionId : chatId,
memoryKey
}
if (baseURL) {
obj = {
...obj,
url: baseURL
}
} else {
obj = {
...obj,
apiKey,
clientId
}
}
return new MotorheadMemory(obj)
} }
} }
const initalizeMotorhead = (nodeData: INodeData, options: ICommonObject): MotorheadMemory => {
const memoryKey = nodeData.inputs?.memoryKey as string
const baseURL = nodeData.inputs?.baseURL as string
const sessionId = nodeData.inputs?.sessionId as string
const apiKey = nodeData.inputs?.apiKey as string
const clientId = nodeData.inputs?.clientId as string
const chatId = options?.chatId as string
let obj: MotorheadMemoryInput = {
returnMessages: true,
sessionId: sessionId ? sessionId : chatId,
memoryKey
}
if (baseURL) {
obj = {
...obj,
url: baseURL
}
} else {
obj = {
...obj,
apiKey,
clientId
}
}
return new MotorheadMemory(obj)
}
module.exports = { nodeClass: MotorMemory_Memory } module.exports = { nodeClass: MotorMemory_Memory }

View File

@ -56,31 +56,40 @@ class RedisBackedChatMemory_Memory implements INode {
} }
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> { async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const baseURL = nodeData.inputs?.baseURL as string return initalizeRedis(nodeData, options)
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 async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const redis = initalizeRedis(nodeData, options)
const redisClient = createClient({ url: baseURL }) redis.clear()
let obj: RedisChatMessageHistoryInput = {
sessionId: sessionId ? sessionId : chatId,
client: redisClient
}
if (sessionTTL) {
obj = {
...obj,
sessionTTL
}
}
let redisChatMessageHistory = new RedisChatMessageHistory(obj)
let redis = new BufferMemory({ memoryKey, chatHistory: redisChatMessageHistory, returnMessages: true })
return redis
} }
} }
const initalizeRedis = (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 redisClient = createClient({ url: baseURL })
let obj: RedisChatMessageHistoryInput = {
sessionId: sessionId ? sessionId : chatId,
client: redisClient
}
if (sessionTTL) {
obj = {
...obj,
sessionTTL
}
}
let redisChatMessageHistory = new RedisChatMessageHistory(obj)
let redis = new BufferMemory({ memoryKey, chatHistory: redisChatMessageHistory, returnMessages: true })
return redis
}
module.exports = { nodeClass: RedisBackedChatMemory_Memory } module.exports = { nodeClass: RedisBackedChatMemory_Memory }

View File

@ -104,31 +104,11 @@ class ZepMemory_Memory implements INode {
} }
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> { async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const baseURL = nodeData.inputs?.baseURL as string
const aiPrefix = nodeData.inputs?.aiPrefix as string
const humanPrefix = nodeData.inputs?.humanPrefix as string
const memoryKey = nodeData.inputs?.memoryKey as string
const inputKey = nodeData.inputs?.inputKey as string
const autoSummaryTemplate = nodeData.inputs?.autoSummaryTemplate as string const autoSummaryTemplate = nodeData.inputs?.autoSummaryTemplate as string
const autoSummary = nodeData.inputs?.autoSummary as boolean const autoSummary = nodeData.inputs?.autoSummary as boolean
const sessionId = nodeData.inputs?.sessionId as string
const apiKey = nodeData.inputs?.apiKey as string
const k = nodeData.inputs?.k as string const k = nodeData.inputs?.k as string
const chatId = options?.chatId as string let zep = initalizeZep(nodeData, options)
const obj: ZepMemoryInput = {
baseURL,
sessionId: sessionId ? sessionId : chatId,
aiPrefix,
humanPrefix,
returnMessages: true,
memoryKey,
inputKey
}
if (apiKey) obj.apiKey = apiKey
let zep = new ZepMemory(obj)
// hack to support summary // hack to support summary
let tmpFunc = zep.loadMemoryVariables let tmpFunc = zep.loadMemoryVariables
@ -153,6 +133,37 @@ class ZepMemory_Memory implements INode {
} }
return zep return zep
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const zep = initalizeZep(nodeData, options)
zep.clear()
}
}
const initalizeZep = (nodeData: INodeData, options: ICommonObject) => {
const baseURL = nodeData.inputs?.baseURL as string
const aiPrefix = nodeData.inputs?.aiPrefix as string
const humanPrefix = nodeData.inputs?.humanPrefix as string
const memoryKey = nodeData.inputs?.memoryKey as string
const inputKey = nodeData.inputs?.inputKey as string
const sessionId = nodeData.inputs?.sessionId as string
const apiKey = nodeData.inputs?.apiKey as string
const chatId = options?.chatId as string
const obj: ZepMemoryInput = {
baseURL,
sessionId: sessionId ? sessionId : chatId,
aiPrefix,
humanPrefix,
returnMessages: true,
memoryKey,
inputKey
}
if (apiKey) obj.apiKey = apiKey
return new ZepMemory(obj)
} }
module.exports = { nodeClass: ZepMemory_Memory } module.exports = { nodeClass: ZepMemory_Memory }

View File

@ -96,6 +96,7 @@ export interface INode extends INodeProperties {
} }
init?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<any> init?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<any>
run?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<string | ICommonObject> run?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<string | ICommonObject>
clearSessionMemory?(nodeData: INodeData, options?: ICommonObject): Promise<void>
} }
export interface INodeData extends INodeProperties { export interface INodeData extends INodeProperties {

View File

@ -39,7 +39,8 @@ import {
isFlowValidForStream, isFlowValidForStream,
isVectorStoreFaiss, isVectorStoreFaiss,
databaseEntities, databaseEntities,
getApiKey getApiKey,
clearSessionMemory
} from './utils' } from './utils'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { getDataSource } from './DataSource' import { getDataSource } from './DataSource'
@ -320,6 +321,19 @@ export class App {
// Delete all chatmessages from chatflowid // Delete all chatmessages from chatflowid
this.app.delete('/api/v1/chatmessage/:id', async (req: Request, res: Response) => { this.app.delete('/api/v1/chatmessage/:id', async (req: Request, res: Response) => {
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
id: req.params.id
})
if (!chatflow) {
res.status(404).send(`Chatflow ${req.params.id} not found`)
return
}
const flowData = chatflow.flowData
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
const nodes = parsedFlowData.nodes
let chatId = await getChatId(chatflow.id)
if (!chatId) chatId = chatflow.id
clearSessionMemory(nodes, this.nodesPool.componentNodes, chatId, req.query.sessionId as string)
const results = await this.AppDataSource.getRepository(ChatMessage).delete({ chatflowid: req.params.id }) const results = await this.AppDataSource.getRepository(ChatMessage).delete({ chatflowid: req.params.id })
return res.json(results) return res.json(results)
}) })
@ -662,7 +676,7 @@ export class App {
if (!chatflow) return res.status(404).send(`Chatflow ${chatflowid} not found`) if (!chatflow) return res.status(404).send(`Chatflow ${chatflowid} not found`)
let chatId = await getChatId(chatflow.id) let chatId = await getChatId(chatflow.id)
if (!chatId) chatId = Date.now().toString() if (!chatId) chatId = chatflowid
if (!isInternal) { if (!isInternal) {
await this.validateKey(req, res, chatflow) await this.validateKey(req, res, chatflow)

View File

@ -267,6 +267,29 @@ export const buildLangchain = async (
return flowNodes return flowNodes
} }
/**
* Clear memory
* @param {IReactFlowNode[]} reactFlowNodes
* @param {IComponentNodes} componentNodes
* @param {string} chatId
* @param {string} sessionId
*/
export const clearSessionMemory = async (
reactFlowNodes: IReactFlowNode[],
componentNodes: IComponentNodes,
chatId: string,
sessionId?: string
) => {
for (const node of reactFlowNodes) {
if (node.data.category !== 'Memory') continue
const nodeInstanceFilePath = componentNodes[node.data.name].filePath as string
const nodeModule = await import(nodeInstanceFilePath)
const newNodeInstance = new nodeModule.nodeClass()
if (sessionId && node.data.inputs) node.data.inputs.sessionId = sessionId
if (newNodeInstance.clearSessionMemory) await newNodeInstance?.clearSessionMemory(node.data, { chatId })
}
}
/** /**
* Get variable value from outputResponses.output * Get variable value from outputResponses.output
* @param {string} paramValue * @param {string} paramValue