From a07546145df9adf31d9de854d77fab515c313183 Mon Sep 17 00:00:00 2001 From: Ong Chung Yau <33013947+chungyau97@users.noreply.github.com> Date: Thu, 3 Apr 2025 12:48:57 +0800 Subject: [PATCH] feature/export-import-3 (#4234) * fix: add ASSISTANT chatflow * feat: add mvp export import for chatMessage, chatFeedback, customTemplate, documentStore * feat: add function to handle duplicate ids during import * chore: typo 'orginalData' to 'originalData' * feat: add import conditions for chatMessage and chatMessageFeedback * feat: reduce spacing before saving chatflows --- .../src/services/chat-messages/index.ts | 30 +- .../server/src/services/chatflows/index.ts | 6 +- .../src/services/export-import/index.ts | 437 ++++++++++++++++-- .../Header/ProfileSection/index.jsx | 40 +- packages/ui/src/utils/exportImport.js | 17 +- 5 files changed, 460 insertions(+), 70 deletions(-) diff --git a/packages/server/src/services/chat-messages/index.ts b/packages/server/src/services/chat-messages/index.ts index dc3a9690d..20f0184fe 100644 --- a/packages/server/src/services/chat-messages/index.ts +++ b/packages/server/src/services/chat-messages/index.ts @@ -1,15 +1,15 @@ -import { DeleteResult, FindOptionsWhere } from 'typeorm' -import { StatusCodes } from 'http-status-codes' -import { ChatMessageRatingType, ChatType, IChatMessage, MODE } from '../../Interface' -import { utilGetChatMessage } from '../../utils/getChatMessage' -import { utilAddChatMessage } from '../../utils/addChatMesage' -import { getRunningExpressApp } from '../../utils/getRunningExpressApp' -import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' import { removeFilesFromStorage } from 'flowise-components' -import logger from '../../utils/logger' +import { StatusCodes } from 'http-status-codes' +import { DeleteResult, FindOptionsWhere } from 'typeorm' import { ChatMessage } from '../../database/entities/ChatMessage' +import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' +import { ChatMessageRatingType, ChatType, IChatMessage, MODE } from '../../Interface' +import { utilAddChatMessage } from '../../utils/addChatMesage' +import { utilGetChatMessage } from '../../utils/getChatMessage' +import { getRunningExpressApp } from '../../utils/getRunningExpressApp' +import logger from '../../utils/logger' // Add chatmessages for chatflowid const createChatMessage = async (chatMessage: Partial) => { @@ -178,11 +178,23 @@ const abortChatMessage = async (chatId: string, chatflowid: string) => { } } +async function getAllMessages(): Promise { + const appServer = getRunningExpressApp() + return await appServer.AppDataSource.getRepository(ChatMessage).find() +} + +async function getAllMessagesFeedback(): Promise { + const appServer = getRunningExpressApp() + return await appServer.AppDataSource.getRepository(ChatMessageFeedback).find() +} + export default { createChatMessage, getAllChatMessages, getAllInternalChatMessages, removeAllChatMessages, removeChatMessagesByMessageIds, - abortChatMessage + abortChatMessage, + getAllMessages, + getAllMessagesFeedback } diff --git a/packages/server/src/services/chatflows/index.ts b/packages/server/src/services/chatflows/index.ts index 2fc1adc18..603def5f5 100644 --- a/packages/server/src/services/chatflows/index.ts +++ b/packages/server/src/services/chatflows/index.ts @@ -1,6 +1,8 @@ import { ICommonObject, removeFolderFromStorage } from 'flowise-components' import { StatusCodes } from 'http-status-codes' +import { QueryRunner } from 'typeorm' import { ChatflowType, IReactFlowObject } from '../../Interface' +import { FLOWISE_COUNTER_STATUS, FLOWISE_METRIC_COUNTERS } from '../../Interface.Metrics' import { ChatFlow } from '../../database/entities/ChatFlow' import { ChatMessage } from '../../database/entities/ChatMessage' import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' @@ -13,8 +15,6 @@ import { containsBase64File, updateFlowDataWithFilePaths } from '../../utils/fil import { getRunningExpressApp } from '../../utils/getRunningExpressApp' import { utilGetUploadsConfig } from '../../utils/getUploadsConfig' import logger from '../../utils/logger' -import { FLOWISE_METRIC_COUNTERS, FLOWISE_COUNTER_STATUS } from '../../Interface.Metrics' -import { QueryRunner } from 'typeorm' // Check if chatflow valid for streaming const checkIfChatflowIsValidForStreaming = async (chatflowId: string): Promise => { @@ -120,6 +120,8 @@ const getAllChatflows = async (type?: ChatflowType): Promise => { const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).find() if (type === 'MULTIAGENT') { return dbResponse.filter((chatflow) => chatflow.type === 'MULTIAGENT') + } else if (type === 'ASSISTANT') { + return dbResponse.filter((chatflow) => chatflow.type === 'ASSISTANT') } else if (type === 'CHATFLOW') { // fetch all chatflows that are not agentflow return dbResponse.filter((chatflow) => chatflow.type === 'CHATFLOW' || !chatflow.type) diff --git a/packages/server/src/services/export-import/index.ts b/packages/server/src/services/export-import/index.ts index c113476a3..9c4f3173c 100644 --- a/packages/server/src/services/export-import/index.ts +++ b/packages/server/src/services/export-import/index.ts @@ -1,40 +1,68 @@ import { StatusCodes } from 'http-status-codes' +import { In, QueryRunner } from 'typeorm' +import { v4 as uuidv4 } from 'uuid' +import { Assistant } from '../../database/entities/Assistant' import { ChatFlow } from '../../database/entities/ChatFlow' +import { ChatMessage } from '../../database/entities/ChatMessage' +import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' +import { CustomTemplate } from '../../database/entities/CustomTemplate' +import { DocumentStore } from '../../database/entities/DocumentStore' +import { DocumentStoreFileChunk } from '../../database/entities/DocumentStoreFileChunk' import { Tool } from '../../database/entities/Tool' import { Variable } from '../../database/entities/Variable' -import { Assistant } from '../../database/entities/Assistant' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { getErrorMessage } from '../../errors/utils' import { getRunningExpressApp } from '../../utils/getRunningExpressApp' +import assistantService from '../assistants' +import chatMessagesService from '../chat-messages' import chatflowService from '../chatflows' +import documenStoreService from '../documentstore' +import marketplacesService from '../marketplaces' import toolsService from '../tools' import variableService from '../variables' -import assistantService from '../assistants' type ExportInput = { - tool: boolean - chatflow: boolean agentflow: boolean - variable: boolean assistant: boolean + chatflow: boolean + chat_message: boolean + chat_feedback: boolean + custom_template: boolean + document_store: boolean + tool: boolean + variable: boolean } type ExportData = { - Tool: Tool[] - ChatFlow: ChatFlow[] AgentFlow: ChatFlow[] - Variable: Variable[] + AssistantFlow: ChatFlow[] Assistant: Assistant[] + ChatFlow: ChatFlow[] + ChatMessage: ChatMessage[] + ChatMessageFeedback: ChatMessageFeedback[] + CustomTemplate: CustomTemplate[] + DocumentStore: DocumentStore[] + DocumentStoreFileChunk: DocumentStoreFileChunk[] + Tool: Tool[] + Variable: Variable[] } const convertExportInput = (body: any): ExportInput => { try { if (!body || typeof body !== 'object') throw new Error('Invalid ExportInput object in request body') - if (body.tool && typeof body.tool !== 'boolean') throw new Error('Invalid tool property in ExportInput object') - if (body.chatflow && typeof body.chatflow !== 'boolean') throw new Error('Invalid chatflow property in ExportInput object') if (body.agentflow && typeof body.agentflow !== 'boolean') throw new Error('Invalid agentflow property in ExportInput object') - if (body.variable && typeof body.variable !== 'boolean') throw new Error('Invalid variable property in ExportInput object') if (body.assistant && typeof body.assistant !== 'boolean') throw new Error('Invalid assistant property in ExportInput object') + if (body.chatflow && typeof body.chatflow !== 'boolean') throw new Error('Invalid chatflow property in ExportInput object') + if (body.chat_message && typeof body.chat_message !== 'boolean') + throw new Error('Invalid chat_message property in ExportInput object') + if (body.chat_feedback && typeof body.chat_feedback !== 'boolean') + throw new Error('Invalid chat_feedback property in ExportInput object') + if (body.custom_template && typeof body.custom_template !== 'boolean') + throw new Error('Invalid custom_template property in ExportInput object') + if (body.document_store && typeof body.document_store !== 'boolean') + throw new Error('Invalid document_store property in ExportInput object') + if (body.tool && typeof body.tool !== 'boolean') throw new Error('Invalid tool property in ExportInput object') + if (body.variable && typeof body.variable !== 'boolean') throw new Error('Invalid variable property in ExportInput object') return body as ExportInput } catch (error) { throw new InternalFlowiseError( @@ -47,31 +75,44 @@ const convertExportInput = (body: any): ExportInput => { const FileDefaultName = 'ExportData.json' const exportData = async (exportInput: ExportInput): Promise<{ FileDefaultName: string } & ExportData> => { try { - // step 1 - get all Tool - let allTool: Tool[] = [] - if (exportInput.tool === true) allTool = await toolsService.getAllTools() + let AgentFlow: ChatFlow[] = exportInput.agentflow === true ? await chatflowService.getAllChatflows('MULTIAGENT') : [] - // step 2 - get all ChatFlow - let allChatflow: ChatFlow[] = [] - if (exportInput.chatflow === true) allChatflow = await chatflowService.getAllChatflows('CHATFLOW') + let Assistant: Assistant[] = exportInput.assistant === true ? await assistantService.getAllAssistants() : [] - // step 3 - get all MultiAgent - let allMultiAgent: ChatFlow[] = [] - if (exportInput.agentflow === true) allMultiAgent = await chatflowService.getAllChatflows('MULTIAGENT') + let AssistantFlow: ChatFlow[] = exportInput.assistant === true ? await chatflowService.getAllChatflows('ASSISTANT') : [] - let allVars: Variable[] = [] - if (exportInput.variable === true) allVars = await variableService.getAllVariables() + let ChatFlow: ChatFlow[] = exportInput.chatflow === true ? await chatflowService.getAllChatflows('CHATFLOW') : [] - let allAssistants: Assistant[] = [] - if (exportInput.assistant === true) allAssistants = await assistantService.getAllAssistants() + let ChatMessage: ChatMessage[] = exportInput.chat_message === true ? await chatMessagesService.getAllMessages() : [] + + let ChatMessageFeedback: ChatMessageFeedback[] = + exportInput.chat_feedback === true ? await chatMessagesService.getAllMessagesFeedback() : [] + + let CustomTemplate: CustomTemplate[] = exportInput.custom_template === true ? await marketplacesService.getAllCustomTemplates() : [] + CustomTemplate = CustomTemplate.map((customTemplate) => ({ ...customTemplate, usecases: JSON.stringify(customTemplate.usecases) })) + + let DocumentStore: DocumentStore[] = exportInput.document_store === true ? await documenStoreService.getAllDocumentStores() : [] + + let DocumentStoreFileChunk: DocumentStoreFileChunk[] = + exportInput.document_store === true ? await documenStoreService.getAllDocumentFileChunks() : [] + + let Tool: Tool[] = exportInput.tool === true ? await toolsService.getAllTools() : [] + + let Variable: Variable[] = exportInput.variable === true ? await variableService.getAllVariables() : [] return { FileDefaultName, - Tool: allTool, - ChatFlow: allChatflow, - AgentFlow: allMultiAgent, - Variable: allVars, - Assistant: allAssistants + AgentFlow, + AssistantFlow, + Assistant, + ChatFlow, + ChatMessage, + ChatMessageFeedback, + CustomTemplate, + DocumentStore, + DocumentStoreFileChunk, + Tool, + Variable } } catch (error) { throw new InternalFlowiseError( @@ -81,28 +122,342 @@ const exportData = async (exportInput: ExportInput): Promise<{ FileDefaultName: } } -const importData = async (importData: ExportData) => { +async function replaceDuplicateIdsForChatFlow(queryRunner: QueryRunner, originalData: ExportData, chatflows: ChatFlow[]) { try { - const appServer = getRunningExpressApp() - const queryRunner = appServer.AppDataSource.createQueryRunner() + const ids = chatflows.map((chatflow) => chatflow.id) + const records = await queryRunner.manager.find(ChatFlow, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForChatflow - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForAssistant(queryRunner: QueryRunner, originalData: ExportData, assistants: Assistant[]) { + try { + const ids = assistants.map((assistant) => assistant.id) + const records = await queryRunner.manager.find(Assistant, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForAssistant - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForChatMessage(queryRunner: QueryRunner, originalData: ExportData, chatMessages: ChatMessage[]) { + try { + const chatmessageChatflowIds = chatMessages.map((chatMessage) => { + return { id: chatMessage.chatflowid, qty: 0 } + }) + const originalDataChatflowIds = originalData.ChatFlow.map((chatflow) => chatflow.id) + chatmessageChatflowIds.forEach((item) => { + if (originalDataChatflowIds.includes(item.id)) { + item.qty += 1 + } + }) + const databaseChatflowIds = await ( + await queryRunner.manager.find(ChatFlow, { + where: { id: In(chatmessageChatflowIds.map((chatmessageChatflowId) => chatmessageChatflowId.id)) } + }) + ).map((chatflow) => chatflow.id) + chatmessageChatflowIds.forEach((item) => { + if (databaseChatflowIds.includes(item.id)) { + item.qty += 1 + } + }) + + const missingChatflowIds = chatmessageChatflowIds.filter((item) => item.qty === 0).map((item) => item.id) + if (missingChatflowIds.length > 0) { + chatMessages = chatMessages.filter((chatMessage) => !missingChatflowIds.includes(chatMessage.chatflowid)) + originalData.ChatMessage = chatMessages + } + + const ids = chatMessages.map((chatMessage) => chatMessage.id) + const records = await queryRunner.manager.find(ChatMessage, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForChatMessage - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForChatMessageFeedback( + queryRunner: QueryRunner, + originalData: ExportData, + chatMessageFeedbacks: ChatMessageFeedback[] +) { + try { + const feedbackChatflowIds = chatMessageFeedbacks.map((feedback) => { + return { id: feedback.chatflowid, qty: 0 } + }) + const originalDataChatflowIds = originalData.ChatFlow.map((chatflow) => chatflow.id) + feedbackChatflowIds.forEach((item) => { + if (originalDataChatflowIds.includes(item.id)) { + item.qty += 1 + } + }) + const databaseChatflowIds = await ( + await queryRunner.manager.find(ChatFlow, { + where: { id: In(feedbackChatflowIds.map((feedbackChatflowId) => feedbackChatflowId.id)) } + }) + ).map((chatflow) => chatflow.id) + feedbackChatflowIds.forEach((item) => { + if (databaseChatflowIds.includes(item.id)) { + item.qty += 1 + } + }) + + const feedbackMessageIds = chatMessageFeedbacks.map((feedback) => { + return { id: feedback.messageId, qty: 0 } + }) + const originalDataMessageIds = originalData.ChatMessage.map((chatMessage) => chatMessage.id) + feedbackMessageIds.forEach((item) => { + if (originalDataMessageIds.includes(item.id)) { + item.qty += 1 + } + }) + const databaseMessageIds = await ( + await queryRunner.manager.find(ChatMessage, { + where: { id: In(feedbackMessageIds.map((feedbackMessageId) => feedbackMessageId.id)) } + }) + ).map((chatMessage) => chatMessage.id) + feedbackMessageIds.forEach((item) => { + if (databaseMessageIds.includes(item.id)) { + item.qty += 1 + } + }) + + const missingChatflowIds = feedbackChatflowIds.filter((item) => item.qty === 0).map((item) => item.id) + const missingMessageIds = feedbackMessageIds.filter((item) => item.qty === 0).map((item) => item.id) + + if (missingChatflowIds.length > 0 || missingMessageIds.length > 0) { + chatMessageFeedbacks = chatMessageFeedbacks.filter( + (feedback) => !missingChatflowIds.includes(feedback.chatflowid) && !missingMessageIds.includes(feedback.messageId) + ) + originalData.ChatMessageFeedback = chatMessageFeedbacks + } + + const ids = chatMessageFeedbacks.map((chatMessageFeedback) => chatMessageFeedback.id) + const records = await queryRunner.manager.find(ChatMessageFeedback, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForChatMessageFeedback - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForCustomTemplate(queryRunner: QueryRunner, originalData: ExportData, customTemplates: CustomTemplate[]) { + try { + const ids = customTemplates.map((customTemplate) => customTemplate.id) + const records = await queryRunner.manager.find(CustomTemplate, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForCustomTemplate - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForDocumentStore(queryRunner: QueryRunner, originalData: ExportData, documentStores: DocumentStore[]) { + try { + const ids = documentStores.map((documentStore) => documentStore.id) + const records = await queryRunner.manager.find(DocumentStore, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForDocumentStore - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForDocumentStoreFileChunk( + queryRunner: QueryRunner, + originalData: ExportData, + documentStoreFileChunks: DocumentStoreFileChunk[] +) { + try { + const ids = documentStoreFileChunks.map((documentStoreFileChunk) => documentStoreFileChunk.id) + const records = await queryRunner.manager.find(DocumentStoreFileChunk, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForDocumentStoreFileChunk - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForTool(queryRunner: QueryRunner, originalData: ExportData, tools: Tool[]) { + try { + const ids = tools.map((tool) => tool.id) + const records = await queryRunner.manager.find(Tool, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForTool - ${getErrorMessage(error)}` + ) + } +} + +async function replaceDuplicateIdsForVariable(queryRunner: QueryRunner, originalData: ExportData, variables: Variable[]) { + try { + const ids = variables.map((variable) => variable.id) + const records = await queryRunner.manager.find(Variable, { + where: { id: In(ids) } + }) + if (records.length < 0) return originalData + for (let record of records) { + const oldId = record.id + const newId = uuidv4() + originalData = JSON.parse(JSON.stringify(originalData).replaceAll(oldId, newId)) + } + return originalData + } catch (error) { + throw new InternalFlowiseError( + StatusCodes.INTERNAL_SERVER_ERROR, + `Error: exportImportService.replaceDuplicateIdsForVariable - ${getErrorMessage(error)}` + ) + } +} + +function reduceSpaceForChatflowFlowData(chatflows: ChatFlow[]) { + return chatflows.map((chatflow) => { + return { ...chatflow, flowData: JSON.stringify(JSON.parse(chatflow.flowData)) } + }) +} + +const importData = async (importData: ExportData) => { + let queryRunner + try { + queryRunner = getRunningExpressApp().AppDataSource.createQueryRunner() + await queryRunner.connect() try { + if (importData.AgentFlow.length > 0) { + importData.AgentFlow = reduceSpaceForChatflowFlowData(importData.AgentFlow) + importData = await replaceDuplicateIdsForChatFlow(queryRunner, importData, importData.AgentFlow) + } + if (importData.AssistantFlow.length > 0) { + importData.AssistantFlow = reduceSpaceForChatflowFlowData(importData.AssistantFlow) + importData = await replaceDuplicateIdsForChatFlow(queryRunner, importData, importData.AssistantFlow) + } + if (importData.Assistant.length > 0) + importData = await replaceDuplicateIdsForAssistant(queryRunner, importData, importData.Assistant) + if (importData.ChatFlow.length > 0) { + importData.ChatFlow = reduceSpaceForChatflowFlowData(importData.ChatFlow) + importData = await replaceDuplicateIdsForChatFlow(queryRunner, importData, importData.ChatFlow) + } + if (importData.ChatMessage.length > 0) + importData = await replaceDuplicateIdsForChatMessage(queryRunner, importData, importData.ChatMessage) + if (importData.ChatMessageFeedback.length > 0) + importData = await replaceDuplicateIdsForChatMessageFeedback(queryRunner, importData, importData.ChatMessageFeedback) + if (importData.CustomTemplate.length > 0) + importData = await replaceDuplicateIdsForCustomTemplate(queryRunner, importData, importData.CustomTemplate) + if (importData.DocumentStore.length > 0) + importData = await replaceDuplicateIdsForDocumentStore(queryRunner, importData, importData.DocumentStore) + if (importData.DocumentStoreFileChunk.length > 0) + importData = await replaceDuplicateIdsForDocumentStoreFileChunk(queryRunner, importData, importData.DocumentStoreFileChunk) + if (importData.Tool.length > 0) importData = await replaceDuplicateIdsForTool(queryRunner, importData, importData.Tool) + if (importData.Variable.length > 0) + importData = await replaceDuplicateIdsForVariable(queryRunner, importData, importData.Variable) + await queryRunner.startTransaction() - if (importData.Tool.length > 0) await toolsService.importTools(importData.Tool, queryRunner) - if (importData.ChatFlow.length > 0) await chatflowService.importChatflows(importData.ChatFlow, queryRunner) - if (importData.AgentFlow.length > 0) await chatflowService.importChatflows(importData.AgentFlow, queryRunner) - if (importData.Variable.length > 0) await variableService.importVariables(importData.Variable, queryRunner) - if (importData.Assistant.length > 0) await assistantService.importAssistants(importData.Assistant, queryRunner) + if (importData.AgentFlow.length > 0) await queryRunner.manager.save(ChatFlow, importData.AgentFlow) + if (importData.AssistantFlow.length > 0) await queryRunner.manager.save(ChatFlow, importData.AssistantFlow) + if (importData.Assistant.length > 0) await queryRunner.manager.save(Assistant, importData.Assistant) + if (importData.ChatFlow.length > 0) await queryRunner.manager.save(ChatFlow, importData.ChatFlow) + if (importData.ChatMessage.length > 0) await queryRunner.manager.save(ChatMessage, importData.ChatMessage) + if (importData.ChatMessageFeedback.length > 0) + await queryRunner.manager.save(ChatMessageFeedback, importData.ChatMessageFeedback) + if (importData.CustomTemplate.length > 0) await queryRunner.manager.save(CustomTemplate, importData.CustomTemplate) + if (importData.DocumentStore.length > 0) await queryRunner.manager.save(DocumentStore, importData.DocumentStore) + if (importData.DocumentStoreFileChunk.length > 0) + await queryRunner.manager.save(DocumentStoreFileChunk, importData.DocumentStoreFileChunk) + if (importData.Tool.length > 0) await queryRunner.manager.save(Tool, importData.Tool) + if (importData.Variable.length > 0) await queryRunner.manager.save(Variable, importData.Variable) await queryRunner.commitTransaction() } catch (error) { - await queryRunner.rollbackTransaction() + if (queryRunner && !queryRunner.isTransactionActive) await queryRunner.rollbackTransaction() throw error } finally { - if (!queryRunner.isReleased) { - await queryRunner.release() - } + if (queryRunner && !queryRunner.isReleased) await queryRunner.release() } } catch (error) { throw new InternalFlowiseError( diff --git a/packages/ui/src/layout/MainLayout/Header/ProfileSection/index.jsx b/packages/ui/src/layout/MainLayout/Header/ProfileSection/index.jsx index 5caec1463..59b4b8280 100644 --- a/packages/ui/src/layout/MainLayout/Header/ProfileSection/index.jsx +++ b/packages/ui/src/layout/MainLayout/Header/ProfileSection/index.jsx @@ -3,8 +3,8 @@ import { exportData, stringify } from '@/utils/exportImport' import useNotifier from '@/utils/useNotifier' import PropTypes from 'prop-types' import { useEffect, useRef, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' import { createPortal } from 'react-dom' +import { useDispatch, useSelector } from 'react-redux' // material-ui import { @@ -12,22 +12,22 @@ import { Box, Button, ButtonBase, + Checkbox, ClickAwayListener, + Dialog, + DialogActions, + DialogContent, + DialogTitle, Divider, + FormControlLabel, List, ListItemButton, ListItemIcon, ListItemText, Paper, Popper, - Typography, - Dialog, - DialogTitle, - DialogContent, Stack, - FormControlLabel, - Checkbox, - DialogActions + Typography } from '@mui/material' import { useTheme } from '@mui/material/styles' @@ -40,9 +40,9 @@ import AboutDialog from '@/ui-component/dialog/AboutDialog' import Transitions from '@/ui-component/extended/Transitions' // assets +import ExportingGIF from '@/assets/images/Exporting.gif' import { IconFileExport, IconFileUpload, IconInfoCircle, IconLogout, IconSettings, IconX } from '@tabler/icons-react' import './index.css' -import ExportingGIF from '@/assets/images/Exporting.gif' //API import exportImportApi from '@/api/exportimport' @@ -52,12 +52,22 @@ import useApi from '@/hooks/useApi' import { getErrorMessage } from '@/utils/errorHandler' import { useNavigate } from 'react-router-dom' -const dataToExport = ['Chatflows', 'Agentflows', 'Tools', 'Variables', 'Assistants'] +const dataToExport = [ + 'Agentflows', + 'Assistants', + 'Chatflows', + 'Chat Messages', + 'Chat Feedbacks', + 'Custom Templates', + 'Document Stores', + 'Tools', + 'Variables' +] const ExportDialog = ({ show, onCancel, onExport }) => { const portalElement = document.getElementById('portal') - const [selectedData, setSelectedData] = useState(['Chatflows', 'Agentflows', 'Tools', 'Variables', 'Assistants']) + const [selectedData, setSelectedData] = useState(dataToExport) const [isExporting, setIsExporting] = useState(false) useEffect(() => { @@ -243,11 +253,15 @@ const ProfileSection = ({ username, handleLogout }) => { const onExport = (data) => { const body = {} - if (data.includes('Chatflows')) body.chatflow = true if (data.includes('Agentflows')) body.agentflow = true + if (data.includes('Assistants')) body.assistant = true + if (data.includes('Chatflows')) body.chatflow = true + if (data.includes('Chat Messages')) body.chat_message = true + if (data.includes('Chat Feedbacks')) body.chat_feedback = true + if (data.includes('Custom Templates')) body.custom_template = true + if (data.includes('Document Stores')) body.document_store = true if (data.includes('Tools')) body.tool = true if (data.includes('Variables')) body.variable = true - if (data.includes('Assistants')) body.assistant = true exportAllApi.request(body) } diff --git a/packages/ui/src/utils/exportImport.js b/packages/ui/src/utils/exportImport.js index b160583d0..518f8230f 100644 --- a/packages/ui/src/utils/exportImport.js +++ b/packages/ui/src/utils/exportImport.js @@ -57,7 +57,8 @@ const sanitizeAssistant = (Assistant) => { id: assistant.id, details: assistant.details, credential: assistant.credential, - iconSrc: assistant.iconSrc + iconSrc: assistant.iconSrc, + type: assistant.type } }) } catch (error) { @@ -76,11 +77,17 @@ export const stringify = (object) => { export const exportData = (exportAllData) => { try { return { - Tool: sanitizeTool(exportAllData.Tool), - ChatFlow: sanitizeChatflow(exportAllData.ChatFlow), AgentFlow: sanitizeChatflow(exportAllData.AgentFlow), - Variable: sanitizeVariable(exportAllData.Variable), - Assistant: sanitizeAssistant(exportAllData.Assistant) + AssistantFlow: sanitizeChatflow(exportAllData.AssistantFlow), + Assistant: sanitizeAssistant(exportAllData.Assistant), + ChatFlow: sanitizeChatflow(exportAllData.ChatFlow), + ChatMessage: exportAllData.ChatMessage, + ChatMessageFeedback: exportAllData.ChatMessageFeedback, + CustomTemplate: exportAllData.CustomTemplate, + DocumentStore: exportAllData.DocumentStore, + DocumentStoreFileChunk: exportAllData.DocumentStoreFileChunk, + Tool: sanitizeTool(exportAllData.Tool), + Variable: sanitizeVariable(exportAllData.Variable) } } catch (error) { throw new Error(`exportImport.exportData ${getErrorMessage(error)}`)