diff --git a/packages/components/nodes/agents/OpenAIAssistant/OpenAIAssistant.ts b/packages/components/nodes/agents/OpenAIAssistant/OpenAIAssistant.ts index 35299057c..7f2377bde 100644 --- a/packages/components/nodes/agents/OpenAIAssistant/OpenAIAssistant.ts +++ b/packages/components/nodes/agents/OpenAIAssistant/OpenAIAssistant.ts @@ -85,43 +85,46 @@ class OpenAIAssistant_Agents implements INode { return null } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const selectedAssistantId = nodeData.inputs?.selectedAssistant as string - const appDataSource = options.appDataSource as DataSource - const databaseEntities = options.databaseEntities as IDatabaseEntity - let sessionId = nodeData.inputs?.sessionId as string + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const selectedAssistantId = nodeData.inputs?.selectedAssistant as string + const appDataSource = options.appDataSource as DataSource + const databaseEntities = options.databaseEntities as IDatabaseEntity + let sessionId = nodeData.inputs?.sessionId as string - const assistant = await appDataSource.getRepository(databaseEntities['Assistant']).findOneBy({ - id: selectedAssistantId - }) - - if (!assistant) { - options.logger.error(`Assistant ${selectedAssistantId} not found`) - return - } - - if (!sessionId && options.chatId) { - const chatmsg = await appDataSource.getRepository(databaseEntities['ChatMessage']).findOneBy({ - chatId: options.chatId + const assistant = await appDataSource.getRepository(databaseEntities['Assistant']).findOneBy({ + id: selectedAssistantId }) - if (!chatmsg) { - options.logger.error(`Chat Message with Chat Id: ${options.chatId} not found`) + + if (!assistant) { + options.logger.error(`Assistant ${selectedAssistantId} not found`) return } - sessionId = chatmsg.sessionId - } - const credentialData = await getCredentialData(assistant.credential ?? '', options) - const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData) - if (!openAIApiKey) { - options.logger.error(`OpenAI ApiKey not found`) - return - } + if (!sessionId && options.chatId) { + const chatmsg = await appDataSource.getRepository(databaseEntities['ChatMessage']).findOneBy({ + chatId: options.chatId + }) + if (!chatmsg) { + options.logger.error(`Chat Message with Chat Id: ${options.chatId} not found`) + return + } + sessionId = chatmsg.sessionId + } - const openai = new OpenAI({ apiKey: openAIApiKey }) - options.logger.info(`Clearing OpenAI Thread ${sessionId}`) - if (sessionId) await openai.beta.threads.del(sessionId) - options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`) + const credentialData = await getCredentialData(assistant.credential ?? '', options) + const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData) + if (!openAIApiKey) { + options.logger.error(`OpenAI ApiKey not found`) + return + } + + const openai = new OpenAI({ apiKey: openAIApiKey }) + options.logger.info(`Clearing OpenAI Thread ${sessionId}`) + if (sessionId) await openai.beta.threads.del(sessionId) + options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`) + } } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { @@ -359,7 +362,10 @@ class OpenAIAssistant_Agents implements INode { } // Replace the text with a footnote - message_content.value = message_content.value.replace(`${annotation.text}`, `${filePath}`) + message_content.value = message_content.value.replace( + `${annotation.text}`, + `${disableFileDownload ? '' : filePath}` + ) } returnVal += message_content.value diff --git a/packages/components/nodes/memory/DynamoDb/DynamoDb.ts b/packages/components/nodes/memory/DynamoDb/DynamoDb.ts index ac4f76020..8ca6cf9e5 100644 --- a/packages/components/nodes/memory/DynamoDb/DynamoDb.ts +++ b/packages/components/nodes/memory/DynamoDb/DynamoDb.ts @@ -1,4 +1,13 @@ -import { ICommonObject, INode, INodeData, INodeParams, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src' +import { + ICommonObject, + INode, + INodeData, + INodeParams, + getBaseClasses, + getCredentialData, + getCredentialParam, + serializeChatHistory +} from '../../../src' import { DynamoDBChatMessageHistory } from 'langchain/stores/message/dynamodb' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' @@ -70,13 +79,23 @@ class DynamoDb_Memory implements INode { return initalizeDynamoDB(nodeData, options) } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const dynamodbMemory = await initalizeDynamoDB(nodeData, options) - const sessionId = nodeData.inputs?.sessionId as string - const chatId = options?.chatId as string - options.logger.info(`Clearing DynamoDb memory session ${sessionId ? sessionId : chatId}`) - await dynamodbMemory.clear() - options.logger.info(`Successfully cleared DynamoDb memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const dynamodbMemory = await initalizeDynamoDB(nodeData, options) + const sessionId = nodeData.inputs?.sessionId as string + const chatId = options?.chatId as string + options.logger.info(`Clearing DynamoDb memory session ${sessionId ? sessionId : chatId}`) + await dynamodbMemory.clear() + options.logger.info(`Successfully cleared DynamoDb memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const memoryKey = nodeData.inputs?.memoryKey as string + const dynamodbMemory = await initalizeDynamoDB(nodeData, options) + const key = memoryKey ?? 'chat_history' + const memoryResult = await dynamodbMemory.loadMemoryVariables({}) + return serializeChatHistory(memoryResult[key]) + } } } diff --git a/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts b/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts index 6f800cdc4..76cb7e313 100644 --- a/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts +++ b/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts @@ -1,4 +1,13 @@ -import { getBaseClasses, getCredentialData, getCredentialParam, ICommonObject, INode, INodeData, INodeParams } from '../../../src' +import { + getBaseClasses, + getCredentialData, + getCredentialParam, + ICommonObject, + INode, + INodeData, + INodeParams, + serializeChatHistory +} from '../../../src' import { MongoDBChatMessageHistory } from 'langchain/stores/message/mongodb' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { BaseMessage, mapStoredMessageToChatMessage } from 'langchain/schema' @@ -67,13 +76,23 @@ class MongoDB_Memory implements INode { return initializeMongoDB(nodeData, options) } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const mongodbMemory = await initializeMongoDB(nodeData, options) - const sessionId = nodeData.inputs?.sessionId as string - const chatId = options?.chatId as string - options.logger.info(`Clearing MongoDB memory session ${sessionId ? sessionId : chatId}`) - await mongodbMemory.clear() - options.logger.info(`Successfully cleared MongoDB memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const mongodbMemory = await initializeMongoDB(nodeData, options) + const sessionId = nodeData.inputs?.sessionId as string + const chatId = options?.chatId as string + options.logger.info(`Clearing MongoDB memory session ${sessionId ? sessionId : chatId}`) + await mongodbMemory.clear() + options.logger.info(`Successfully cleared MongoDB memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const memoryKey = nodeData.inputs?.memoryKey as string + const mongodbMemory = await initializeMongoDB(nodeData, options) + const key = memoryKey ?? 'chat_history' + const memoryResult = await mongodbMemory.loadMemoryVariables({}) + return serializeChatHistory(memoryResult[key]) + } } } diff --git a/packages/components/nodes/memory/MotorheadMemory/MotorheadMemory.ts b/packages/components/nodes/memory/MotorheadMemory/MotorheadMemory.ts index 0ec2f42ad..9cdbcd5cc 100644 --- a/packages/components/nodes/memory/MotorheadMemory/MotorheadMemory.ts +++ b/packages/components/nodes/memory/MotorheadMemory/MotorheadMemory.ts @@ -3,6 +3,7 @@ import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../ import { ICommonObject } from '../../../src' import { MotorheadMemory, MotorheadMemoryInput } from 'langchain/memory' import fetch from 'node-fetch' +import { getBufferString } from 'langchain/memory' class MotorMemory_Memory implements INode { label: string @@ -64,13 +65,23 @@ class MotorMemory_Memory implements INode { return initalizeMotorhead(nodeData, options) } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const motorhead = await initalizeMotorhead(nodeData, options) - const sessionId = nodeData.inputs?.sessionId as string - const chatId = options?.chatId as string - options.logger.info(`Clearing Motorhead memory session ${sessionId ? sessionId : chatId}`) - await motorhead.clear() - options.logger.info(`Successfully cleared Motorhead memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const motorhead = await initalizeMotorhead(nodeData, options) + const sessionId = nodeData.inputs?.sessionId as string + const chatId = options?.chatId as string + options.logger.info(`Clearing Motorhead memory session ${sessionId ? sessionId : chatId}`) + await motorhead.clear() + options.logger.info(`Successfully cleared Motorhead memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const memoryKey = nodeData.inputs?.memoryKey as string + const motorhead = await initalizeMotorhead(nodeData, options) + const key = memoryKey ?? 'chat_history' + const memoryResult = await motorhead.loadMemoryVariables({}) + return getBufferString(memoryResult[key]) + } } } diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index bdb62911c..7fe447ad5 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -1,5 +1,5 @@ import { INode, INodeData, INodeParams, ICommonObject } from '../../../src/Interface' -import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { getBaseClasses, getCredentialData, getCredentialParam, serializeChatHistory } from '../../../src/utils' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/ioredis' import { mapStoredMessageToChatMessage, BaseMessage } from 'langchain/schema' @@ -65,13 +65,23 @@ class RedisBackedChatMemory_Memory implements INode { return await initalizeRedis(nodeData, options) } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - 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}`) - await redis.clear() - options.logger.info(`Successfully cleared Redis memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + 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}`) + await redis.clear() + options.logger.info(`Successfully cleared Redis memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const memoryKey = nodeData.inputs?.memoryKey as string + const redis = await initalizeRedis(nodeData, options) + const key = memoryKey ?? 'chat_history' + const memoryResult = await redis.loadMemoryVariables({}) + return serializeChatHistory(memoryResult[key]) + } } } diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index 2b8b46503..8bca04404 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -1,5 +1,5 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { getBaseClasses, getCredentialData, getCredentialParam, serializeChatHistory } from '../../../src/utils' import { ICommonObject } from '../../../src' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { UpstashRedisChatMessageHistory } from 'langchain/stores/message/upstash_redis' @@ -63,13 +63,22 @@ class UpstashRedisBackedChatMemory_Memory implements INode { return initalizeUpstashRedis(nodeData, options) } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - 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}`) - await redis.clear() - options.logger.info(`Successfully cleared Upstash Redis memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + 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}`) + await redis.clear() + options.logger.info(`Successfully cleared Upstash Redis memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const redis = await initalizeUpstashRedis(nodeData, options) + const key = 'chat_history' + const memoryResult = await redis.loadMemoryVariables({}) + return serializeChatHistory(memoryResult[key]) + } } } diff --git a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts index c44986444..ced871a1e 100644 --- a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts +++ b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts @@ -3,6 +3,7 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { ZepMemory, ZepMemoryInput } from 'langchain/memory/zep' import { ICommonObject } from '../../../src' +import { getBufferString } from 'langchain/memory' class ZepMemory_Memory implements INode { label: string @@ -140,13 +141,25 @@ class ZepMemory_Memory implements INode { return zep } - async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { - const zep = await initalizeZep(nodeData, options) - const sessionId = nodeData.inputs?.sessionId as string - const chatId = options?.chatId as string - options.logger.info(`Clearing Zep memory session ${sessionId ? sessionId : chatId}`) - await zep.clear() - options.logger.info(`Successfully cleared Zep memory session ${sessionId ? sessionId : chatId}`) + //@ts-ignore + memoryMethods = { + async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise { + const zep = await initalizeZep(nodeData, options) + const sessionId = nodeData.inputs?.sessionId as string + const chatId = options?.chatId as string + options.logger.info(`Clearing Zep memory session ${sessionId ? sessionId : chatId}`) + await zep.clear() + options.logger.info(`Successfully cleared Zep memory session ${sessionId ? sessionId : chatId}`) + }, + async getChatMessages(nodeData: INodeData, options: ICommonObject): Promise { + const memoryKey = nodeData.inputs?.memoryKey as string + const aiPrefix = nodeData.inputs?.aiPrefix as string + const humanPrefix = nodeData.inputs?.humanPrefix as string + const zep = await initalizeZep(nodeData, options) + const key = memoryKey ?? 'chat_history' + const memoryResult = await zep.loadMemoryVariables({}) + return getBufferString(memoryResult[key], humanPrefix, aiPrefix) + } } } diff --git a/packages/components/nodes/tools/ChainTool/core.ts b/packages/components/nodes/tools/ChainTool/core.ts index 6c3dba554..5520d6df0 100644 --- a/packages/components/nodes/tools/ChainTool/core.ts +++ b/packages/components/nodes/tools/ChainTool/core.ts @@ -1,5 +1,6 @@ import { DynamicTool, DynamicToolInput } from 'langchain/tools' import { BaseChain } from 'langchain/chains' +import { handleEscapeCharacters } from '../../../src/utils' export interface ChainToolInput extends Omit { chain: BaseChain @@ -14,7 +15,8 @@ export class ChainTool extends DynamicTool { func: async (input, runManager) => { // To enable LLM Chain which has promptValues if ((chain as any).prompt && (chain as any).prompt.promptValues) { - const values = await chain.call((chain as any).prompt.promptValues, runManager?.getChild()) + const promptValues = handleEscapeCharacters((chain as any).prompt.promptValues, true) + const values = await chain.call(promptValues, runManager?.getChild()) return values?.text } return chain.run(input, runManager?.getChild()) diff --git a/packages/components/src/Interface.ts b/packages/components/src/Interface.ts index af3042720..6752f9440 100644 --- a/packages/components/src/Interface.ts +++ b/packages/components/src/Interface.ts @@ -107,9 +107,12 @@ export interface INode extends INodeProperties { search: (nodeData: INodeData, options?: ICommonObject) => Promise delete: (nodeData: INodeData, options?: ICommonObject) => Promise } + memoryMethods?: { + clearSessionMemory: (nodeData: INodeData, options?: ICommonObject) => Promise + getChatMessages: (nodeData: INodeData, options?: ICommonObject) => Promise + } init?(nodeData: INodeData, input: string, options?: ICommonObject): Promise run?(nodeData: INodeData, input: string, options?: ICommonObject): Promise - clearSessionMemory?(nodeData: INodeData, options?: ICommonObject): Promise } export interface INodeData extends INodeProperties { diff --git a/packages/components/src/utils.ts b/packages/components/src/utils.ts index 69f8b268e..404f7c75d 100644 --- a/packages/components/src/utils.ts +++ b/packages/components/src/utils.ts @@ -549,6 +549,18 @@ export const convertChatHistoryToText = (chatHistory: IMessage[] = []): string = .join('\n') } +/** + * Serialize array chat history to string + * @param {IMessage[]} chatHistory + * @returns {string} + */ +export const serializeChatHistory = (chatHistory: string | Array) => { + if (Array.isArray(chatHistory)) { + return chatHistory.join('\n') + } + return chatHistory +} + /** * Convert schema to zod schema * @param {string | object} schema diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 1ebd9312d..d87d2c0ac 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -42,7 +42,8 @@ import { getEncryptionKey, checkMemorySessionId, clearSessionMemoryFromViewMessageDialog, - getUserHome + getUserHome, + replaceChatHistory } from './utils' import { cloneDeep, omit, uniqWith, isEqual } from 'lodash' import { getDataSource } from './DataSource' @@ -54,7 +55,7 @@ import { Tool } from './database/entities/Tool' import { Assistant } from './database/entities/Assistant' import { ChatflowPool } from './ChatflowPool' import { CachePool } from './CachePool' -import { ICommonObject, INodeOptionsValue } from 'flowise-components' +import { ICommonObject, IMessage, INodeOptionsValue } from 'flowise-components' import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit' import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey' @@ -1267,14 +1268,14 @@ export class App { * @param {IReactFlowEdge[]} edges * @returns {string | undefined} */ - findMemoryLabel(nodes: IReactFlowNode[], edges: IReactFlowEdge[]): string | undefined { + findMemoryLabel(nodes: IReactFlowNode[], edges: IReactFlowEdge[]): IReactFlowNode | undefined { const memoryNodes = nodes.filter((node) => node.data.category === 'Memory') const memoryNodeIds = memoryNodes.map((mem) => mem.data.id) for (const edge of edges) { if (memoryNodeIds.includes(edge.source)) { const memoryNode = nodes.find((node) => node.data.id === edge.source) - return memoryNode ? memoryNode.data.label : undefined + return memoryNode } } return undefined @@ -1395,6 +1396,19 @@ export class App { isStreamValid = isFlowValidForStream(nodes, endingNodeData) + let chatHistory: IMessage[] | string = incomingInput.history + if ( + endingNodeData.inputs?.memory && + !incomingInput.history && + (incomingInput.chatId || incomingInput.overrideConfig?.sessionId) + ) { + const memoryNodeId = endingNodeData.inputs?.memory.split('.')[0].replace('{{', '') + const memoryNode = nodes.find((node) => node.data.id === memoryNodeId) + if (memoryNode) { + chatHistory = await replaceChatHistory(memoryNode, incomingInput, this.AppDataSource, databaseEntities, logger) + } + } + /*** Get Starting Nodes with Non-Directed Graph ***/ const constructedObj = constructGraphs(nodes, edges, true) const nonDirectedGraph = constructedObj.graph @@ -1409,7 +1423,7 @@ export class App { depthQueue, this.nodesPool.componentNodes, incomingInput.question, - incomingInput.history, + chatHistory, chatId, chatflowid, this.AppDataSource, @@ -1429,7 +1443,7 @@ export class App { nodeToExecute.data, reactFlowNodes, incomingInput.question, - incomingInput.history + chatHistory ) nodeToExecuteData = reactFlowNodeData @@ -1446,11 +1460,17 @@ export class App { let sessionId = undefined if (nodeToExecuteData.instance) sessionId = checkMemorySessionId(nodeToExecuteData.instance, chatId) - const memoryType = this.findMemoryLabel(nodes, edges) + const memoryNode = this.findMemoryLabel(nodes, edges) + const memoryType = memoryNode?.data.label + + let chatHistory: IMessage[] | string = incomingInput.history + if (memoryNode && !incomingInput.history && (incomingInput.chatId || incomingInput.overrideConfig?.sessionId)) { + chatHistory = await replaceChatHistory(memoryNode, incomingInput, this.AppDataSource, databaseEntities, logger) + } let result = isStreamValid ? await nodeInstance.run(nodeToExecuteData, incomingInput.question, { - chatHistory: incomingInput.history, + chatHistory, socketIO, socketIOClientId: incomingInput.socketIOClientId, logger, @@ -1460,7 +1480,7 @@ export class App { chatId }) : await nodeInstance.run(nodeToExecuteData, incomingInput.question, { - chatHistory: incomingInput.history, + chatHistory, logger, appDataSource: this.AppDataSource, databaseEntities, diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index 423d32afe..0b1e62d25 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -15,7 +15,8 @@ import { IOverrideConfig, IReactFlowEdge, IReactFlowNode, - IVariableDict + IVariableDict, + IncomingInput } from '../Interface' import { cloneDeep, get, isEqual } from 'lodash' import { @@ -216,7 +217,7 @@ export const buildLangchain = async ( depthQueue: IDepthQueue, componentNodes: IComponentNodes, question: string, - chatHistory: IMessage[], + chatHistory: IMessage[] | string, chatId: string, chatflowid: string, appDataSource: DataSource, @@ -347,8 +348,8 @@ export const clearAllSessionMemory = async ( node.data.inputs.sessionId = sessionId } - if (newNodeInstance.clearSessionMemory) { - await newNodeInstance?.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) + if (newNodeInstance.memoryMethods && newNodeInstance.memoryMethods.clearSessionMemory) { + await newNodeInstance.memoryMethods.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) } } } @@ -380,8 +381,8 @@ export const clearSessionMemoryFromViewMessageDialog = async ( if (sessionId && node.data.inputs) node.data.inputs.sessionId = sessionId - if (newNodeInstance.clearSessionMemory) { - await newNodeInstance?.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) + if (newNodeInstance.memoryMethods && newNodeInstance.memoryMethods.clearSessionMemory) { + await newNodeInstance.memoryMethods.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) return } } @@ -399,7 +400,7 @@ export const getVariableValue = ( paramValue: string, reactFlowNodes: IReactFlowNode[], question: string, - chatHistory: IMessage[], + chatHistory: IMessage[] | string, isAcceptVariable = false ) => { let returnVal = paramValue @@ -432,7 +433,10 @@ export const getVariableValue = ( } if (isAcceptVariable && variableFullPath === CHAT_HISTORY_VAR_PREFIX) { - variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters(convertChatHistoryToText(chatHistory), false) + variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters( + typeof chatHistory === 'string' ? chatHistory : convertChatHistoryToText(chatHistory), + false + ) } // Split by first occurrence of '.' to get just nodeId @@ -475,7 +479,7 @@ export const resolveVariables = ( reactFlowNodeData: INodeData, reactFlowNodes: IReactFlowNode[], question: string, - chatHistory: IMessage[] + chatHistory: IMessage[] | string ): INodeData => { let flowNodeData = cloneDeep(reactFlowNodeData) const types = 'inputs' @@ -873,3 +877,39 @@ export const checkMemorySessionId = (instance: any, chatId: string): string | un return instance.memory.chatHistory.sessionId return undefined } + +/** + * Replace chatHistory if incomingInput.history is empty and sessionId/chatId is provided + * @param {IReactFlowNode} memoryNode + * @param {IncomingInput} incomingInput + * @param {DataSource} appDataSource + * @param {IDatabaseEntity} databaseEntities + * @param {any} logger + * @returns {string} + */ +export const replaceChatHistory = async ( + memoryNode: IReactFlowNode, + incomingInput: IncomingInput, + appDataSource: DataSource, + databaseEntities: IDatabaseEntity, + logger: any +): Promise => { + const nodeInstanceFilePath = memoryNode.data.filePath as string + const nodeModule = await import(nodeInstanceFilePath) + const newNodeInstance = new nodeModule.nodeClass() + + if (incomingInput.overrideConfig?.sessionId && memoryNode.data.inputs) { + memoryNode.data.inputs.sessionId = incomingInput.overrideConfig.sessionId + } + + if (newNodeInstance.memoryMethods && newNodeInstance.memoryMethods.getChatMessages) { + return await newNodeInstance.memoryMethods.getChatMessages(memoryNode.data, { + chatId: incomingInput.chatId, + appDataSource, + databaseEntities, + logger + }) + } + + return '' +}