From 0fb1d8a1ff056e1462954d396b40c6a70dbf8b07 Mon Sep 17 00:00:00 2001 From: ivalkshfoeif Date: Fri, 23 Jun 2023 17:57:36 -0700 Subject: [PATCH 01/10] add redis-backed chat memory --- .../RedisBackedChatMemory.ts | 85 +++++++++++++++++++ .../memory/RedisBackedChatMemory/memory.svg | 8 ++ packages/components/package.json | 1 + 3 files changed, 94 insertions(+) create mode 100644 packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts create mode 100644 packages/components/nodes/memory/RedisBackedChatMemory/memory.svg diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts new file mode 100644 index 00000000..155ea5f5 --- /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 = 'memory.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/memory.svg b/packages/components/nodes/memory/RedisBackedChatMemory/memory.svg new file mode 100644 index 00000000..ca8e17da --- /dev/null +++ b/packages/components/nodes/memory/RedisBackedChatMemory/memory.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/components/package.json b/packages/components/package.json index e5e0ba00..12210951 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -42,6 +42,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", From e554ac54dddf54b742eac1234e66957d534e726e Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 26 Jun 2023 02:37:40 +0100 Subject: [PATCH 02/10] add full page chatbot and react chatbot --- packages/server/src/Interface.ts | 1 + packages/server/src/entity/ChatFlow.ts | 3 + packages/ui/craco.config.js | 16 + packages/ui/package.json | 14 +- packages/ui/src/assets/images/sharing.png | Bin 0 -> 17473 bytes packages/ui/src/routes/ChatbotRoutes.js | 23 + packages/ui/src/routes/index.js | 3 +- packages/ui/src/views/canvas/CanvasHeader.js | 5 +- packages/ui/src/views/chatbot/index.js | 59 +++ .../chatflows}/APICodeDialog.js | 204 +++------ packages/ui/src/views/chatflows/EmbedChat.js | 324 ++++++++++++++ .../ui/src/views/chatflows/ShareChatbot.js | 420 ++++++++++++++++++ 12 files changed, 924 insertions(+), 148 deletions(-) create mode 100644 packages/ui/craco.config.js create mode 100644 packages/ui/src/assets/images/sharing.png create mode 100644 packages/ui/src/routes/ChatbotRoutes.js create mode 100644 packages/ui/src/views/chatbot/index.js rename packages/ui/src/{ui-component/dialog => views/chatflows}/APICodeDialog.js (73%) create mode 100644 packages/ui/src/views/chatflows/EmbedChat.js create mode 100644 packages/ui/src/views/chatflows/ShareChatbot.js diff --git a/packages/server/src/Interface.ts b/packages/server/src/Interface.ts index 1eafcae6..9473638f 100644 --- a/packages/server/src/Interface.ts +++ b/packages/server/src/Interface.ts @@ -13,6 +13,7 @@ export interface IChatFlow { deployed: boolean updatedDate: Date createdDate: Date + chatbotConfig?: string } export interface IChatMessage { diff --git a/packages/server/src/entity/ChatFlow.ts b/packages/server/src/entity/ChatFlow.ts index d9b12929..910272ad 100644 --- a/packages/server/src/entity/ChatFlow.ts +++ b/packages/server/src/entity/ChatFlow.ts @@ -19,6 +19,9 @@ export class ChatFlow implements IChatFlow { @Column() deployed: boolean + @Column({ nullable: true }) + chatbotConfig?: string + @CreateDateColumn() createdDate: Date diff --git a/packages/ui/craco.config.js b/packages/ui/craco.config.js new file mode 100644 index 00000000..142305e0 --- /dev/null +++ b/packages/ui/craco.config.js @@ -0,0 +1,16 @@ +module.exports = { + webpack: { + configure: { + module: { + rules: [ + { + test: /\.m?js$/, + resolve: { + fullySpecified: false + } + } + ] + } + } + } +} diff --git a/packages/ui/package.json b/packages/ui/package.json index 258b5471..1e55f1c8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -16,6 +16,8 @@ "@mui/x-data-grid": "^6.8.0", "@tabler/icons": "^1.39.1", "clsx": "^1.1.1", + "flowise-embed": "*", + "flowise-embed-react": "*", "formik": "^2.2.6", "framer-motion": "^4.1.13", "history": "^5.0.0", @@ -27,6 +29,7 @@ "prop-types": "^15.7.2", "react": "^18.2.0", "react-code-blocks": "^0.0.9-0", + "react-color": "^2.19.3", "react-datepicker": "^4.8.0", "react-device-detect": "^1.17.0", "react-dom": "^18.2.0", @@ -47,11 +50,11 @@ "yup": "^0.32.9" }, "scripts": { - "start": "react-scripts start", - "dev": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" + "start": "craco start", + "dev": "craco start", + "build": "craco build", + "test": "craco test", + "eject": "craco eject" }, "babel": { "presets": [ @@ -72,6 +75,7 @@ }, "devDependencies": { "@babel/eslint-parser": "^7.15.8", + "@craco/craco": "^7.1.0", "@testing-library/jest-dom": "^5.11.10", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^12.8.3", diff --git a/packages/ui/src/assets/images/sharing.png b/packages/ui/src/assets/images/sharing.png new file mode 100644 index 0000000000000000000000000000000000000000..1e538f2efd29a6d0d6d5dd2c88ca04aa74fffa36 GIT binary patch literal 17473 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 Jx@Z3G{{f}$cL4wZ literal 0 HcmV?d00001 diff --git a/packages/ui/src/routes/ChatbotRoutes.js b/packages/ui/src/routes/ChatbotRoutes.js new file mode 100644 index 00000000..25d298d6 --- /dev/null +++ b/packages/ui/src/routes/ChatbotRoutes.js @@ -0,0 +1,23 @@ +import { lazy } from 'react' + +// project imports +import Loadable from 'ui-component/loading/Loadable' +import MinimalLayout from 'layout/MinimalLayout' + +// canvas routing +const ChatbotFull = Loadable(lazy(() => import('views/chatbot'))) + +// ==============================|| CANVAS ROUTING ||============================== // + +const ChatbotRoutes = { + path: '/', + element: , + children: [ + { + path: '/chatbot/:id', + element: + } + ] +} + +export default ChatbotRoutes diff --git a/packages/ui/src/routes/index.js b/packages/ui/src/routes/index.js index 15fe4dca..ff8c1920 100644 --- a/packages/ui/src/routes/index.js +++ b/packages/ui/src/routes/index.js @@ -3,10 +3,11 @@ import { useRoutes } from 'react-router-dom' // routes import MainRoutes from './MainRoutes' import CanvasRoutes from './CanvasRoutes' +import ChatbotRoutes from './ChatbotRoutes' import config from 'config' // ==============================|| ROUTING RENDER ||============================== // export default function ThemeRoutes() { - return useRoutes([MainRoutes, CanvasRoutes], config.basename) + return useRoutes([MainRoutes, CanvasRoutes, ChatbotRoutes], config.basename) } diff --git a/packages/ui/src/views/canvas/CanvasHeader.js b/packages/ui/src/views/canvas/CanvasHeader.js index 1f4a1f93..521aa9d3 100644 --- a/packages/ui/src/views/canvas/CanvasHeader.js +++ b/packages/ui/src/views/canvas/CanvasHeader.js @@ -13,7 +13,7 @@ import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, // project imports import Settings from 'views/settings' import SaveChatflowDialog from 'ui-component/dialog/SaveChatflowDialog' -import APICodeDialog from 'ui-component/dialog/APICodeDialog' +import APICodeDialog from 'views/chatflows/APICodeDialog' // API import chatflowsApi from 'api/chatflows' @@ -107,7 +107,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl title: 'Embed in website or use as API', chatflowid: chatflow.id, chatflowApiKeyId: chatflow.apikeyid, - isFormDataRequired + isFormDataRequired, + chatbotConfig: chatflow.chatbotConfig }) setAPIDialogOpen(true) } diff --git a/packages/ui/src/views/chatbot/index.js b/packages/ui/src/views/chatbot/index.js new file mode 100644 index 00000000..b33bec2c --- /dev/null +++ b/packages/ui/src/views/chatbot/index.js @@ -0,0 +1,59 @@ +import { useEffect, useState } from 'react' +import { baseURL } from 'store/constant' +import axios from 'axios' +import { FullPageChat } from 'flowise-embed-react' + +// ==============================|| 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 [chatflow, setChatflow] = useState(null) + const [chatbotTheme, setChatbotTheme] = useState({}) + + 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({}) + } + } + } + fetchData() + })() + }, [chatflowId]) + + return ( + <> + {!chatflow || chatflow.apikeyid ? ( +

Invalid Chatbot

+ ) : ( + + )} + + ) +} + +export default ChatbotFull diff --git a/packages/ui/src/ui-component/dialog/APICodeDialog.js b/packages/ui/src/views/chatflows/APICodeDialog.js similarity index 73% rename from packages/ui/src/ui-component/dialog/APICodeDialog.js rename to packages/ui/src/views/chatflows/APICodeDialog.js index e64f4bf8..fea49909 100644 --- a/packages/ui/src/ui-component/dialog/APICodeDialog.js +++ b/packages/ui/src/views/chatflows/APICodeDialog.js @@ -9,6 +9,8 @@ import { CopyBlock, atomOneDark } from 'react-code-blocks' // Project import import { Dropdown } from 'ui-component/dropdown/Dropdown' +import ShareChatbot from './ShareChatbot' +import EmbedChat from './EmbedChat' // Const import { baseURL } from 'store/constant' @@ -19,6 +21,7 @@ import pythonSVG from 'assets/images/python.svg' import javascriptSVG from 'assets/images/javascript.svg' import cURLSVG from 'assets/images/cURL.svg' import EmbedSVG from 'assets/images/embed.svg' +import ShareChatbotSVG from 'assets/images/sharing.png' // API import apiKeyApi from 'api/apikey' @@ -119,77 +122,19 @@ const getConfigExamplesForCurl = (configData, bodyType) => { return finalStr } -const embedCode = (chatflowid) => { - return `` -} - -const embedCodeCustomization = (chatflowid) => { - return `` -} - const APICodeDialog = ({ show, dialogProps, onCancel }) => { const portalElement = document.getElementById('portal') const navigate = useNavigate() const dispatch = useDispatch() - const codes = ['Embed', 'Python', 'JavaScript', 'cURL'] + + const codes = ['Embed', 'Python', 'JavaScript', 'cURL', 'Share Chatbot'] const [value, setValue] = useState(0) const [keyOptions, setKeyOptions] = useState([]) const [apiKeys, setAPIKeys] = useState([]) const [chatflowApiKeyId, setChatflowApiKeyId] = useState('') const [selectedApiKey, setSelectedApiKey] = useState({}) const [checkboxVal, setCheckbox] = useState(false) - const [embedChatCheckboxVal, setEmbedChatCheckbox] = useState(false) + const [chatbotConfig, setChatbotConfig] = useState(null) const getAllAPIKeysApi = useApi(apiKeyApi.getAllAPIKeys) const updateChatflowApi = useApi(chatflowsApi.updateChatflow) @@ -203,10 +148,6 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => { } } - const onCheckBoxEmbedChatChanged = (newVal) => { - setEmbedChatCheckbox(newVal) - } - const onApiKeySelected = (keyValue) => { if (keyValue === 'addnewkey') { navigate('/apikey') @@ -265,8 +206,6 @@ query({"question": "Hey, how are you?"}).then((response) => { return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ -X POST \\ -d '{"question": "Hey, how are you?"}'` - } else if (codeLang === 'Embed') { - return embedCode(dialogProps.chatflowid) } return '' } @@ -309,8 +248,6 @@ query({"question": "Hey, how are you?"}).then((response) => { -X POST \\ -d '{"question": "Hey, how are you?"}' \\ -H "Authorization: Bearer ${selectedApiKey?.apiKey}"` - } else if (codeLang === 'Embed') { - return embedCode(dialogProps.chatflowid) } return '' } @@ -318,7 +255,7 @@ query({"question": "Hey, how are you?"}).then((response) => { const getLang = (codeLang) => { if (codeLang === 'Python') { return 'python' - } else if (codeLang === 'JavaScript' || codeLang === 'Embed') { + } else if (codeLang === 'JavaScript') { return 'javascript' } else if (codeLang === 'cURL') { return 'bash' @@ -335,6 +272,8 @@ query({"question": "Hey, how are you?"}).then((response) => { return EmbedSVG } else if (codeLang === 'cURL') { return cURLSVG + } else if (codeLang === 'Share Chatbot') { + return ShareChatbotSVG } return pythonSVG } @@ -552,6 +491,12 @@ 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]) @@ -593,92 +538,71 @@ query({ ))} - {value !== 0 && ( -
- onApiKeySelected(newValue)} - value={dialogProps.chatflowApiKeyId ?? chatflowApiKeyId ?? 'Choose an API key'} - /> -
- )} +
+ onApiKeySelected(newValue)} + value={dialogProps.chatflowApiKeyId ?? chatflowApiKeyId ?? 'Choose an API key'} + /> +
{codes.map((codeLang, index) => ( - {value === 0 && ( + {(codeLang === 'Embed' || codeLang === 'Share Chatbot') && chatflowApiKeyId && ( <> - - Paste this anywhere in the {``} tag of your html file. -

- You can also specify a  - - version - - : {`https://cdn.jsdelivr.net/npm/flowise-embed@/dist/web.js`} -

-
-
+

You cannot use API key while embedding/sharing chatbot.

+

+ Please select "No Authorization" from the dropdown at the top right corner. +

)} - - {value !== 0 && } - {value !== 0 && checkboxVal && getConfigApi.data && getConfigApi.data.length > 0 && ( + {codeLang === 'Embed' && !chatflowApiKeyId && } + {codeLang !== 'Embed' && codeLang !== 'Share Chatbot' && ( <> - + + {checkboxVal && getConfigApi.data && getConfigApi.data.length > 0 && ( + <> + + + + )} + {getIsChatflowStreamingApi.data?.isStreaming && ( +

+ Read  + + here + +  on how to stream response back to application +

+ )} )} - {value === 0 && ( - - )} - {value === 0 && embedChatCheckboxVal && ( - - )} - {value !== 0 && getIsChatflowStreamingApi.data?.isStreaming && ( -

- Read  - - here - -  on how to stream response back to application -

+ {codeLang === 'Share Chatbot' && !chatflowApiKeyId && ( + )}
))} diff --git a/packages/ui/src/views/chatflows/EmbedChat.js b/packages/ui/src/views/chatflows/EmbedChat.js new file mode 100644 index 00000000..c6385efb --- /dev/null +++ b/packages/ui/src/views/chatflows/EmbedChat.js @@ -0,0 +1,324 @@ +import { useState } from 'react' +import PropTypes from 'prop-types' + +import { Tabs, Tab, Box } from '@mui/material' +import { CopyBlock, atomOneDark } from 'react-code-blocks' + +// Project import +import { CheckboxInput } from 'ui-component/checkbox/Checkbox' + +// Const +import { baseURL } from 'store/constant' + +function TabPanel(props) { + const { children, value, index, ...other } = props + return ( + + ) +} + +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.number.isRequired, + value: PropTypes.number.isRequired +} + +function a11yProps(index) { + return { + id: `attachment-tab-${index}`, + 'aria-controls': `attachment-tabpanel-${index}` + } +} + +const embedPopupHtmlCode = (chatflowid) => { + return `` +} + +const embedPopupReactCode = (chatflowid) => { + return `import { BubbleChat } from 'flowise-embed-react' + +const App = () => { + return ( + + ); +};` +} + +const embedFullpageHtmlCode = (chatflowid) => { + return ` +` +} + +const embedFullpageReactCode = (chatflowid) => { + return `import { FullPageChat } from "flowise-embed-react" + +const App = () => { + return ( + + ); +};` +} + +const buttonConfig = (isReact = false) => { + return isReact + ? `button: { + backgroundColor: "#3B81F6", + right: 20, + bottom: 20, + size: "medium", + iconColor: "white", + customIconSrc: "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/svg/google-messages.svg", + }` + : `button: { + backgroundColor: "#3B81F6", + right: 20, + bottom: 20, + size: "medium", + iconColor: "white", + customIconSrc: "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/svg/google-messages.svg", + }` +} + +const chatwindowConfig = (isReact = false) => { + return isReact + ? `chatWindow: { + welcomeMessage: "Hello! This is custom welcome message", + backgroundColor: "#ffffff", + height: 700, + width: 400, + fontSize: 16, + poweredByTextColor: "#303235", + botMessage: { + backgroundColor: "#f7f8ff", + textColor: "#303235", + showAvatar: true, + avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png", + }, + userMessage: { + backgroundColor: "#3B81F6", + textColor: "#ffffff", + showAvatar: true, + avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png", + }, + textInput: { + placeholder: "Type your question", + backgroundColor: "#ffffff", + textColor: "#303235", + sendButtonColor: "#3B81F6", + } + }` + : `chatWindow: { + welcomeMessage: "Hello! This is custom welcome message", + backgroundColor: "#ffffff", + height: 700, + width: 400, + fontSize: 16, + poweredByTextColor: "#303235", + botMessage: { + backgroundColor: "#f7f8ff", + textColor: "#303235", + showAvatar: true, + avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png", + }, + userMessage: { + backgroundColor: "#3B81F6", + textColor: "#ffffff", + showAvatar: true, + avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png", + }, + textInput: { + placeholder: "Type your question", + backgroundColor: "#ffffff", + textColor: "#303235", + sendButtonColor: "#3B81F6", + } + }` +} + +const embedPopupHtmlCodeCustomization = (chatflowid) => { + return `` +} + +const embedPopupReactCodeCustomization = (chatflowid) => { + return `import { BubbleChat } from 'flowise-embed-react' + +const App = () => { + return ( + + ); +};` +} + +const embedFullpageHtmlCodeCustomization = (chatflowid) => { + return ` +` +} + +const embedFullpageReactCodeCustomization = (chatflowid) => { + return `import { FullPageChat } from "flowise-embed-react" + +const App = () => { + return ( + + ); +};` +} + +const EmbedChat = ({ chatflowid }) => { + const codes = ['Popup Html', 'Fullpage Html', 'Popup React', 'Fullpage React'] + const [value, setValue] = useState(0) + const [embedChatCheckboxVal, setEmbedChatCheckbox] = useState(false) + + const onCheckBoxEmbedChatChanged = (newVal) => { + setEmbedChatCheckbox(newVal) + } + + const handleChange = (event, newValue) => { + setValue(newValue) + } + + const getCode = (codeLang) => { + switch (codeLang) { + case 'Popup Html': + return embedPopupHtmlCode(chatflowid) + case 'Fullpage Html': + return embedFullpageHtmlCode(chatflowid) + case 'Popup React': + return embedPopupReactCode(chatflowid) + case 'Fullpage React': + return embedFullpageReactCode(chatflowid) + default: + return '' + } + } + + const getCodeCustomization = (codeLang) => { + switch (codeLang) { + case 'Popup Html': + return embedPopupHtmlCodeCustomization(chatflowid) + case 'Fullpage Html': + return embedFullpageHtmlCodeCustomization(chatflowid) + case 'Popup React': + return embedPopupReactCodeCustomization(chatflowid) + case 'Fullpage React': + return embedFullpageReactCodeCustomization(chatflowid) + default: + return '' + } + } + + return ( + <> +
+
+ + {codes.map((codeLang, index) => ( + + ))} + +
+
+
+ {codes.map((codeLang, index) => ( + + {(value === 0 || value === 1) && ( + <> + + Paste this anywhere in the {``} tag of your html file. +

+ You can also specify a  + + version + + : {`https://cdn.jsdelivr.net/npm/flowise-embed@/dist/web.js`} +

+
+
+ + )} + + + + + {embedChatCheckboxVal && ( + + )} +
+ ))} + + ) +} + +EmbedChat.propTypes = { + chatflowid: PropTypes.string +} + +export default EmbedChat diff --git a/packages/ui/src/views/chatflows/ShareChatbot.js b/packages/ui/src/views/chatflows/ShareChatbot.js new file mode 100644 index 00000000..dffecf5b --- /dev/null +++ b/packages/ui/src/views/chatflows/ShareChatbot.js @@ -0,0 +1,420 @@ +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 { SketchPicker } from 'react-color' + +import { Box, Typography, Button, Switch, OutlinedInput, Popover, Stack, IconButton } from '@mui/material' +import { useTheme } from '@mui/material/styles' + +// Project import +import { StyledButton } from 'ui-component/button/StyledButton' + +// Icons +import { IconX, IconCopy, IconArrowUpRightCircle } from '@tabler/icons' + +// API +import chatflowsApi from 'api/chatflows' + +// utils +import useNotifier from 'utils/useNotifier' + +// Const +import { baseURL } from 'store/constant' + +const defaultConfig = { + backgroundColor: '#ffffff', + fontSize: 16, + poweredByTextColor: '#303235', + botMessage: { + backgroundColor: '#f7f8ff', + textColor: '#303235' + }, + userMessage: { + backgroundColor: '#3B81F6', + textColor: '#ffffff' + }, + textInput: { + backgroundColor: '#ffffff', + textColor: '#303235', + sendButtonColor: '#3B81F6' + } +} + +const ShareChatbot = ({ chatflowid, chatbotConfig }) => { + const dispatch = useDispatch() + const theme = useTheme() + + useNotifier() + + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + + const [welcomeMessage, setWelcomeMessage] = useState(chatbotConfig?.welcomeMessage ?? '') + const [backgroundColor, setBackgroundColor] = useState(chatbotConfig?.backgroundColor ?? defaultConfig.backgroundColor) + const [fontSize, setFontSize] = useState(chatbotConfig?.fontSize ?? defaultConfig.fontSize) + const [poweredByTextColor, setPoweredByTextColor] = useState(chatbotConfig?.poweredByTextColor ?? defaultConfig.poweredByTextColor) + + const [botMessageBackgroundColor, setBotMessageBackgroundColor] = useState( + chatbotConfig?.botMessage?.backgroundColor ?? defaultConfig.botMessage.backgroundColor + ) + const [botMessageTextColor, setBotMessageTextColor] = useState( + chatbotConfig?.botMessage?.textColor ?? defaultConfig.botMessage.textColor + ) + const [botMessageAvatarSrc, setBotMessageAvatarSrc] = useState(chatbotConfig?.botMessage?.avatarSrc ?? '') + const [botMessageShowAvatar, setBotMessageShowAvatar] = useState(chatbotConfig?.botMessage?.showAvatar ?? false) + + const [userMessageBackgroundColor, setUserMessageBackgroundColor] = useState( + chatbotConfig?.userMessage?.backgroundColor ?? defaultConfig.userMessage.backgroundColor + ) + const [userMessageTextColor, setUserMessageTextColor] = useState( + chatbotConfig?.userMessage?.textColor ?? defaultConfig.userMessage.textColor + ) + const [userMessageAvatarSrc, setUserMessageAvatarSrc] = useState(chatbotConfig?.userMessage?.avatarSrc ?? '') + const [userMessageShowAvatar, setUserMessageShowAvatar] = useState(chatbotConfig?.userMessage?.showAvatar ?? false) + + const [textInputBackgroundColor, setTextInputBackgroundColor] = useState( + chatbotConfig?.textInput?.backgroundColor ?? defaultConfig.textInput.backgroundColor + ) + const [textInputTextColor, setTextInputTextColor] = useState(chatbotConfig?.textInput?.textColor ?? defaultConfig.textInput.textColor) + const [textInputPlaceholder, setTextInputPlaceholder] = useState(chatbotConfig?.textInput?.placeholder ?? '') + const [textInputSendButtonColor, setTextInputSendButtonColor] = useState( + chatbotConfig?.textInput?.sendButtonColor ?? defaultConfig.textInput.sendButtonColor + ) + + const [colorAnchorEl, setColorAnchorEl] = useState(null) + const [selectedColorConfig, setSelectedColorConfig] = useState('') + const [sketchPickerColor, setSketchPickerColor] = useState('') + const openColorPopOver = Boolean(colorAnchorEl) + + const [copyAnchorEl, setCopyAnchorEl] = useState(null) + const openCopyPopOver = Boolean(copyAnchorEl) + + const formatObj = () => { + const obj = { + botMessage: { + showAvatar: false + }, + userMessage: { + showAvatar: false + }, + textInput: {} + } + if (welcomeMessage) obj.welcomeMessage = welcomeMessage + if (backgroundColor) obj.backgroundColor = backgroundColor + if (fontSize) obj.fontSize = fontSize + if (poweredByTextColor) obj.poweredByTextColor = poweredByTextColor + + if (botMessageBackgroundColor) obj.botMessage.backgroundColor = botMessageBackgroundColor + if (botMessageTextColor) obj.botMessage.textColor = botMessageTextColor + if (botMessageAvatarSrc) obj.botMessage.avatarSrc = botMessageAvatarSrc + if (botMessageShowAvatar) obj.botMessage.showAvatar = botMessageShowAvatar + + if (userMessageBackgroundColor) obj.userMessage.backgroundColor = userMessageBackgroundColor + if (userMessageTextColor) obj.userMessage.textColor = userMessageTextColor + if (userMessageAvatarSrc) obj.userMessage.avatarSrc = userMessageAvatarSrc + if (userMessageShowAvatar) obj.userMessage.showAvatar = userMessageShowAvatar + + if (textInputBackgroundColor) obj.textInput.backgroundColor = textInputBackgroundColor + if (textInputTextColor) obj.textInput.textColor = textInputTextColor + if (textInputPlaceholder) obj.textInput.placeholder = textInputPlaceholder + if (textInputSendButtonColor) obj.textInput.sendButtonColor = textInputSendButtonColor + + return obj + } + + const onSave = async () => { + try { + const saveResp = await chatflowsApi.updateChatflow(chatflowid, { + chatbotConfig: JSON.stringify(formatObj()) + }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'Chatbot Configuration Saved', + options: { + key: new Date().getTime() + Math.random(), + variant: 'success', + action: (key) => ( + + ) + } + }) + } + } 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 handleClosePopOver = () => { + setColorAnchorEl(null) + } + + const handleCloseCopyPopOver = () => { + setCopyAnchorEl(null) + } + + const onColorSelected = (hexColor) => { + switch (selectedColorConfig) { + case 'backgroundColor': + setBackgroundColor(hexColor) + break + case 'poweredByTextColor': + setPoweredByTextColor(hexColor) + break + case 'botMessageBackgroundColor': + setBotMessageBackgroundColor(hexColor) + break + case 'botMessageTextColor': + setBotMessageTextColor(hexColor) + break + case 'userMessageBackgroundColor': + setUserMessageBackgroundColor(hexColor) + break + case 'userMessageTextColor': + setUserMessageTextColor(hexColor) + break + case 'textInputBackgroundColor': + setTextInputBackgroundColor(hexColor) + break + case 'textInputTextColor': + setTextInputTextColor(hexColor) + break + case 'textInputSendButtonColor': + setTextInputSendButtonColor(hexColor) + break + } + setSketchPickerColor(hexColor) + } + + const onTextChanged = (value, fieldName) => { + switch (fieldName) { + case 'welcomeMessage': + setWelcomeMessage(value) + break + case 'fontSize': + setFontSize(value) + break + case 'botMessageAvatarSrc': + setBotMessageAvatarSrc(value) + break + case 'userMessageAvatarSrc': + setUserMessageAvatarSrc(value) + break + case 'textInputPlaceholder': + setTextInputPlaceholder(value) + break + } + } + + const onBooleanChanged = (value, fieldName) => { + switch (fieldName) { + case 'botMessageShowAvatar': + setBotMessageShowAvatar(value) + break + case 'userMessageShowAvatar': + setUserMessageShowAvatar(value) + break + } + } + + const colorField = (color, fieldName, fieldLabel) => { + return ( + +
+ {fieldLabel} + { + setSelectedColorConfig(fieldName) + setSketchPickerColor(color ?? '#ffffff') + setColorAnchorEl(event.currentTarget) + }} + > +
+
+ ) + } + + const booleanField = (value, fieldName, fieldLabel) => { + return ( + +
+ {fieldLabel} + { + onBooleanChanged(event.target.checked, fieldName) + }} + /> +
+
+ ) + } + + const textField = (message, fieldName, fieldLabel, fieldType = 'string', placeholder = '') => { + return ( + +
+ {fieldLabel} + { + onTextChanged(e.target.value, fieldName) + }} + /> +
+
+ ) + } + + return ( + <> + + + {`${baseURL}/chatbot/${chatflowid}`} + + { + navigator.clipboard.writeText(`${baseURL}/chatbot/${chatflowid}`) + setCopyAnchorEl(event.currentTarget) + setTimeout(() => { + handleCloseCopyPopOver() + }, 1500) + }} + > + + + window.open(`${baseURL}/chatbot/${chatflowid}`, '_blank')}> + + + + {textField(welcomeMessage, 'welcomeMessage', 'Welcome Message', 'string', 'Hello! This is custom welcome message')} + {colorField(backgroundColor, 'backgroundColor', 'Background Color')} + {textField(fontSize, 'fontSize', 'Font Size', 'number')} + {colorField(poweredByTextColor, 'poweredByTextColor', 'PoweredBy TextColor')} + + {/*BOT Message*/} + + Bot Message + + {colorField(botMessageBackgroundColor, 'botMessageBackgroundColor', 'Background Color')} + {colorField(botMessageTextColor, 'botMessageTextColor', 'Text Color')} + {textField( + botMessageAvatarSrc, + 'botMessageAvatarSrc', + 'Avatar Link', + 'string', + `https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png` + )} + {booleanField(botMessageShowAvatar, 'botMessageShowAvatar', 'Show Avatar')} + + {/*USER Message*/} + + User Message + + {colorField(userMessageBackgroundColor, 'userMessageBackgroundColor', 'Background Color')} + {colorField(userMessageTextColor, 'userMessageTextColor', 'Text Color')} + {textField( + userMessageAvatarSrc, + 'userMessageAvatarSrc', + 'Avatar Link', + 'string', + `https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png` + )} + {booleanField(userMessageShowAvatar, 'userMessageShowAvatar', 'Show Avatar')} + + {/*TEXT Input*/} + + Text Input + + {colorField(textInputBackgroundColor, 'textInputBackgroundColor', 'Background Color')} + {colorField(textInputTextColor, 'textInputTextColor', 'Text Color')} + {textField(textInputPlaceholder, 'textInputPlaceholder', 'TextInput Placeholder', 'string', `Type question..`)} + {colorField(textInputSendButtonColor, 'textInputSendButtonColor', 'TextIntput Send Button Color')} + + onSave()}> + Save Changes + + + onColorSelected(color.hex)} /> + + + + Copied! + + + + ) +} + +ShareChatbot.propTypes = { + chatflowid: PropTypes.string, + chatbotConfig: PropTypes.object +} + +export default ShareChatbot From 3784a700b6ddf9c8c9c22d69f3d72ff5dafb02b9 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 26 Jun 2023 18:05:48 +0100 Subject: [PATCH 03/10] update faiss version --- packages/components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/package.json b/packages/components/package.json index f5b67cc7..72feeed5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,7 +30,7 @@ "d3-dsv": "2", "dotenv": "^16.0.0", "express": "^4.17.3", - "faiss-node": "^0.2.1", + "faiss-node": "^0.2.2", "form-data": "^4.0.0", "graphql": "^16.6.0", "html-to-text": "^9.0.5", From a0ddad8e52352ff9bcfc4c9880b1f916bb7b4c55 Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 26 Jun 2023 19:26:13 +0100 Subject: [PATCH 04/10] update README --- README.md | 4 ++-- packages/server/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dbce8f3b..90f2711f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Flowise - LangchainJS UI +# Flowise - Low-Code LLM apps builder -Drag & drop UI to build your customized LLM flow using [LangchainJS](https://github.com/hwchase17/langchainjs) +Drag & drop UI to build your customized LLM flow ## ⚡Quick Start diff --git a/packages/server/README.md b/packages/server/README.md index 74ba9a25..7895bd90 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -1,10 +1,10 @@ -# Flowise - LangchainJS UI +# Flowise - Low-Code LLM apps builder ![Flowise](https://github.com/FlowiseAI/Flowise/blob/main/images/flowise.gif?raw=true) -Drag & drop UI to build your customized LLM flow using [LangchainJS](https://github.com/hwchase17/langchainjs) +Drag & drop UI to build your customized LLM flow ## ⚡Quick Start From dd8b59abb8a3850eef2e9b5f78db70a59eb55ec7 Mon Sep 17 00:00:00 2001 From: chungyau97 Date: Tue, 27 Jun 2023 23:01:09 +0800 Subject: [PATCH 05/10] add zapier integration --- packages/server/src/index.ts | 32 +++++++++++++++++++++++++++++- packages/server/src/utils/index.ts | 12 +++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 65bfef23..3e729464 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -36,7 +36,8 @@ import { replaceAllAPIKeys, isFlowValidForStream, isVectorStoreFaiss, - databaseEntities + databaseEntities, + getApiKey } from './utils' import { cloneDeep } from 'lodash' import { getDataSource } from './DataSource' @@ -177,6 +178,24 @@ export class App { return res.json(chatflows) }) + // Get specific chatflow via api key + this.app.get('/api/v1/chatflows/apikey/:apiKey', async (req: Request, res: Response) => { + try { + const apiKey = await getApiKey(req.params.apiKey) + if (!apiKey) return res.status(401).send('Unauthorized') + const chatflows = await this.AppDataSource.getRepository(ChatFlow) + .createQueryBuilder('cf') + .where('cf.apikeyid = :apikeyid', { apikeyid: apiKey.id }) + .orWhere('cf.apikeyid IS NULL') + .orderBy('cf.name', 'ASC') + .getMany() + if (chatflows.length >= 1) return res.status(200).send(chatflows) + return res.status(404).send('Chatflow not found') + } catch (err: any) { + return res.status(500).send(err?.message) + } + }) + // Get specific chatflow via id this.app.get('/api/v1/chatflows/:id', async (req: Request, res: Response) => { const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({ @@ -472,6 +491,17 @@ export class App { return res.json(keys) }) + // Verify api key + this.app.get('/api/v1/apikey/:apiKey', async (req: Request, res: Response) => { + try { + const apiKey = await getApiKey(req.params.apiKey) + if (!apiKey) return res.status(401).send('Unauthorized') + return res.status(200).send('OK') + } catch (err: any) { + return res.status(500).send(err?.message) + } + }) + // ---------------------------------------- // Serve UI static // ---------------------------------------- diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts index e3005c7b..e861e6fa 100644 --- a/packages/server/src/utils/index.ts +++ b/packages/server/src/utils/index.ts @@ -547,6 +547,18 @@ export const addAPIKey = async (keyName: string): Promise => { return content } +/** + * Get API Key details + * @param {string} apiKey + * @returns {Promise} + */ +export const getApiKey = async (apiKey: string) => { + const existingAPIKeys = await getAPIKeys() + const keyIndex = existingAPIKeys.findIndex((key) => key.apiKey === apiKey) + if (keyIndex < 0) return undefined + return existingAPIKeys[keyIndex] +} + /** * Update existing API key * @param {string} keyIdToUpdate From b6a5cd0cb327e9b380f62b61b790632cbdaf8a2b Mon Sep 17 00:00:00 2001 From: chungyau97 Date: Tue, 27 Jun 2023 23:07:31 +0800 Subject: [PATCH 06/10] remove returnJSONStr function --- .../nodes/prompts/PromptTemplate/PromptTemplate.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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) From c36489f9470aa81b92ec89d51b18f1e85f1da80c Mon Sep 17 00:00:00 2001 From: ivalkshfoeif Date: Wed, 28 Jun 2023 10:10:12 -0700 Subject: [PATCH 07/10] Update redis icon --- .../memory/RedisBackedChatMemory/RedisBackedChatMemory.ts | 2 +- .../nodes/memory/RedisBackedChatMemory/memory.svg | 8 -------- .../nodes/memory/RedisBackedChatMemory/redis.svg | 1 + 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 packages/components/nodes/memory/RedisBackedChatMemory/memory.svg create mode 100644 packages/components/nodes/memory/RedisBackedChatMemory/redis.svg diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts index 155ea5f5..e332181d 100644 --- a/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts +++ b/packages/components/nodes/memory/RedisBackedChatMemory/RedisBackedChatMemory.ts @@ -19,7 +19,7 @@ class RedisBackedChatMemory_Memory implements INode { this.label = 'Redis-Backed Chat Memory' this.name = 'RedisBackedChatMemory' this.type = 'RedisBackedChatMemory' - this.icon = 'memory.svg' + 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)] diff --git a/packages/components/nodes/memory/RedisBackedChatMemory/memory.svg b/packages/components/nodes/memory/RedisBackedChatMemory/memory.svg deleted file mode 100644 index ca8e17da..00000000 --- a/packages/components/nodes/memory/RedisBackedChatMemory/memory.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file 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 From 7a8db1dff31572526a640e2b046c3f947f213458 Mon Sep 17 00:00:00 2001 From: Jeffrey <944688482@qq.com> Date: Thu, 29 Jun 2023 14:21:44 +0800 Subject: [PATCH 08/10] fix: temperature should convert to float --- packages/components/nodes/chatmodels/ChatOpenAI/ChatOpenAI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 184a783847c0b7388c48f06c863a94ccca8a37e5 Mon Sep 17 00:00:00 2001 From: Jeffrey-Wang Date: Thu, 29 Jun 2023 21:23:21 +0800 Subject: [PATCH 09/10] fix: temperature convert to float --- .../nodes/chatmodels/AzureChatOpenAI/AzureChatOpenAI.ts | 2 +- .../components/nodes/chatmodels/ChatAnthropic/ChatAnthropic.ts | 2 +- .../nodes/chatmodels/ChatHuggingFace/ChatHuggingFace.ts | 2 +- packages/components/nodes/chatmodels/ChatLocalAI/ChatLocalAI.ts | 2 +- packages/components/nodes/llms/Azure OpenAI/AzureOpenAI.ts | 2 +- packages/components/nodes/llms/Cohere/Cohere.ts | 2 +- .../nodes/llms/HuggingFaceInference/HuggingFaceInference.ts | 2 +- packages/components/nodes/llms/OpenAI/OpenAI.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) 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/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 From 7141401e265a7dfa8261970c430470683697eeeb Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 29 Jun 2023 23:47:20 +0100 Subject: [PATCH 10/10] fix bug where share chatflow is not able to open on any other browser --- packages/server/.env.example | 1 + packages/server/src/Interface.ts | 10 +- packages/server/src/entity/ChatFlow.ts | 8 +- packages/server/src/entity/ChatMessage.ts | 2 +- packages/server/src/entity/Tool.ts | 4 +- packages/server/src/index.ts | 12 +- packages/server/src/utils/index.ts | 2 +- packages/ui/src/api/chatflows.js | 3 + packages/ui/src/views/canvas/CanvasHeader.js | 8 +- packages/ui/src/views/canvas/index.js | 2 +- packages/ui/src/views/chatbot/index.js | 122 ++++++++++++------ .../ui/src/views/chatflows/APICodeDialog.js | 11 +- .../ui/src/views/chatflows/ShareChatbot.js | 71 ++++++++-- 13 files changed, 183 insertions(+), 73 deletions(-) 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 65bfef23..e13934b2 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -92,7 +92,7 @@ export class App { const basicAuthMiddleware = basicAuth({ users: { [username]: password } }) - const whitelistURLs = ['/api/v1/prediction/', '/api/v1/node-icon/', '/api/v1/chatflows-streaming'] + const whitelistURLs = ['/api/v1/public-chatflows', '/api/v1/prediction/', '/api/v1/node-icon/', '/api/v1/chatflows-streaming'] this.app.use((req, res, next) => { if (req.url.includes('/api/v1/')) { whitelistURLs.some((url) => req.url.includes(url)) ? next() : basicAuthMiddleware(req, res, next) @@ -186,6 +186,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 e3005c7b..3601c77d 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