fix chat history

This commit is contained in:
Henry 2023-11-30 16:01:16 +00:00
parent 5a5097e997
commit 4b5f7028e3
12 changed files with 262 additions and 98 deletions

View File

@ -85,43 +85,46 @@ class OpenAIAssistant_Agents implements INode {
return null return null
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const selectedAssistantId = nodeData.inputs?.selectedAssistant as string memoryMethods = {
const appDataSource = options.appDataSource as DataSource async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const databaseEntities = options.databaseEntities as IDatabaseEntity const selectedAssistantId = nodeData.inputs?.selectedAssistant as string
let sessionId = nodeData.inputs?.sessionId 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({ const assistant = await appDataSource.getRepository(databaseEntities['Assistant']).findOneBy({
id: selectedAssistantId 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
}) })
if (!chatmsg) {
options.logger.error(`Chat Message with Chat Id: ${options.chatId} not found`) if (!assistant) {
options.logger.error(`Assistant ${selectedAssistantId} not found`)
return return
} }
sessionId = chatmsg.sessionId
}
const credentialData = await getCredentialData(assistant.credential ?? '', options) if (!sessionId && options.chatId) {
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData) const chatmsg = await appDataSource.getRepository(databaseEntities['ChatMessage']).findOneBy({
if (!openAIApiKey) { chatId: options.chatId
options.logger.error(`OpenAI ApiKey not found`) })
return if (!chatmsg) {
} options.logger.error(`Chat Message with Chat Id: ${options.chatId} not found`)
return
}
sessionId = chatmsg.sessionId
}
const openai = new OpenAI({ apiKey: openAIApiKey }) const credentialData = await getCredentialData(assistant.credential ?? '', options)
options.logger.info(`Clearing OpenAI Thread ${sessionId}`) const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
if (sessionId) await openai.beta.threads.del(sessionId) if (!openAIApiKey) {
options.logger.info(`Successfully cleared OpenAI Thread ${sessionId}`) 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<string | object> { async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string | object> {
@ -359,7 +362,10 @@ class OpenAIAssistant_Agents implements INode {
} }
// Replace the text with a footnote // 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 returnVal += message_content.value

View File

@ -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 { DynamoDBChatMessageHistory } from 'langchain/stores/message/dynamodb'
import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { BufferMemory, BufferMemoryInput } from 'langchain/memory'
@ -70,13 +79,23 @@ class DynamoDb_Memory implements INode {
return initalizeDynamoDB(nodeData, options) return initalizeDynamoDB(nodeData, options)
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const dynamodbMemory = await initalizeDynamoDB(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const dynamodbMemory = await initalizeDynamoDB(nodeData, options)
options.logger.info(`Clearing DynamoDb memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await dynamodbMemory.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared DynamoDb memory session ${sessionId ? sessionId : chatId}`) 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<string> {
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])
}
} }
} }

View File

@ -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 { MongoDBChatMessageHistory } from 'langchain/stores/message/mongodb'
import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { BufferMemory, BufferMemoryInput } from 'langchain/memory'
import { BaseMessage, mapStoredMessageToChatMessage } from 'langchain/schema' import { BaseMessage, mapStoredMessageToChatMessage } from 'langchain/schema'
@ -67,13 +76,23 @@ class MongoDB_Memory implements INode {
return initializeMongoDB(nodeData, options) return initializeMongoDB(nodeData, options)
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const mongodbMemory = await initializeMongoDB(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const mongodbMemory = await initializeMongoDB(nodeData, options)
options.logger.info(`Clearing MongoDB memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await mongodbMemory.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared MongoDB memory session ${sessionId ? sessionId : chatId}`) 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<string> {
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])
}
} }
} }

View File

@ -3,6 +3,7 @@ import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../
import { ICommonObject } from '../../../src' import { ICommonObject } from '../../../src'
import { MotorheadMemory, MotorheadMemoryInput } from 'langchain/memory' import { MotorheadMemory, MotorheadMemoryInput } from 'langchain/memory'
import fetch from 'node-fetch' import fetch from 'node-fetch'
import { getBufferString } from 'langchain/memory'
class MotorMemory_Memory implements INode { class MotorMemory_Memory implements INode {
label: string label: string
@ -64,13 +65,23 @@ class MotorMemory_Memory implements INode {
return initalizeMotorhead(nodeData, options) return initalizeMotorhead(nodeData, options)
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const motorhead = await initalizeMotorhead(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const motorhead = await initalizeMotorhead(nodeData, options)
options.logger.info(`Clearing Motorhead memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await motorhead.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared Motorhead memory session ${sessionId ? sessionId : chatId}`) 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<string> {
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])
}
} }
} }

View File

@ -1,5 +1,5 @@
import { INode, INodeData, INodeParams, ICommonObject } from '../../../src/Interface' 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 { BufferMemory, BufferMemoryInput } from 'langchain/memory'
import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/ioredis' import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/ioredis'
import { mapStoredMessageToChatMessage, BaseMessage } from 'langchain/schema' import { mapStoredMessageToChatMessage, BaseMessage } from 'langchain/schema'
@ -65,13 +65,23 @@ class RedisBackedChatMemory_Memory implements INode {
return await initalizeRedis(nodeData, options) return await initalizeRedis(nodeData, options)
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const redis = await initalizeRedis(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const redis = await initalizeRedis(nodeData, options)
options.logger.info(`Clearing Redis memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await redis.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared Redis memory session ${sessionId ? sessionId : chatId}`) 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<string> {
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])
}
} }
} }

View File

@ -1,5 +1,5 @@
import { INode, INodeData, INodeParams } from '../../../src/Interface' 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 { ICommonObject } from '../../../src'
import { BufferMemory, BufferMemoryInput } from 'langchain/memory' import { BufferMemory, BufferMemoryInput } from 'langchain/memory'
import { UpstashRedisChatMessageHistory } from 'langchain/stores/message/upstash_redis' import { UpstashRedisChatMessageHistory } from 'langchain/stores/message/upstash_redis'
@ -63,13 +63,22 @@ class UpstashRedisBackedChatMemory_Memory implements INode {
return initalizeUpstashRedis(nodeData, options) return initalizeUpstashRedis(nodeData, options)
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const redis = await initalizeUpstashRedis(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const redis = await initalizeUpstashRedis(nodeData, options)
options.logger.info(`Clearing Upstash Redis memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await redis.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared Upstash Redis memory session ${sessionId ? sessionId : chatId}`) 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<string> {
const redis = await initalizeUpstashRedis(nodeData, options)
const key = 'chat_history'
const memoryResult = await redis.loadMemoryVariables({})
return serializeChatHistory(memoryResult[key])
}
} }
} }

View File

@ -3,6 +3,7 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ZepMemory, ZepMemoryInput } from 'langchain/memory/zep' import { ZepMemory, ZepMemoryInput } from 'langchain/memory/zep'
import { ICommonObject } from '../../../src' import { ICommonObject } from '../../../src'
import { getBufferString } from 'langchain/memory'
class ZepMemory_Memory implements INode { class ZepMemory_Memory implements INode {
label: string label: string
@ -140,13 +141,25 @@ class ZepMemory_Memory implements INode {
return zep return zep
} }
async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> { //@ts-ignore
const zep = await initalizeZep(nodeData, options) memoryMethods = {
const sessionId = nodeData.inputs?.sessionId as string async clearSessionMemory(nodeData: INodeData, options: ICommonObject): Promise<void> {
const chatId = options?.chatId as string const zep = await initalizeZep(nodeData, options)
options.logger.info(`Clearing Zep memory session ${sessionId ? sessionId : chatId}`) const sessionId = nodeData.inputs?.sessionId as string
await zep.clear() const chatId = options?.chatId as string
options.logger.info(`Successfully cleared Zep memory session ${sessionId ? sessionId : chatId}`) 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<string> {
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)
}
} }
} }

View File

@ -1,5 +1,6 @@
import { DynamicTool, DynamicToolInput } from 'langchain/tools' import { DynamicTool, DynamicToolInput } from 'langchain/tools'
import { BaseChain } from 'langchain/chains' import { BaseChain } from 'langchain/chains'
import { handleEscapeCharacters } from '../../../src/utils'
export interface ChainToolInput extends Omit<DynamicToolInput, 'func'> { export interface ChainToolInput extends Omit<DynamicToolInput, 'func'> {
chain: BaseChain chain: BaseChain
@ -14,7 +15,8 @@ export class ChainTool extends DynamicTool {
func: async (input, runManager) => { func: async (input, runManager) => {
// To enable LLM Chain which has promptValues // To enable LLM Chain which has promptValues
if ((chain as any).prompt && (chain as any).prompt.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 values?.text
} }
return chain.run(input, runManager?.getChild()) return chain.run(input, runManager?.getChild())

View File

@ -107,9 +107,12 @@ export interface INode extends INodeProperties {
search: (nodeData: INodeData, options?: ICommonObject) => Promise<any> search: (nodeData: INodeData, options?: ICommonObject) => Promise<any>
delete: (nodeData: INodeData, options?: ICommonObject) => Promise<void> delete: (nodeData: INodeData, options?: ICommonObject) => Promise<void>
} }
memoryMethods?: {
clearSessionMemory: (nodeData: INodeData, options?: ICommonObject) => Promise<void>
getChatMessages: (nodeData: INodeData, options?: ICommonObject) => Promise<string>
}
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

@ -549,6 +549,18 @@ export const convertChatHistoryToText = (chatHistory: IMessage[] = []): string =
.join('\n') .join('\n')
} }
/**
* Serialize array chat history to string
* @param {IMessage[]} chatHistory
* @returns {string}
*/
export const serializeChatHistory = (chatHistory: string | Array<string>) => {
if (Array.isArray(chatHistory)) {
return chatHistory.join('\n')
}
return chatHistory
}
/** /**
* Convert schema to zod schema * Convert schema to zod schema
* @param {string | object} schema * @param {string | object} schema

View File

@ -42,7 +42,8 @@ import {
getEncryptionKey, getEncryptionKey,
checkMemorySessionId, checkMemorySessionId,
clearSessionMemoryFromViewMessageDialog, clearSessionMemoryFromViewMessageDialog,
getUserHome getUserHome,
replaceChatHistory
} from './utils' } from './utils'
import { cloneDeep, omit, uniqWith, isEqual } from 'lodash' import { cloneDeep, omit, uniqWith, isEqual } from 'lodash'
import { getDataSource } from './DataSource' import { getDataSource } from './DataSource'
@ -54,7 +55,7 @@ import { Tool } from './database/entities/Tool'
import { Assistant } from './database/entities/Assistant' import { Assistant } from './database/entities/Assistant'
import { ChatflowPool } from './ChatflowPool' import { ChatflowPool } from './ChatflowPool'
import { CachePool } from './CachePool' 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 { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey' import { addAPIKey, compareKeys, deleteAPIKey, getApiKey, getAPIKeys, updateAPIKey } from './utils/apiKey'
@ -1267,14 +1268,14 @@ export class App {
* @param {IReactFlowEdge[]} edges * @param {IReactFlowEdge[]} edges
* @returns {string | undefined} * @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 memoryNodes = nodes.filter((node) => node.data.category === 'Memory')
const memoryNodeIds = memoryNodes.map((mem) => mem.data.id) const memoryNodeIds = memoryNodes.map((mem) => mem.data.id)
for (const edge of edges) { for (const edge of edges) {
if (memoryNodeIds.includes(edge.source)) { if (memoryNodeIds.includes(edge.source)) {
const memoryNode = nodes.find((node) => node.data.id === edge.source) const memoryNode = nodes.find((node) => node.data.id === edge.source)
return memoryNode ? memoryNode.data.label : undefined return memoryNode
} }
} }
return undefined return undefined
@ -1395,6 +1396,19 @@ export class App {
isStreamValid = isFlowValidForStream(nodes, endingNodeData) 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 ***/ /*** Get Starting Nodes with Non-Directed Graph ***/
const constructedObj = constructGraphs(nodes, edges, true) const constructedObj = constructGraphs(nodes, edges, true)
const nonDirectedGraph = constructedObj.graph const nonDirectedGraph = constructedObj.graph
@ -1409,7 +1423,7 @@ export class App {
depthQueue, depthQueue,
this.nodesPool.componentNodes, this.nodesPool.componentNodes,
incomingInput.question, incomingInput.question,
incomingInput.history, chatHistory,
chatId, chatId,
chatflowid, chatflowid,
this.AppDataSource, this.AppDataSource,
@ -1429,7 +1443,7 @@ export class App {
nodeToExecute.data, nodeToExecute.data,
reactFlowNodes, reactFlowNodes,
incomingInput.question, incomingInput.question,
incomingInput.history chatHistory
) )
nodeToExecuteData = reactFlowNodeData nodeToExecuteData = reactFlowNodeData
@ -1446,11 +1460,17 @@ export class App {
let sessionId = undefined let sessionId = undefined
if (nodeToExecuteData.instance) sessionId = checkMemorySessionId(nodeToExecuteData.instance, chatId) 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 let result = isStreamValid
? await nodeInstance.run(nodeToExecuteData, incomingInput.question, { ? await nodeInstance.run(nodeToExecuteData, incomingInput.question, {
chatHistory: incomingInput.history, chatHistory,
socketIO, socketIO,
socketIOClientId: incomingInput.socketIOClientId, socketIOClientId: incomingInput.socketIOClientId,
logger, logger,
@ -1460,7 +1480,7 @@ export class App {
chatId chatId
}) })
: await nodeInstance.run(nodeToExecuteData, incomingInput.question, { : await nodeInstance.run(nodeToExecuteData, incomingInput.question, {
chatHistory: incomingInput.history, chatHistory,
logger, logger,
appDataSource: this.AppDataSource, appDataSource: this.AppDataSource,
databaseEntities, databaseEntities,

View File

@ -15,7 +15,8 @@ import {
IOverrideConfig, IOverrideConfig,
IReactFlowEdge, IReactFlowEdge,
IReactFlowNode, IReactFlowNode,
IVariableDict IVariableDict,
IncomingInput
} from '../Interface' } from '../Interface'
import { cloneDeep, get, isEqual } from 'lodash' import { cloneDeep, get, isEqual } from 'lodash'
import { import {
@ -216,7 +217,7 @@ export const buildLangchain = async (
depthQueue: IDepthQueue, depthQueue: IDepthQueue,
componentNodes: IComponentNodes, componentNodes: IComponentNodes,
question: string, question: string,
chatHistory: IMessage[], chatHistory: IMessage[] | string,
chatId: string, chatId: string,
chatflowid: string, chatflowid: string,
appDataSource: DataSource, appDataSource: DataSource,
@ -347,8 +348,8 @@ export const clearAllSessionMemory = async (
node.data.inputs.sessionId = sessionId node.data.inputs.sessionId = sessionId
} }
if (newNodeInstance.clearSessionMemory) { if (newNodeInstance.memoryMethods && newNodeInstance.memoryMethods.clearSessionMemory) {
await newNodeInstance?.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) 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 (sessionId && node.data.inputs) node.data.inputs.sessionId = sessionId
if (newNodeInstance.clearSessionMemory) { if (newNodeInstance.memoryMethods && newNodeInstance.memoryMethods.clearSessionMemory) {
await newNodeInstance?.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger }) await newNodeInstance.memoryMethods.clearSessionMemory(node.data, { chatId, appDataSource, databaseEntities, logger })
return return
} }
} }
@ -399,7 +400,7 @@ export const getVariableValue = (
paramValue: string, paramValue: string,
reactFlowNodes: IReactFlowNode[], reactFlowNodes: IReactFlowNode[],
question: string, question: string,
chatHistory: IMessage[], chatHistory: IMessage[] | string,
isAcceptVariable = false isAcceptVariable = false
) => { ) => {
let returnVal = paramValue let returnVal = paramValue
@ -432,7 +433,10 @@ export const getVariableValue = (
} }
if (isAcceptVariable && variableFullPath === CHAT_HISTORY_VAR_PREFIX) { 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 // Split by first occurrence of '.' to get just nodeId
@ -475,7 +479,7 @@ export const resolveVariables = (
reactFlowNodeData: INodeData, reactFlowNodeData: INodeData,
reactFlowNodes: IReactFlowNode[], reactFlowNodes: IReactFlowNode[],
question: string, question: string,
chatHistory: IMessage[] chatHistory: IMessage[] | string
): INodeData => { ): INodeData => {
let flowNodeData = cloneDeep(reactFlowNodeData) let flowNodeData = cloneDeep(reactFlowNodeData)
const types = 'inputs' const types = 'inputs'
@ -873,3 +877,39 @@ export const checkMemorySessionId = (instance: any, chatId: string): string | un
return instance.memory.chatHistory.sessionId return instance.memory.chatHistory.sessionId
return undefined 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<string> => {
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 ''
}