diff --git a/packages/components/nodes/chatmodels/AzureChatOpenAI/AzureChatOpenAI.ts b/packages/components/nodes/chatmodels/AzureChatOpenAI/AzureChatOpenAI.ts index 7857bfdf..60295890 100644 --- a/packages/components/nodes/chatmodels/AzureChatOpenAI/AzureChatOpenAI.ts +++ b/packages/components/nodes/chatmodels/AzureChatOpenAI/AzureChatOpenAI.ts @@ -124,7 +124,7 @@ class AzureChatOpenAI_ChatModels implements INode { const streaming = nodeData.inputs?.streaming as boolean const obj: Partial & Partial = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, azureOpenAIApiKey, azureOpenAIApiInstanceName, diff --git a/packages/components/nodes/chatmodels/ChatAnthropic/ChatAnthropic.ts b/packages/components/nodes/chatmodels/ChatAnthropic/ChatAnthropic.ts index 708849e5..3d861d24 100644 --- a/packages/components/nodes/chatmodels/ChatAnthropic/ChatAnthropic.ts +++ b/packages/components/nodes/chatmodels/ChatAnthropic/ChatAnthropic.ts @@ -120,7 +120,7 @@ class ChatAnthropic_ChatModels implements INode { const streaming = nodeData.inputs?.streaming as boolean const obj: Partial & { anthropicApiKey?: string } = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, anthropicApiKey, streaming: streaming ?? true diff --git a/packages/components/nodes/chatmodels/ChatHuggingFace/ChatHuggingFace.ts b/packages/components/nodes/chatmodels/ChatHuggingFace/ChatHuggingFace.ts index 3252a61a..1dae41e4 100644 --- a/packages/components/nodes/chatmodels/ChatHuggingFace/ChatHuggingFace.ts +++ b/packages/components/nodes/chatmodels/ChatHuggingFace/ChatHuggingFace.ts @@ -89,7 +89,7 @@ class ChatHuggingFace_ChatModels implements INode { apiKey } - if (temperature) obj.temperature = parseInt(temperature, 10) + if (temperature) obj.temperature = parseFloat(temperature) if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10) if (topP) obj.topP = parseInt(topP, 10) if (hfTopK) obj.topK = parseInt(hfTopK, 10) diff --git a/packages/components/nodes/chatmodels/ChatLocalAI/ChatLocalAI.ts b/packages/components/nodes/chatmodels/ChatLocalAI/ChatLocalAI.ts index bd25a9fa..c5860b24 100644 --- a/packages/components/nodes/chatmodels/ChatLocalAI/ChatLocalAI.ts +++ b/packages/components/nodes/chatmodels/ChatLocalAI/ChatLocalAI.ts @@ -74,7 +74,7 @@ class ChatLocalAI_ChatModels implements INode { const basePath = nodeData.inputs?.basePath as string const obj: Partial & { openAIApiKey?: string } = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, openAIApiKey: 'sk-' } diff --git a/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts b/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts index 26f54db8..955563ff 100644 --- a/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts +++ b/packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts @@ -132,7 +132,7 @@ class ChatOpenAI_ChatModels implements INode { const basePath = nodeData.inputs?.basepath as string const obj: Partial & { openAIApiKey?: string } = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, openAIApiKey, streaming: streaming ?? true diff --git a/packages/components/nodes/llms/Azure OpenAI/AzureOpenAI.ts b/packages/components/nodes/llms/Azure OpenAI/AzureOpenAI.ts index c19aa83a..f81c9349 100644 --- a/packages/components/nodes/llms/Azure OpenAI/AzureOpenAI.ts +++ b/packages/components/nodes/llms/Azure OpenAI/AzureOpenAI.ts @@ -179,7 +179,7 @@ class AzureOpenAI_LLMs implements INode { const streaming = nodeData.inputs?.streaming as boolean const obj: Partial & Partial = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, azureOpenAIApiKey, azureOpenAIApiInstanceName, diff --git a/packages/components/nodes/llms/Cohere/Cohere.ts b/packages/components/nodes/llms/Cohere/Cohere.ts index a7e9c696..75151571 100644 --- a/packages/components/nodes/llms/Cohere/Cohere.ts +++ b/packages/components/nodes/llms/Cohere/Cohere.ts @@ -87,7 +87,7 @@ class Cohere_LLMs implements INode { if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10) if (modelName) obj.model = modelName - if (temperature) obj.temperature = parseInt(temperature, 10) + if (temperature) obj.temperature = parseFloat(temperature) const model = new Cohere(obj) return model diff --git a/packages/components/nodes/llms/HuggingFaceInference/HuggingFaceInference.ts b/packages/components/nodes/llms/HuggingFaceInference/HuggingFaceInference.ts index 88a7db07..291f67c9 100644 --- a/packages/components/nodes/llms/HuggingFaceInference/HuggingFaceInference.ts +++ b/packages/components/nodes/llms/HuggingFaceInference/HuggingFaceInference.ts @@ -89,7 +89,7 @@ class HuggingFaceInference_LLMs implements INode { apiKey } - if (temperature) obj.temperature = parseInt(temperature, 10) + if (temperature) obj.temperature = parseFloat(temperature) if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10) if (topP) obj.topP = parseInt(topP, 10) if (hfTopK) obj.topK = parseInt(hfTopK, 10) diff --git a/packages/components/nodes/llms/OpenAI/OpenAI.ts b/packages/components/nodes/llms/OpenAI/OpenAI.ts index fb7e5b6b..b0af867d 100644 --- a/packages/components/nodes/llms/OpenAI/OpenAI.ts +++ b/packages/components/nodes/llms/OpenAI/OpenAI.ts @@ -132,7 +132,7 @@ class OpenAI_LLMs implements INode { const basePath = nodeData.inputs?.basepath as string const obj: Partial & { openAIApiKey?: string } = { - temperature: parseInt(temperature, 10), + temperature: parseFloat(temperature), modelName, openAIApiKey, streaming: streaming ?? true diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts new file mode 100644 index 00000000..e332181d --- /dev/null +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -0,0 +1,85 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { ICommonObject } from '../../../src' +import { BufferMemory } from 'langchain/memory' +import { RedisChatMessageHistory, RedisChatMessageHistoryInput } from 'langchain/stores/message/redis' +import { createClient } from 'redis' + +class RedisBackedChatMemory_Memory implements INode { + label: string + name: string + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Redis-Backed Chat Memory' + this.name = 'RedisBackedChatMemory' + this.type = 'RedisBackedChatMemory' + this.icon = 'redis.svg' + this.category = 'Memory' + this.description = 'Summarizes the conversation and stores the memory in Redis server' + this.baseClasses = [this.type, ...getBaseClasses(BufferMemory)] + this.inputs = [ + { + label: 'Base URL', + name: 'baseURL', + type: 'string', + default: 'redis://localhost:6379' + }, + { + label: 'Session Id', + name: 'sessionId', + type: 'string', + description: 'if empty, chatId will be used automatically', + default: '', + additionalParams: true + }, + { + label: 'Session Timeouts', + name: 'sessionTTL', + type: 'number', + description: 'Omit this parameter to make sessions never expire', + optional: true + }, + { + label: 'Memory Key', + name: 'memoryKey', + type: 'string', + default: 'chat_history' + } + ] + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const baseURL = nodeData.inputs?.baseURL as string + const sessionId = nodeData.inputs?.sessionId as string + const sessionTTL = nodeData.inputs?.sessionTTL as number + const memoryKey = nodeData.inputs?.memoryKey as string + + const chatId = options?.chatId as string + + const redisClient = createClient({ url: baseURL }) + let obj: RedisChatMessageHistoryInput = { + sessionId: sessionId ? sessionId : chatId, + client: redisClient + } + + if (sessionTTL) { + obj = { + ...obj, + sessionTTL + } + } + + let redisChatMessageHistory = new RedisChatMessageHistory(obj) + let redis = new BufferMemory({ memoryKey, chatHistory: redisChatMessageHistory, returnMessages: true }) + + return redis + } +} + +module.exports = { nodeClass: RedisBackedChatMemory_Memory } diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/redis.svg b/packages/components/nodes/memory/RedisBackedChatMemory/redis.svg new file mode 100644 index 00000000..90359069 --- /dev/null +++ b/packages/components/nodes/memory/RedisBackedChatMemory/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/nodes/prompts/PromptTemplate/PromptTemplate.ts b/packages/components/nodes/prompts/PromptTemplate/PromptTemplate.ts index cfa2c488..f976d64c 100644 --- a/packages/components/nodes/prompts/PromptTemplate/PromptTemplate.ts +++ b/packages/components/nodes/prompts/PromptTemplate/PromptTemplate.ts @@ -1,5 +1,5 @@ import { ICommonObject, INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface' -import { getBaseClasses, getInputVariables, returnJSONStr } from '../../../src/utils' +import { getBaseClasses, getInputVariables } from '../../../src/utils' import { PromptTemplateInput } from 'langchain/prompts' class PromptTemplate_Prompts implements INode { @@ -46,12 +46,11 @@ class PromptTemplate_Prompts implements INode { async init(nodeData: INodeData): Promise { const template = nodeData.inputs?.template as string - let promptValuesStr = nodeData.inputs?.promptValues as string + const promptValuesStr = nodeData.inputs?.promptValues as string let promptValues: ICommonObject = {} if (promptValuesStr) { - promptValuesStr = promptValuesStr.replace(/\s/g, '') - promptValues = JSON.parse(returnJSONStr(promptValuesStr)) + promptValues = JSON.parse(promptValuesStr.replace(/\s/g, '')) } const inputVariables = getInputVariables(template) diff --git a/packages/components/package.json b/packages/components/package.json index 72feeed5..ed48fb65 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -43,6 +43,7 @@ "pdfjs-dist": "^3.7.107", "playwright": "^1.35.0", "puppeteer": "^20.7.1", + "redis": "^4.6.7", "srt-parser-2": "^1.2.3", "vm2": "^3.9.19", "weaviate-ts-client": "^1.1.0", diff --git a/packages/server/.env.example b/packages/server/.env.example index 3d524e5c..80fbc3be 100644 --- a/packages/server/.env.example +++ b/packages/server/.env.example @@ -3,4 +3,5 @@ PORT=3000 # FLOWISE_PASSWORD=1234 # DEBUG=true # DATABASE_PATH=/your_database_path/.flowise +# APIKEY_PATH=/your_api_key_path/.flowise # EXECUTION_MODE=child or main \ No newline at end of file diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index 9473638f..9c47405c 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -9,10 +9,10 @@ export interface IChatFlow { id: string name: string flowData: string - apikeyid: string - deployed: boolean + isPublic: boolean updatedDate: Date createdDate: Date + apikeyid?: string chatbotConfig?: string } @@ -22,7 +22,7 @@ export interface IChatMessage { content: string chatflowid: string createdDate: Date - sourceDocuments: string + sourceDocuments?: string } export interface ITool { @@ -30,8 +30,8 @@ export interface ITool { name: string description: string color: string - schema: string - func: string + schema?: string + func?: string updatedDate: Date createdDate: Date } diff --git a/packages/server/src/entity/ChatFlow.ts b/packages/server/src/entity/ChatFlow.ts index 910272ad..400e0517 100644 --- a/packages/server/src/entity/ChatFlow.ts +++ b/packages/server/src/entity/ChatFlow.ts @@ -13,11 +13,11 @@ export class ChatFlow implements IChatFlow { @Column() flowData: string - @Column({ nullable: true }) - apikeyid: string - @Column() - deployed: boolean + isPublic: boolean + + @Column({ nullable: true }) + apikeyid?: string @Column({ nullable: true }) chatbotConfig?: string diff --git a/packages/server/src/entity/ChatMessage.ts b/packages/server/src/entity/ChatMessage.ts index 236dc5f9..3e4e41d2 100644 --- a/packages/server/src/entity/ChatMessage.ts +++ b/packages/server/src/entity/ChatMessage.ts @@ -18,7 +18,7 @@ export class ChatMessage implements IChatMessage { content: string @Column({ nullable: true }) - sourceDocuments: string + sourceDocuments?: string @CreateDateColumn() createdDate: Date diff --git a/packages/server/src/entity/Tool.ts b/packages/server/src/entity/Tool.ts index d547374c..307e8d23 100644 --- a/packages/server/src/entity/Tool.ts +++ b/packages/server/src/entity/Tool.ts @@ -17,10 +17,10 @@ export class Tool implements ITool { color: string @Column({ nullable: true }) - schema: string + schema?: string @Column({ nullable: true }) - func: string + func?: string @CreateDateColumn() createdDate: Date diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 0b39f73b..fba5be5e 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -211,6 +211,16 @@ export class App { return res.status(404).send(`Chatflow ${req.params.id} not found`) }) + // Get specific chatflow via id (PUBLIC endpoint, used when sharing chatbot link) + this.app.get('/api/v1/public-chatflows/:id', async (req: Request, res: Response) => { + const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({ + id: req.params.id + }) + if (chatflow && chatflow.isPublic) return res.json(chatflow) + else if (chatflow && !chatflow.isPublic) return res.status(401).send(`Unauthorized`) + return res.status(404).send(`Chatflow ${req.params.id} not found`) + }) + // Save chatflow this.app.post('/api/v1/chatflows', async (req: Request, res: Response) => { const body = req.body diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index e861e6fa..005f4a4b 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -463,7 +463,7 @@ export const isSameOverrideConfig = ( * @returns {string} */ export const getAPIKeyPath = (): string => { - return path.join(__dirname, '..', '..', 'api.json') + return process.env.APIKEY_PATH ? path.join(process.env.APIKEY_PATH, 'api.json') : path.join(__dirname, '..', '..', 'api.json') } /** diff --git a/packages/ui/src/api/chatflows.js b/packages/ui/src/api/chatflows.js index 1cd1ebb0..8810b5a5 100644 --- a/packages/ui/src/api/chatflows.js +++ b/packages/ui/src/api/chatflows.js @@ -4,6 +4,8 @@ const getAllChatflows = () => client.get('/chatflows') const getSpecificChatflow = (id) => client.get(`/chatflows/${id}`) +const getSpecificChatflowFromPublicEndpoint = (id) => client.get(`/public-chatflows/${id}`) + const createNewChatflow = (body) => client.post(`/chatflows`, body) const updateChatflow = (id, body) => client.put(`/chatflows/${id}`, body) @@ -15,6 +17,7 @@ const getIsChatflowStreaming = (id) => client.get(`/chatflows-streaming/${id}`) export default { getAllChatflows, getSpecificChatflow, + getSpecificChatflowFromPublicEndpoint, createNewChatflow, updateChatflow, deleteChatflow, diff --git a/packages/ui/src/views/canvas/CanvasHeader.js b/packages/ui/src/views/canvas/CanvasHeader.js index 521aa9d3..1c1e5212 100644 --- a/packages/ui/src/views/canvas/CanvasHeader.js +++ b/packages/ui/src/views/canvas/CanvasHeader.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types' import { useNavigate } from 'react-router-dom' -import { useSelector } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux' import { useEffect, useRef, useState } from 'react' // material-ui @@ -24,11 +24,13 @@ import useApi from 'hooks/useApi' // utils import { generateExportFlowData } from 'utils/genericHelper' import { uiBaseURL } from 'store/constant' +import { SET_CHATFLOW } from 'store/actions' // ==============================|| CANVAS HEADER ||============================== // const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFlow }) => { const theme = useTheme() + const dispatch = useDispatch() const navigate = useNavigate() const flowNameRef = useRef() const settingsRef = useRef() @@ -107,8 +109,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl title: 'Embed in website or use as API', chatflowid: chatflow.id, chatflowApiKeyId: chatflow.apikeyid, - isFormDataRequired, - chatbotConfig: chatflow.chatbotConfig + isFormDataRequired }) setAPIDialogOpen(true) } @@ -126,6 +127,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl useEffect(() => { if (updateChatflowApi.data) { setFlowName(updateChatflowApi.data.name) + dispatch({ type: SET_CHATFLOW, chatflow: updateChatflowApi.data }) } setEditingFlowName(false) diff --git a/packages/ui/src/views/canvas/index.js b/packages/ui/src/views/canvas/index.js index 2d71f03a..03098963 100644 --- a/packages/ui/src/views/canvas/index.js +++ b/packages/ui/src/views/canvas/index.js @@ -201,7 +201,7 @@ const Canvas = () => { if (!chatflow.id) { const newChatflowBody = { name: chatflowName, - deployed: false, + isPublic: false, flowData } createNewChatflowApi.request(newChatflowBody) diff --git a/packages/ui/src/views/chatbot/index.js b/packages/ui/src/views/chatbot/index.js index b33bec2c..f29c35ee 100644 --- a/packages/ui/src/views/chatbot/index.js +++ b/packages/ui/src/views/chatbot/index.js @@ -1,57 +1,107 @@ import { useEffect, useState } from 'react' -import { baseURL } from 'store/constant' -import axios from 'axios' import { FullPageChat } from 'flowise-embed-react' +import { useNavigate } from 'react-router-dom' + +// Project import +import LoginDialog from 'ui-component/dialog/LoginDialog' + +// API +import chatflowsApi from 'api/chatflows' + +// Hooks +import useApi from 'hooks/useApi' + +//Const +import { baseURL } from 'store/constant' // ==============================|| Chatbot ||============================== // -const fetchChatflow = async ({ chatflowId }) => { - const username = localStorage.getItem('username') - const password = localStorage.getItem('password') - - let chatflow = await axios - .get(`${baseURL}/api/v1/chatflows/${chatflowId}`, { auth: username && password ? { username, password } : undefined }) - .then(async function (response) { - return response.data - }) - .catch(function (error) { - console.error(error) - }) - return chatflow -} - const ChatbotFull = () => { const URLpath = document.location.pathname.toString().split('/') const chatflowId = URLpath[URLpath.length - 1] === 'chatbot' ? '' : URLpath[URLpath.length - 1] + const navigate = useNavigate() const [chatflow, setChatflow] = useState(null) const [chatbotTheme, setChatbotTheme] = useState({}) + const [loginDialogOpen, setLoginDialogOpen] = useState(false) + const [loginDialogProps, setLoginDialogProps] = useState({}) + const [isLoading, setLoading] = useState(true) + + const getSpecificChatflowFromPublicApi = useApi(chatflowsApi.getSpecificChatflowFromPublicEndpoint) + const getSpecificChatflowApi = useApi(chatflowsApi.getSpecificChatflow) + + const onLoginClick = (username, password) => { + localStorage.setItem('username', username) + localStorage.setItem('password', password) + navigate(0) + } useEffect(() => { - ;(async () => { - const fetchData = async () => { - let response = await fetchChatflow({ chatflowId }) - setChatflow(response) - if (response.chatbotConfig) { - try { - setChatbotTheme(JSON.parse(response.chatbotConfig)) - } catch (e) { - console.error(e) - setChatbotTheme({}) - } + getSpecificChatflowFromPublicApi.request(chatflowId) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + if (getSpecificChatflowFromPublicApi.error) { + if (getSpecificChatflowFromPublicApi.error?.response?.status === 401) { + if (localStorage.getItem('username') && localStorage.getItem('password')) { + getSpecificChatflowApi.request(chatflowId) + } else { + setLoginDialogProps({ + title: 'Login', + confirmButtonName: 'Login' + }) + setLoginDialogOpen(true) } } - fetchData() - })() - }, [chatflowId]) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [getSpecificChatflowFromPublicApi.error]) + + useEffect(() => { + if (getSpecificChatflowApi.error) { + if (getSpecificChatflowApi.error?.response?.status === 401) { + setLoginDialogProps({ + title: 'Login', + confirmButtonName: 'Login' + }) + setLoginDialogOpen(true) + } + } + }, [getSpecificChatflowApi.error]) + + useEffect(() => { + if (getSpecificChatflowFromPublicApi.data || getSpecificChatflowApi.data) { + const chatflowData = getSpecificChatflowFromPublicApi.data || getSpecificChatflowApi.data + setChatflow(chatflowData) + if (chatflowData.chatbotConfig) { + try { + setChatbotTheme(JSON.parse(chatflowData.chatbotConfig)) + } catch (e) { + console.error(e) + setChatbotTheme({}) + } + } + } + }, [getSpecificChatflowFromPublicApi.data, getSpecificChatflowApi.data]) + + useEffect(() => { + setLoading(getSpecificChatflowFromPublicApi.loading || getSpecificChatflowApi.loading) + }, [getSpecificChatflowFromPublicApi.loading, getSpecificChatflowApi.loading]) return ( <> - {!chatflow || chatflow.apikeyid ? ( -

Invalid Chatbot

- ) : ( - - )} + {!isLoading ? ( + <> + {!chatflow || chatflow.apikeyid ? ( +

Invalid Chatbot

+ ) : ( + + )} + + + ) : null} ) } diff --git a/packages/ui/src/views/chatflows/APICodeDialog.js b/packages/ui/src/views/chatflows/APICodeDialog.js index fea49909..5e32c1d4 100644 --- a/packages/ui/src/views/chatflows/APICodeDialog.js +++ b/packages/ui/src/views/chatflows/APICodeDialog.js @@ -134,7 +134,6 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => { const [chatflowApiKeyId, setChatflowApiKeyId] = useState('') const [selectedApiKey, setSelectedApiKey] = useState({}) const [checkboxVal, setCheckbox] = useState(false) - const [chatbotConfig, setChatbotConfig] = useState(null) const getAllAPIKeysApi = useApi(apiKeyApi.getAllAPIKeys) const updateChatflowApi = useApi(chatflowsApi.updateChatflow) @@ -491,12 +490,6 @@ query({ setChatflowApiKeyId(dialogProps.chatflowApiKeyId) setSelectedApiKey(getAllAPIKeysApi.data.find((key) => key.id === dialogProps.chatflowApiKeyId)) } - - if (dialogProps.chatbotConfig) { - setChatbotConfig(JSON.parse(dialogProps.chatbotConfig)) - } else { - setChatbotConfig(null) - } } }, [dialogProps, getAllAPIKeysApi.data]) @@ -601,9 +594,7 @@ query({ )} )} - {codeLang === 'Share Chatbot' && !chatflowApiKeyId && ( - - )} + {codeLang === 'Share Chatbot' && !chatflowApiKeyId && } ))} diff --git a/packages/ui/src/views/chatflows/ShareChatbot.js b/packages/ui/src/views/chatflows/ShareChatbot.js index dffecf5b..51e12e54 100644 --- a/packages/ui/src/views/chatflows/ShareChatbot.js +++ b/packages/ui/src/views/chatflows/ShareChatbot.js @@ -1,7 +1,6 @@ -import PropTypes from 'prop-types' import { useState } from 'react' -import { useDispatch } from 'react-redux' -import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from 'store/actions' +import { useDispatch, useSelector } from 'react-redux' +import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from 'store/actions' import { SketchPicker } from 'react-color' import { Box, Typography, Button, Switch, OutlinedInput, Popover, Stack, IconButton } from '@mui/material' @@ -9,6 +8,7 @@ import { useTheme } from '@mui/material/styles' // Project import import { StyledButton } from 'ui-component/button/StyledButton' +import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser' // Icons import { IconX, IconCopy, IconArrowUpRightCircle } from '@tabler/icons' @@ -41,15 +41,20 @@ const defaultConfig = { } } -const ShareChatbot = ({ chatflowid, chatbotConfig }) => { +const ShareChatbot = () => { const dispatch = useDispatch() const theme = useTheme() + const chatflow = useSelector((state) => state.canvas.chatflow) + const chatflowid = chatflow.id + const chatbotConfig = chatflow.chatbotConfig ? JSON.parse(chatflow.chatbotConfig) : {} useNotifier() const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + const [isPublicChatflow, setChatflowIsPublic] = useState(chatflow.isPublic ?? false) + const [welcomeMessage, setWelcomeMessage] = useState(chatbotConfig?.welcomeMessage ?? '') const [backgroundColor, setBackgroundColor] = useState(chatbotConfig?.backgroundColor ?? defaultConfig.backgroundColor) const [fontSize, setFontSize] = useState(chatbotConfig?.fontSize ?? defaultConfig.fontSize) @@ -141,6 +146,44 @@ const ShareChatbot = ({ chatflowid, chatbotConfig }) => { ) } }) + dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data }) + } + } catch (error) { + console.error(error) + const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}` + enqueueSnackbar({ + message: `Failed to save Chatbot Configuration: ${errorData}`, + options: { + key: new Date().getTime() + Math.random(), + variant: 'error', + persist: true, + action: (key) => ( + + ) + } + }) + } + } + + const onSwitchChange = async (checked) => { + try { + const saveResp = await chatflowsApi.updateChatflow(chatflowid, { isPublic: checked }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'Chatbot Configuration Saved', + options: { + key: new Date().getTime() + Math.random(), + variant: 'success', + action: (key) => ( + + ) + } + }) + dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data }) } } catch (error) { console.error(error) @@ -328,6 +371,21 @@ const ShareChatbot = ({ chatflowid, chatbotConfig }) => { window.open(`${baseURL}/chatbot/${chatflowid}`, '_blank')}> +
+
+ { + setChatflowIsPublic(event.target.checked) + onSwitchChange(event.target.checked) + }} + /> + Make Public + +
{textField(welcomeMessage, 'welcomeMessage', 'Welcome Message', 'string', 'Hello! This is custom welcome message')} {colorField(backgroundColor, 'backgroundColor', 'Background Color')} @@ -412,9 +470,4 @@ const ShareChatbot = ({ chatflowid, chatbotConfig }) => { ) } -ShareChatbot.propTypes = { - chatflowid: PropTypes.string, - chatbotConfig: PropTypes.object -} - export default ShareChatbot