diff --git a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts index d8b3d7570..cddc81f86 100644 --- a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts +++ b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts @@ -190,6 +190,7 @@ const prepareAgent = async ( const systemMessage = nodeData.inputs?.systemMessage as string const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const outputParser = ChatConversationalAgent.getDefaultOutputParser({ llm: model, @@ -240,7 +241,7 @@ const prepareAgent = async ( [inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input, agent_scratchpad: async (i: { input: string; steps: AgentStep[] }) => await constructScratchPad(i.steps), [memoryKey]: async (_: { input: string; steps: AgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts b/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts index b7a5ae371..c6ffeccba 100644 --- a/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts +++ b/packages/components/nodes/agents/ConversationalRetrievalAgent/ConversationalRetrievalAgent.ts @@ -84,7 +84,7 @@ class ConversationalRetrievalAgent_Agents implements INode { } async init(nodeData: INodeData, input: string, options: ICommonObject): Promise { - return prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { @@ -102,7 +102,7 @@ class ConversationalRetrievalAgent_Agents implements INode { } } - const executor = prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -134,7 +134,7 @@ class ConversationalRetrievalAgent_Agents implements INode { } } -const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { +const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { const model = nodeData.inputs?.model as ChatOpenAI const memory = nodeData.inputs?.memory as FlowiseMemory const systemMessage = nodeData.inputs?.systemMessage as string @@ -143,6 +143,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId tools = flatten(tools) const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const prompt = ChatPromptTemplate.fromMessages([ ['ai', systemMessage ? systemMessage : defaultMessage], @@ -160,7 +161,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId [inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input, agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps), [memoryKey]: async (_: { input: string; steps: AgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/MistralAIToolAgent/MistralAIToolAgent.ts b/packages/components/nodes/agents/MistralAIToolAgent/MistralAIToolAgent.ts index 94f9200f0..4999d51a4 100644 --- a/packages/components/nodes/agents/MistralAIToolAgent/MistralAIToolAgent.ts +++ b/packages/components/nodes/agents/MistralAIToolAgent/MistralAIToolAgent.ts @@ -82,7 +82,7 @@ class MistralAIToolAgent_Agents implements INode { } async init(nodeData: INodeData, input: string, options: ICommonObject): Promise { - return prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { @@ -100,7 +100,7 @@ class MistralAIToolAgent_Agents implements INode { } } - const executor = prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -161,7 +161,7 @@ class MistralAIToolAgent_Agents implements INode { } } -const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { +const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { const model = nodeData.inputs?.model as ChatOpenAI const memory = nodeData.inputs?.memory as FlowiseMemory const maxIterations = nodeData.inputs?.maxIterations as string @@ -170,6 +170,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId tools = flatten(tools) const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const prompt = ChatPromptTemplate.fromMessages([ ['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`], @@ -187,7 +188,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId [inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input, agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps), [memoryKey]: async (_: { input: string; steps: AgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts b/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts index f3891e23b..437102bc4 100644 --- a/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts +++ b/packages/components/nodes/agents/OpenAIFunctionAgent/OpenAIFunctionAgent.ts @@ -81,7 +81,7 @@ class OpenAIFunctionAgent_Agents implements INode { } async init(nodeData: INodeData, input: string, options: ICommonObject): Promise { - return prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { @@ -99,7 +99,7 @@ class OpenAIFunctionAgent_Agents implements INode { } } - const executor = prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -160,7 +160,7 @@ class OpenAIFunctionAgent_Agents implements INode { } } -const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { +const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { const model = nodeData.inputs?.model as ChatOpenAI const maxIterations = nodeData.inputs?.maxIterations as string const memory = nodeData.inputs?.memory as FlowiseMemory @@ -169,6 +169,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId tools = flatten(tools) const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const prompt = ChatPromptTemplate.fromMessages([ ['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`], @@ -186,7 +187,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId [inputKey]: (i: { input: string; steps: AgentStep[] }) => i.input, agent_scratchpad: (i: { input: string; steps: AgentStep[] }) => formatAgentSteps(i.steps), [memoryKey]: async (_: { input: string; steps: AgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent.ts b/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent.ts index 97506fde0..a849bfe4b 100644 --- a/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent.ts +++ b/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent.ts @@ -82,7 +82,7 @@ class OpenAIToolAgent_Agents implements INode { } async init(nodeData: INodeData, input: string, options: ICommonObject): Promise { - return prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + return prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) } async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { @@ -100,7 +100,7 @@ class OpenAIToolAgent_Agents implements INode { } } - const executor = prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + const executor = prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -161,7 +161,7 @@ class OpenAIToolAgent_Agents implements INode { } } -const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { +const prepareAgent = (nodeData: INodeData, options: ICommonObject, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { const model = nodeData.inputs?.model as ChatOpenAI const maxIterations = nodeData.inputs?.maxIterations as string const memory = nodeData.inputs?.memory as FlowiseMemory @@ -170,6 +170,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId tools = flatten(tools) const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const prompt = ChatPromptTemplate.fromMessages([ ['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`], @@ -185,7 +186,7 @@ const prepareAgent = (nodeData: INodeData, flowObj: { sessionId?: string; chatId [inputKey]: (i: { input: string; steps: ToolsAgentStep[] }) => i.input, agent_scratchpad: (i: { input: string; steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(i.steps), [memoryKey]: async (_: { input: string; steps: ToolsAgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent_LlamaIndex.ts b/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent_LlamaIndex.ts index 7a33c6d82..d7ff9ddeb 100644 --- a/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent_LlamaIndex.ts +++ b/packages/components/nodes/agents/OpenAIToolAgent/OpenAIToolAgent_LlamaIndex.ts @@ -61,10 +61,12 @@ class OpenAIFunctionAgent_LlamaIndex_Agents implements INode { return null } - async run(nodeData: INodeData, input: string): Promise { + async run(nodeData: INodeData, input: string, options: ICommonObject): Promise { const memory = nodeData.inputs?.memory as FlowiseMemory const model = nodeData.inputs?.model as OpenAI const systemMessage = nodeData.inputs?.systemMessage as string + const prependMessages = options?.prependMessages + let tools = nodeData.inputs?.tools tools = flatten(tools) @@ -77,7 +79,7 @@ class OpenAIFunctionAgent_LlamaIndex_Agents implements INode { }) } - const msgs = (await memory.getChatMessages(this.sessionId, false)) as IMessage[] + const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[] for (const message of msgs) { if (message.type === 'apiMessage') { chatHistory.push({ diff --git a/packages/components/nodes/agents/ReActAgentChat/ReActAgentChat.ts b/packages/components/nodes/agents/ReActAgentChat/ReActAgentChat.ts index ee5629260..227f10706 100644 --- a/packages/components/nodes/agents/ReActAgentChat/ReActAgentChat.ts +++ b/packages/components/nodes/agents/ReActAgentChat/ReActAgentChat.ts @@ -80,6 +80,7 @@ class ReActAgentChat_Agents implements INode { const model = nodeData.inputs?.model as BaseChatModel let tools = nodeData.inputs?.tools as Tool[] const moderations = nodeData.inputs?.inputModeration as Moderation[] + const prependMessages = options?.prependMessages if (moderations && moderations.length > 0) { try { @@ -134,7 +135,7 @@ class ReActAgentChat_Agents implements INode { const callbacks = await additionalCallbacks(nodeData, options) - const chatHistory = ((await memory.getChatMessages(this.sessionId, false)) as IMessage[]) ?? [] + const chatHistory = ((await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]) ?? [] const chatHistoryString = chatHistory.map((hist) => hist.message).join('\\n') const result = await executor.invoke({ input, chat_history: chatHistoryString }, { callbacks }) diff --git a/packages/components/nodes/agents/ToolAgent/ToolAgent.ts b/packages/components/nodes/agents/ToolAgent/ToolAgent.ts index 6eeeb745d..66b3802c1 100644 --- a/packages/components/nodes/agents/ToolAgent/ToolAgent.ts +++ b/packages/components/nodes/agents/ToolAgent/ToolAgent.ts @@ -191,6 +191,7 @@ const prepareAgent = async ( tools = flatten(tools) const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' const inputKey = memory.inputKey ? memory.inputKey : 'input' + const prependMessages = options?.prependMessages const prompt = ChatPromptTemplate.fromMessages([ ['system', systemMessage], @@ -239,7 +240,7 @@ const prepareAgent = async ( [inputKey]: (i: { input: string; steps: ToolsAgentStep[] }) => i.input, agent_scratchpad: (i: { input: string; steps: ToolsAgentStep[] }) => formatToOpenAIToolMessages(i.steps), [memoryKey]: async (_: { input: string; steps: ToolsAgentStep[] }) => { - const messages = (await memory.getChatMessages(flowObj?.sessionId, true)) as BaseMessage[] + const messages = (await memory.getChatMessages(flowObj?.sessionId, true, prependMessages)) as BaseMessage[] return messages ?? [] } }, diff --git a/packages/components/nodes/agents/XMLAgent/XMLAgent.ts b/packages/components/nodes/agents/XMLAgent/XMLAgent.ts index b92d6fd10..af311fcfb 100644 --- a/packages/components/nodes/agents/XMLAgent/XMLAgent.ts +++ b/packages/components/nodes/agents/XMLAgent/XMLAgent.ts @@ -122,7 +122,7 @@ class XMLAgent_Agents implements INode { return formatResponse(e.message) } } - const executor = await prepareAgent(nodeData, { sessionId: this.sessionId, chatId: options.chatId, input }) + const executor = await prepareAgent(nodeData, options, { sessionId: this.sessionId, chatId: options.chatId, input }) const loggerHandler = new ConsoleCallbackHandler(options.logger) const callbacks = await additionalCallbacks(nodeData, options) @@ -183,7 +183,11 @@ class XMLAgent_Agents implements INode { } } -const prepareAgent = async (nodeData: INodeData, flowObj: { sessionId?: string; chatId?: string; input?: string }) => { +const prepareAgent = async ( + nodeData: INodeData, + options: ICommonObject, + flowObj: { sessionId?: string; chatId?: string; input?: string } +) => { const model = nodeData.inputs?.model as BaseChatModel const maxIterations = nodeData.inputs?.maxIterations as string const memory = nodeData.inputs?.memory as FlowiseMemory @@ -192,6 +196,7 @@ const prepareAgent = async (nodeData: INodeData, flowObj: { sessionId?: string; tools = flatten(tools) const inputKey = memory.inputKey ? memory.inputKey : 'input' const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history' + const prependMessages = options?.prependMessages let promptMessage = systemMessage ? systemMessage : defaultSystemMessage if (memory.memoryKey) promptMessage = promptMessage.replaceAll('{chat_history}', `{${memory.memoryKey}}`) @@ -210,7 +215,7 @@ const prepareAgent = async (nodeData: INodeData, flowObj: { sessionId?: string; const llmWithStop = model.bind({ stop: ['', ''] }) - const messages = (await memory.getChatMessages(flowObj.sessionId, false)) as IMessage[] + const messages = (await memory.getChatMessages(flowObj.sessionId, false, prependMessages)) as IMessage[] let chatHistoryMsgTxt = '' for (const message of messages) { if (message.type === 'apiMessage') { diff --git a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts index 672ede7b1..73dc9c68c 100644 --- a/packages/components/nodes/chains/ConversationChain/ConversationChain.ts +++ b/packages/components/nodes/chains/ConversationChain/ConversationChain.ts @@ -220,6 +220,7 @@ const prepareChain = async (nodeData: INodeData, options: ICommonObject, session let model = nodeData.inputs?.model as BaseChatModel const memory = nodeData.inputs?.memory as FlowiseMemory const memoryKey = memory.memoryKey ?? 'chat_history' + const prependMessages = options?.prependMessages let messageContent: MessageContentImageUrl[] = [] if (llmSupportsVision(model)) { @@ -252,7 +253,7 @@ const prepareChain = async (nodeData: INodeData, options: ICommonObject, session { [inputKey]: (input: { input: string }) => input.input, [memoryKey]: async () => { - const history = await memory.getChatMessages(sessionId, true) + const history = await memory.getChatMessages(sessionId, true, prependMessages) return history }, ...promptVariables diff --git a/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts b/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts index bb68bc476..5a0e40685 100644 --- a/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts +++ b/packages/components/nodes/chains/ConversationalRetrievalQAChain/ConversationalRetrievalQAChain.ts @@ -175,6 +175,7 @@ class ConversationalRetrievalQAChain_Chains implements INode { const rephrasePrompt = nodeData.inputs?.rephrasePrompt as string const responsePrompt = nodeData.inputs?.responsePrompt as string const returnSourceDocuments = nodeData.inputs?.returnSourceDocuments as boolean + const prependMessages = options?.prependMessages const appDataSource = options.appDataSource as DataSource const databaseEntities = options.databaseEntities as IDatabaseEntity @@ -210,7 +211,7 @@ class ConversationalRetrievalQAChain_Chains implements INode { } const answerChain = createChain(model, vectorStoreRetriever, rephrasePrompt, customResponsePrompt) - const history = ((await memory.getChatMessages(this.sessionId, false)) as IMessage[]) ?? [] + const history = ((await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[]) ?? [] const loggerHandler = new ConsoleCallbackHandler(options.logger) const additionalCallback = await additionalCallbacks(nodeData, options) @@ -401,7 +402,11 @@ class BufferMemory extends FlowiseMemory implements MemoryMethods { this.chatflowid = fields.chatflowid } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { if (!overrideSessionId) return [] const chatMessage = await this.appDataSource.getRepository(this.databaseEntities['ChatMessage']).find({ @@ -414,6 +419,10 @@ class BufferMemory extends FlowiseMemory implements MemoryMethods { } }) + if (prependMessages?.length) { + chatMessage.unshift(...prependMessages) + } + if (returnBaseMessages) { return mapChatMessageToBaseMessage(chatMessage) } diff --git a/packages/components/nodes/engine/ChatEngine/ContextChatEngine.ts b/packages/components/nodes/engine/ChatEngine/ContextChatEngine.ts index f7e3ff4fc..ac944d441 100644 --- a/packages/components/nodes/engine/ChatEngine/ContextChatEngine.ts +++ b/packages/components/nodes/engine/ChatEngine/ContextChatEngine.ts @@ -71,6 +71,7 @@ class ContextChatEngine_LlamaIndex implements INode { const systemMessagePrompt = nodeData.inputs?.systemMessagePrompt as string const memory = nodeData.inputs?.memory as FlowiseMemory const returnSourceDocuments = nodeData.inputs?.returnSourceDocuments as boolean + const prependMessages = options?.prependMessages const chatHistory = [] as ChatMessage[] @@ -83,7 +84,7 @@ class ContextChatEngine_LlamaIndex implements INode { const chatEngine = new ContextChatEngine({ chatModel: model, retriever: vectorStoreRetriever }) - const msgs = (await memory.getChatMessages(this.sessionId, false)) as IMessage[] + const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[] for (const message of msgs) { if (message.type === 'apiMessage') { chatHistory.push({ diff --git a/packages/components/nodes/engine/ChatEngine/SimpleChatEngine.ts b/packages/components/nodes/engine/ChatEngine/SimpleChatEngine.ts index 221c60ada..5734288d1 100644 --- a/packages/components/nodes/engine/ChatEngine/SimpleChatEngine.ts +++ b/packages/components/nodes/engine/ChatEngine/SimpleChatEngine.ts @@ -56,6 +56,7 @@ class SimpleChatEngine_LlamaIndex implements INode { const model = nodeData.inputs?.model as LLM const systemMessagePrompt = nodeData.inputs?.systemMessagePrompt as string const memory = nodeData.inputs?.memory as FlowiseMemory + const prependMessages = options?.prependMessages const chatHistory = [] as ChatMessage[] @@ -68,7 +69,7 @@ class SimpleChatEngine_LlamaIndex implements INode { const chatEngine = new SimpleChatEngine({ llm: model }) - const msgs = (await memory.getChatMessages(this.sessionId, false)) as IMessage[] + const msgs = (await memory.getChatMessages(this.sessionId, false, prependMessages)) as IMessage[] for (const message of msgs) { if (message.type === 'apiMessage') { chatHistory.push({ diff --git a/packages/components/nodes/memory/BufferMemory/BufferMemory.ts b/packages/components/nodes/memory/BufferMemory/BufferMemory.ts index 80bf7f963..d9557febe 100644 --- a/packages/components/nodes/memory/BufferMemory/BufferMemory.ts +++ b/packages/components/nodes/memory/BufferMemory/BufferMemory.ts @@ -94,7 +94,11 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { this.chatflowid = fields.chatflowid } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId if (!id) return [] @@ -108,6 +112,10 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { } }) + if (prependMessages?.length) { + chatMessage.unshift(...prependMessages) + } + if (returnBaseMessages) { return mapChatMessageToBaseMessage(chatMessage) } diff --git a/packages/components/nodes/memory/BufferWindowMemory/BufferWindowMemory.ts b/packages/components/nodes/memory/BufferWindowMemory/BufferWindowMemory.ts index 66d5549c8..422cac55f 100644 --- a/packages/components/nodes/memory/BufferWindowMemory/BufferWindowMemory.ts +++ b/packages/components/nodes/memory/BufferWindowMemory/BufferWindowMemory.ts @@ -105,7 +105,11 @@ class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMe this.chatflowid = fields.chatflowid } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId if (!id) return [] @@ -123,6 +127,10 @@ class BufferWindowMemoryExtended extends FlowiseWindowMemory implements MemoryMe // reverse the order of human and ai messages if (chatMessage.length) chatMessage.reverse() + if (prependMessages?.length) { + chatMessage.unshift(...prependMessages) + } + if (returnBaseMessages) { return mapChatMessageToBaseMessage(chatMessage) } diff --git a/packages/components/nodes/memory/ConversationSummaryBufferMemory/ConversationSummaryBufferMemory.ts b/packages/components/nodes/memory/ConversationSummaryBufferMemory/ConversationSummaryBufferMemory.ts index c57560b73..5f58b0ca8 100644 --- a/packages/components/nodes/memory/ConversationSummaryBufferMemory/ConversationSummaryBufferMemory.ts +++ b/packages/components/nodes/memory/ConversationSummaryBufferMemory/ConversationSummaryBufferMemory.ts @@ -114,7 +114,11 @@ class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory this.chatflowid = fields.chatflowid } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId if (!id) return [] @@ -128,6 +132,10 @@ class ConversationSummaryBufferMemoryExtended extends FlowiseSummaryBufferMemory } }) + if (prependMessages?.length) { + chatMessage.unshift(...prependMessages) + } + let baseMessages = mapChatMessageToBaseMessage(chatMessage) // Prune baseMessages if it exceeds max token limit diff --git a/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts b/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts index 29614da5c..bd1c5617d 100644 --- a/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts +++ b/packages/components/nodes/memory/ConversationSummaryMemory/ConversationSummaryMemory.ts @@ -104,7 +104,11 @@ class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements this.chatflowid = fields.chatflowid } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId if (!id) return [] @@ -119,6 +123,10 @@ class ConversationSummaryMemoryExtended extends FlowiseSummaryMemory implements } }) + if (prependMessages?.length) { + chatMessage.unshift(...prependMessages) + } + const baseMessages = mapChatMessageToBaseMessage(chatMessage) // Get summary diff --git a/packages/components/nodes/memory/DynamoDb/DynamoDb.ts b/packages/components/nodes/memory/DynamoDb/DynamoDb.ts index 778357090..2c64d42e6 100644 --- a/packages/components/nodes/memory/DynamoDb/DynamoDb.ts +++ b/packages/components/nodes/memory/DynamoDb/DynamoDb.ts @@ -12,7 +12,13 @@ import { import { DynamoDBChatMessageHistory } from '@langchain/community/stores/message/dynamodb' import { mapStoredMessageToChatMessage, AIMessage, HumanMessage, StoredMessage, BaseMessage } from '@langchain/core/messages' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface' class DynamoDb_Memory implements INode { @@ -219,7 +225,11 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { await client.send(new UpdateItemCommand(params)) } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { if (!this.dynamodbClient) return [] const dynamoKey = overrideSessionId ? this.overrideDynamoKey(overrideSessionId) : this.dynamoKey @@ -243,6 +253,9 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { })) .filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined) const baseMessages = messages.map(mapStoredMessageToChatMessage) + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts b/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts index cf56e1f9c..f4351aaf8 100644 --- a/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts +++ b/packages/components/nodes/memory/MongoDBMemory/MongoDBMemory.ts @@ -2,7 +2,13 @@ import { MongoClient, Collection, Document } from 'mongodb' import { MongoDBChatMessageHistory } from '@langchain/mongodb' import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { mapStoredMessageToChatMessage, AIMessage, HumanMessage, BaseMessage } from '@langchain/core/messages' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' import { FlowiseMemory, ICommonObject, IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface' let mongoClientSingleton: MongoClient @@ -151,13 +157,20 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { this.collection = fields.collection } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { if (!this.collection) return [] const id = overrideSessionId ? overrideSessionId : this.sessionId const document = await this.collection.findOne({ sessionId: id }) const messages = document?.messages || [] const baseMessages = messages.map(mapStoredMessageToChatMessage) + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index b9f27a909..9900b8cd0 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -4,7 +4,13 @@ import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from '@langchain/community/stores/message/ioredis' import { mapStoredMessageToChatMessage, BaseMessage, AIMessage, HumanMessage } from '@langchain/core/messages' import { INode, INodeData, INodeParams, ICommonObject, MessageType, IMessage, MemoryMethods, FlowiseMemory } from '../../../src/Interface' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' let redisClientSingleton: Redis let redisClientOption: RedisOptions @@ -190,13 +196,20 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { this.sessionTTL = fields.sessionTTL } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { if (!this.redisClient) return [] const id = overrideSessionId ? overrideSessionId : this.sessionId const rawStoredMessages = await this.redisClient.lrange(id, this.windowSize ? this.windowSize * -1 : 0, -1) const orderedMessages = rawStoredMessages.reverse().map((message) => JSON.parse(message)) const baseMessages = orderedMessages.map(mapStoredMessageToChatMessage) + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts index a2201790c..7eb9b3906 100644 --- a/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/UpstashRedisBackedChatMemory/UpstashRedisBackedChatMemory.ts @@ -4,7 +4,13 @@ import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { UpstashRedisChatMessageHistory } from '@langchain/community/stores/message/upstash_redis' import { mapStoredMessageToChatMessage, AIMessage, HumanMessage, StoredMessage, BaseMessage } from '@langchain/core/messages' import { FlowiseMemory, IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' import { ICommonObject } from '../../../src/Interface' let redisClientSingleton: Redis @@ -143,7 +149,11 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { this.sessionTTL = fields.sessionTTL } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { if (!this.redisClient) return [] const id = overrideSessionId ? overrideSessionId : this.sessionId @@ -151,6 +161,9 @@ class BufferMemoryExtended extends FlowiseMemory implements MemoryMethods { const orderedMessages = rawStoredMessages.reverse() const previousMessages = orderedMessages.filter((x): x is StoredMessage => x.type !== undefined && x.data.content !== undefined) const baseMessages = previousMessages.map(mapStoredMessageToChatMessage) + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts index c8d3d1552..3f654146d 100644 --- a/packages/components/nodes/memory/ZepMemory/ZepMemory.ts +++ b/packages/components/nodes/memory/ZepMemory/ZepMemory.ts @@ -2,7 +2,13 @@ import { ZepMemory, ZepMemoryInput } from '@langchain/community/memory/zep' import { BaseMessage } from '@langchain/core/messages' import { InputValues, MemoryVariables, OutputValues } from 'langchain/memory' import { IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType, ICommonObject } from '../../../src/Interface' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' class ZepMemory_Memory implements INode { label: string @@ -161,10 +167,17 @@ class ZepMemoryExtended extends ZepMemory implements MemoryMethods { return super.clear() } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId const memoryVariables = await this.loadMemoryVariables({}, id) const baseMessages = memoryVariables[this.memoryKey] + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/nodes/memory/ZepMemoryCloud/ZepMemoryCloud.ts b/packages/components/nodes/memory/ZepMemoryCloud/ZepMemoryCloud.ts index d5c950f4b..6be233bbf 100644 --- a/packages/components/nodes/memory/ZepMemoryCloud/ZepMemoryCloud.ts +++ b/packages/components/nodes/memory/ZepMemoryCloud/ZepMemoryCloud.ts @@ -1,5 +1,11 @@ import { IMessage, INode, INodeData, INodeParams, MemoryMethods, MessageType } from '../../../src/Interface' -import { convertBaseMessagetoIMessage, getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' +import { + convertBaseMessagetoIMessage, + getBaseClasses, + getCredentialData, + getCredentialParam, + mapChatMessageToBaseMessage +} from '../../../src/utils' import { ZepMemory, ZepMemoryInput } from '@getzep/zep-cloud/langchain' import { ICommonObject } from '../../../src' @@ -155,10 +161,17 @@ class ZepMemoryExtended extends ZepMemory implements MemoryMethods { return super.clear() } - async getChatMessages(overrideSessionId = '', returnBaseMessages = false): Promise { + async getChatMessages( + overrideSessionId = '', + returnBaseMessages = false, + prependMessages?: IMessage[] + ): Promise { const id = overrideSessionId ? overrideSessionId : this.sessionId const memoryVariables = await this.loadMemoryVariables({}, id) const baseMessages = memoryVariables[this.memoryKey] + if (prependMessages?.length) { + baseMessages.unshift(...mapChatMessageToBaseMessage(prependMessages)) + } return returnBaseMessages ? baseMessages : convertBaseMessagetoIMessage(baseMessages) } diff --git a/packages/components/src/Interface.ts b/packages/components/src/Interface.ts index b2a7d2102..7e6e7ebd2 100644 --- a/packages/components/src/Interface.ts +++ b/packages/components/src/Interface.ts @@ -243,31 +243,51 @@ import { BaseMessage } from '@langchain/core/messages' import { BufferMemory, BufferWindowMemory, ConversationSummaryMemory, ConversationSummaryBufferMemory } from 'langchain/memory' export interface MemoryMethods { - getChatMessages(overrideSessionId?: string, returnBaseMessages?: boolean): Promise + getChatMessages( + overrideSessionId?: string, + returnBaseMessages?: boolean, + prependMessages?: IMessage[] + ): Promise addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId?: string): Promise clearChatMessages(overrideSessionId?: string): Promise } export abstract class FlowiseMemory extends BufferMemory implements MemoryMethods { - abstract getChatMessages(overrideSessionId?: string, returnBaseMessages?: boolean): Promise + abstract getChatMessages( + overrideSessionId?: string, + returnBaseMessages?: boolean, + prependMessages?: IMessage[] + ): Promise abstract addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId?: string): Promise abstract clearChatMessages(overrideSessionId?: string): Promise } export abstract class FlowiseWindowMemory extends BufferWindowMemory implements MemoryMethods { - abstract getChatMessages(overrideSessionId?: string, returnBaseMessages?: boolean): Promise + abstract getChatMessages( + overrideSessionId?: string, + returnBaseMessages?: boolean, + prependMessages?: IMessage[] + ): Promise abstract addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId?: string): Promise abstract clearChatMessages(overrideSessionId?: string): Promise } export abstract class FlowiseSummaryMemory extends ConversationSummaryMemory implements MemoryMethods { - abstract getChatMessages(overrideSessionId?: string, returnBaseMessages?: boolean): Promise + abstract getChatMessages( + overrideSessionId?: string, + returnBaseMessages?: boolean, + prependMessages?: IMessage[] + ): Promise abstract addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId?: string): Promise abstract clearChatMessages(overrideSessionId?: string): Promise } export abstract class FlowiseSummaryBufferMemory extends ConversationSummaryBufferMemory implements MemoryMethods { - abstract getChatMessages(overrideSessionId?: string, returnBaseMessages?: boolean): Promise + abstract getChatMessages( + overrideSessionId?: string, + returnBaseMessages?: boolean, + prependMessages?: IMessage[] + ): Promise abstract addChatMessages(msgArray: { text: string; type: MessageType }[], overrideSessionId?: string): Promise abstract clearChatMessages(overrideSessionId?: string): Promise } diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index 7c3b265fd..479d4c3be 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -212,6 +212,7 @@ export interface IncomingInput { stopNodeId?: string uploads?: IFileUpload[] leadEmail?: string + history?: IMessage[] } export interface IActiveChatflows { diff --git a/packages/server/src/utils/buildChatflow.ts b/packages/server/src/utils/buildChatflow.ts index ae452f2c1..03035e063 100644 --- a/packages/server/src/utils/buildChatflow.ts +++ b/packages/server/src/utils/buildChatflow.ts @@ -145,6 +145,9 @@ export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInter const memoryType = memoryNode?.data.label let sessionId = getMemorySessionId(memoryNode, incomingInput, chatId, isInternal) + // Get prepend messages + const prependMessages = incomingInput.history + /* Reuse the flow without having to rebuild (to avoid duplicated upsert, recomputation, reinitialization of memory) when all these conditions met: * - Node Data already exists in pool * - Still in sync (i.e the flow has not been modified since) @@ -226,7 +229,8 @@ export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInter appServer.nodesPool.componentNodes, appServer.AppDataSource, databaseEntities, - logger + logger, + prependMessages ) } @@ -301,7 +305,8 @@ export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInter analytic: chatflow.analytic, uploads: incomingInput.uploads, socketIO, - socketIOClientId: incomingInput.socketIOClientId + socketIOClientId: incomingInput.socketIOClientId, + prependMessages }) : await nodeInstance.run(nodeToExecuteData, incomingInput.question, { chatId, @@ -310,7 +315,8 @@ export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInter appDataSource: appServer.AppDataSource, databaseEntities, analytic: chatflow.analytic, - uploads: incomingInput.uploads + uploads: incomingInput.uploads, + prependMessages }) result = typeof result === 'string' ? { text: result } : result diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index aa0c03e8b..5da29ba87 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -1306,7 +1306,8 @@ export const getSessionChatHistory = async ( componentNodes: IComponentNodes, appDataSource: DataSource, databaseEntities: IDatabaseEntity, - logger: any + logger: any, + prependMessages?: IMessage[] ): Promise => { const nodeInstanceFilePath = componentNodes[memoryNode.data.name].filePath as string const nodeModule = await import(nodeInstanceFilePath) @@ -1324,7 +1325,7 @@ export const getSessionChatHistory = async ( logger }) - return (await initializedInstance.getChatMessages(sessionId)) as IMessage[] + return (await initializedInstance.getChatMessages(sessionId, undefined, prependMessages)) as IMessage[] } /**