diff --git a/packages/components/nodes/memory/AgentMemory/AgentMemory.ts b/packages/components/nodes/memory/AgentMemory/AgentMemory.ts index 33ef61cb..d317e138 100644 --- a/packages/components/nodes/memory/AgentMemory/AgentMemory.ts +++ b/packages/components/nodes/memory/AgentMemory/AgentMemory.ts @@ -2,10 +2,10 @@ import path from 'path' import { getBaseClasses, getCredentialData, getCredentialParam, getUserHome } from '../../../src/utils' import { SaverOptions } from './interface' import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../src/Interface' -import { SqliteSaver } from './sqliteSaver' +import { SqliteSaver } from './SQLiteAgentMemory/sqliteSaver' import { DataSource } from 'typeorm' -import { PostgresSaver } from './pgSaver' -import { MySQLSaver } from './mysqlSaver' +import { PostgresSaver } from './PostgresAgentMemory/pgSaver' +import { MySQLSaver } from './MySQLAgentMemory/mysqlSaver' class AgentMemory_Memory implements INode { label: string @@ -29,6 +29,7 @@ class AgentMemory_Memory implements INode { this.category = 'Memory' this.description = 'Memory for agentflow to remember the state of the conversation' this.baseClasses = [this.type, ...getBaseClasses(SqliteSaver)] + this.badge = 'DEPRECATING' this.credential = { label: 'Connect Credential', name: 'credential', diff --git a/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/MySQLAgentMemory.ts b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/MySQLAgentMemory.ts new file mode 100644 index 00000000..af3ebe61 --- /dev/null +++ b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/MySQLAgentMemory.ts @@ -0,0 +1,112 @@ +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../../src/utils' +import { SaverOptions } from '../interface' +import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../../src/Interface' +import { DataSource } from 'typeorm' +import { MySQLSaver } from './mysqlSaver' + +class MySQLAgentMemory_Memory implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + badge: string + baseClasses: string[] + inputs: INodeParams[] + credential: INodeParams + + constructor() { + this.label = 'MySQL Agent Memory' + this.name = 'mySQLAgentMemory' + this.version = 1.0 + this.type = 'AgentMemory' + this.icon = 'mysql.png' + this.category = 'Memory' + this.description = 'Memory for agentflow to remember the state of the conversation using MySQL database' + this.baseClasses = [this.type, ...getBaseClasses(MySQLSaver)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['MySQLApi'], + optional: true + } + this.inputs = [ + { + label: 'Host', + name: 'host', + type: 'string' + }, + { + label: 'Database', + name: 'database', + type: 'string' + }, + { + label: 'Port', + name: 'port', + type: 'number', + default: '3306' + }, + { + label: 'Additional Connection Configuration', + name: 'additionalConfig', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const additionalConfig = nodeData.inputs?.additionalConfig as string + const databaseEntities = options.databaseEntities as IDatabaseEntity + const chatflowid = options.chatflowid as string + const appDataSource = options.appDataSource as DataSource + + let additionalConfiguration = {} + if (additionalConfig) { + try { + additionalConfiguration = typeof additionalConfig === 'object' ? additionalConfig : JSON.parse(additionalConfig) + } catch (exception) { + throw new Error('Invalid JSON in the Additional Configuration: ' + exception) + } + } + + const threadId = options.sessionId || options.chatId + + let datasourceOptions: ICommonObject = { + ...additionalConfiguration, + type: 'mysql' + } + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const user = getCredentialParam('user', credentialData, nodeData) + const password = getCredentialParam('password', credentialData, nodeData) + const _port = (nodeData.inputs?.port as string) || '3306' + const port = parseInt(_port) + datasourceOptions = { + ...datasourceOptions, + host: nodeData.inputs?.host as string, + port, + database: nodeData.inputs?.database as string, + username: user, + user: user, + password: password, + charset: 'utf8mb4' + } + const args: SaverOptions = { + datasourceOptions, + threadId, + appDataSource, + databaseEntities, + chatflowid + } + const recordManager = new MySQLSaver(args) + return recordManager + } +} + +module.exports = { nodeClass: MySQLAgentMemory_Memory } diff --git a/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysql.png b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysql.png new file mode 100644 index 00000000..11c2d8b2 Binary files /dev/null and b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysql.png differ diff --git a/packages/components/nodes/memory/AgentMemory/mysqlSaver.ts b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts similarity index 98% rename from packages/components/nodes/memory/AgentMemory/mysqlSaver.ts rename to packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts index 147acace..525886ee 100644 --- a/packages/components/nodes/memory/AgentMemory/mysqlSaver.ts +++ b/packages/components/nodes/memory/AgentMemory/MySQLAgentMemory/mysqlSaver.ts @@ -2,9 +2,9 @@ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata } from '@langchain/ import { RunnableConfig } from '@langchain/core/runnables' import { BaseMessage } from '@langchain/core/messages' import { DataSource } from 'typeorm' -import { CheckpointTuple, SaverOptions, SerializerProtocol } from './interface' -import { IMessage, MemoryMethods } from '../../../src/Interface' -import { mapChatMessageToBaseMessage } from '../../../src/utils' +import { CheckpointTuple, SaverOptions, SerializerProtocol } from '../interface' +import { IMessage, MemoryMethods } from '../../../../src/Interface' +import { mapChatMessageToBaseMessage } from '../../../../src/utils' export class MySQLSaver extends BaseCheckpointSaver implements MemoryMethods { protected isSetup: boolean diff --git a/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/PostgresAgentMemory.ts b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/PostgresAgentMemory.ts new file mode 100644 index 00000000..cb6d041a --- /dev/null +++ b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/PostgresAgentMemory.ts @@ -0,0 +1,111 @@ +import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../../src/utils' +import { SaverOptions } from '../interface' +import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../../src/Interface' +import { DataSource } from 'typeorm' +import { PostgresSaver } from './pgSaver' + +class PostgresAgentMemory_Memory implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + badge: string + baseClasses: string[] + inputs: INodeParams[] + credential: INodeParams + + constructor() { + this.label = 'Postgres Agent Memory' + this.name = 'postgresAgentMemory' + this.version = 1.0 + this.type = 'AgentMemory' + this.icon = 'postgres.svg' + this.category = 'Memory' + this.description = 'Memory for agentflow to remember the state of the conversation using Postgres database' + this.baseClasses = [this.type, ...getBaseClasses(PostgresSaver)] + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['PostgresApi'], + optional: true + } + this.inputs = [ + { + label: 'Host', + name: 'host', + type: 'string' + }, + { + label: 'Database', + name: 'database', + type: 'string' + }, + { + label: 'Port', + name: 'port', + type: 'number', + default: '5432' + }, + { + label: 'Additional Connection Configuration', + name: 'additionalConfig', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const additionalConfig = nodeData.inputs?.additionalConfig as string + const databaseEntities = options.databaseEntities as IDatabaseEntity + const chatflowid = options.chatflowid as string + const appDataSource = options.appDataSource as DataSource + + let additionalConfiguration = {} + if (additionalConfig) { + try { + additionalConfiguration = typeof additionalConfig === 'object' ? additionalConfig : JSON.parse(additionalConfig) + } catch (exception) { + throw new Error('Invalid JSON in the Additional Configuration: ' + exception) + } + } + + const threadId = options.sessionId || options.chatId + + let datasourceOptions: ICommonObject = { + ...additionalConfiguration, + type: 'postgres' + } + + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const user = getCredentialParam('user', credentialData, nodeData) + const password = getCredentialParam('password', credentialData, nodeData) + const _port = (nodeData.inputs?.port as string) || '5432' + const port = parseInt(_port) + datasourceOptions = { + ...datasourceOptions, + host: nodeData.inputs?.host as string, + port, + database: nodeData.inputs?.database as string, + username: user, + user: user, + password: password + } + const args: SaverOptions = { + datasourceOptions, + threadId, + appDataSource, + databaseEntities, + chatflowid + } + const recordManager = new PostgresSaver(args) + return recordManager + } +} + +module.exports = { nodeClass: PostgresAgentMemory_Memory } diff --git a/packages/components/nodes/memory/AgentMemory/pgSaver.ts b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts similarity index 98% rename from packages/components/nodes/memory/AgentMemory/pgSaver.ts rename to packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts index 7913825a..11f261a4 100644 --- a/packages/components/nodes/memory/AgentMemory/pgSaver.ts +++ b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/pgSaver.ts @@ -2,9 +2,9 @@ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata } from '@langchain/ import { RunnableConfig } from '@langchain/core/runnables' import { BaseMessage } from '@langchain/core/messages' import { DataSource } from 'typeorm' -import { CheckpointTuple, SaverOptions, SerializerProtocol } from './interface' -import { IMessage, MemoryMethods } from '../../../src/Interface' -import { mapChatMessageToBaseMessage } from '../../../src/utils' +import { CheckpointTuple, SaverOptions, SerializerProtocol } from '../interface' +import { IMessage, MemoryMethods } from '../../../../src/Interface' +import { mapChatMessageToBaseMessage } from '../../../../src/utils' export class PostgresSaver extends BaseCheckpointSaver implements MemoryMethods { protected isSetup: boolean diff --git a/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/postgres.svg b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/postgres.svg new file mode 100644 index 00000000..f631e7a8 --- /dev/null +++ b/packages/components/nodes/memory/AgentMemory/PostgresAgentMemory/postgres.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/SQLiteAgentMemory.ts b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/SQLiteAgentMemory.ts new file mode 100644 index 00000000..b301e3a8 --- /dev/null +++ b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/SQLiteAgentMemory.ts @@ -0,0 +1,87 @@ +import path from 'path' +import { getBaseClasses, getUserHome } from '../../../../src/utils' +import { SaverOptions } from '../interface' +import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../../src/Interface' +import { SqliteSaver } from './sqliteSaver' +import { DataSource } from 'typeorm' + +class SQLiteAgentMemory_Memory implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + badge: string + baseClasses: string[] + inputs: INodeParams[] + credential: INodeParams + + constructor() { + this.label = 'SQLite Agent Memory' + this.name = 'sqliteAgentMemory' + this.version = 1.0 + this.type = 'SQLiteAgentMemory' + this.icon = 'sqlite.png' + this.category = 'Memory' + this.description = 'Memory for agentflow to remember the state of the conversation using SQLite database' + this.baseClasses = [this.type, ...getBaseClasses(SqliteSaver)] + this.inputs = [ + /*{ + label: 'Database File Path', + name: 'databaseFilePath', + type: 'string', + placeholder: 'C:\\Users\\User\\.flowise\\database.sqlite', + description: 'Path to the SQLite database file. Leave empty to use default application database', + optional: true + },*/ + { + label: 'Additional Connection Configuration', + name: 'additionalConfig', + type: 'json', + additionalParams: true, + optional: true + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const additionalConfig = nodeData.inputs?.additionalConfig as string + const databaseEntities = options.databaseEntities as IDatabaseEntity + const chatflowid = options.chatflowid as string + const appDataSource = options.appDataSource as DataSource + + let additionalConfiguration = {} + if (additionalConfig) { + try { + additionalConfiguration = typeof additionalConfig === 'object' ? additionalConfig : JSON.parse(additionalConfig) + } catch (exception) { + throw new Error('Invalid JSON in the Additional Configuration: ' + exception) + } + } + + const threadId = options.sessionId || options.chatId + + const database = path.join(process.env.DATABASE_PATH ?? path.join(getUserHome(), '.flowise'), 'database.sqlite') + + let datasourceOptions: ICommonObject = { + database, + ...additionalConfiguration, + type: 'sqlite' + } + + const args: SaverOptions = { + datasourceOptions, + threadId, + appDataSource, + databaseEntities, + chatflowid + } + + const recordManager = new SqliteSaver(args) + return recordManager + } +} + +module.exports = { nodeClass: SQLiteAgentMemory_Memory } diff --git a/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqlite.png b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqlite.png new file mode 100644 index 00000000..9f35f2bd Binary files /dev/null and b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqlite.png differ diff --git a/packages/components/nodes/memory/AgentMemory/sqliteSaver.ts b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts similarity index 98% rename from packages/components/nodes/memory/AgentMemory/sqliteSaver.ts rename to packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts index 4e5c5b80..dc746936 100644 --- a/packages/components/nodes/memory/AgentMemory/sqliteSaver.ts +++ b/packages/components/nodes/memory/AgentMemory/SQLiteAgentMemory/sqliteSaver.ts @@ -2,9 +2,9 @@ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata } from '@langchain/ import { RunnableConfig } from '@langchain/core/runnables' import { BaseMessage } from '@langchain/core/messages' import { DataSource, QueryRunner } from 'typeorm' -import { CheckpointTuple, SaverOptions, SerializerProtocol } from './interface' -import { IMessage, MemoryMethods } from '../../../src/Interface' -import { mapChatMessageToBaseMessage } from '../../../src/utils' +import { CheckpointTuple, SaverOptions, SerializerProtocol } from '../interface' +import { IMessage, MemoryMethods } from '../../../../src/Interface' +import { mapChatMessageToBaseMessage } from '../../../../src/utils' export class SqliteSaver extends BaseCheckpointSaver implements MemoryMethods { protected isSetup: boolean diff --git a/packages/server/src/utils/buildAgentGraph.ts b/packages/server/src/utils/buildAgentGraph.ts index c0e8d7a6..011cbcab 100644 --- a/packages/server/src/utils/buildAgentGraph.ts +++ b/packages/server/src/utils/buildAgentGraph.ts @@ -98,7 +98,8 @@ export const buildAgentGraph = async ( /*** Get Memory Node for Chat History ***/ let chatHistory: IMessage[] = [] - const memoryNode = nodes.find((node) => node.data.name === 'agentMemory') + const agentMemoryList = ['agentMemory', 'sqliteAgentMemory', 'postgresAgentMemory', 'mySQLAgentMemory'] + const memoryNode = nodes.find((node) => agentMemoryList.includes(node.data.name)) if (memoryNode) { chatHistory = await getSessionChatHistory( chatflowid, diff --git a/packages/ui/src/views/canvas/AddNodes.jsx b/packages/ui/src/views/canvas/AddNodes.jsx index e3e54383..93079a30 100644 --- a/packages/ui/src/views/canvas/AddNodes.jsx +++ b/packages/ui/src/views/canvas/AddNodes.jsx @@ -56,15 +56,17 @@ function a11yProps(index) { const blacklistCategoriesForAgentCanvas = ['Agents', 'Memory', 'Record Manager', 'Utilities'] +const agentMemoryNodes = ['agentMemory', 'sqliteAgentMemory', 'postgresAgentMemory', 'mySQLAgentMemory'] + // Show blacklisted nodes (exceptions) for agent canvas const exceptionsForAgentCanvas = { - Memory: ['agentMemory'], + Memory: agentMemoryNodes, Utilities: ['getVariable', 'setVariable', 'stickyNote'] } // Hide some nodes from the chatflow canvas const blacklistForChatflowCanvas = { - Memory: ['agentMemory'] + Memory: agentMemoryNodes } const AddNodes = ({ nodesData, node, isAgentCanvas }) => {