diff --git a/packages/ui/src/assets/images/settings.svg b/packages/ui/src/assets/images/settings.svg new file mode 100644 index 00000000..4f4dfc09 --- /dev/null +++ b/packages/ui/src/assets/images/settings.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/ui/src/views/chatflows/APICodeDialog.js b/packages/ui/src/views/chatflows/APICodeDialog.js index 0ae8a8f4..49c718cc 100644 --- a/packages/ui/src/views/chatflows/APICodeDialog.js +++ b/packages/ui/src/views/chatflows/APICodeDialog.js @@ -34,6 +34,7 @@ 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' +import settingsSVG from 'assets/images/settings.svg' // API import apiKeyApi from 'api/apikey' @@ -46,6 +47,7 @@ import { CheckboxInput } from 'ui-component/checkbox/Checkbox' import { TableViewOnly } from 'ui-component/table/Table' import { IconBulb } from '@tabler/icons' +import Configuration from './Configuration' function TabPanel(props) { const { children, value, index, ...other } = props @@ -141,7 +143,7 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => { const navigate = useNavigate() const dispatch = useDispatch() - const codes = ['Embed', 'Python', 'JavaScript', 'cURL', 'Share Chatbot'] + const codes = ['Embed', 'Python', 'JavaScript', 'cURL', 'Share Chatbot', 'Configuration'] const [value, setValue] = useState(0) const [keyOptions, setKeyOptions] = useState([]) const [apiKeys, setAPIKeys] = useState([]) @@ -321,6 +323,8 @@ query({"question": "Hey, how are you?"}).then((response) => { return cURLSVG } else if (codeLang === 'Share Chatbot') { return ShareChatbotSVG + } else if (codeLang === 'Configuration') { + return settingsSVG } return pythonSVG } @@ -647,7 +651,7 @@ formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")` )} {codeLang === 'Embed' && !chatflowApiKeyId && } - {codeLang !== 'Embed' && codeLang !== 'Share Chatbot' && ( + {codeLang !== 'Embed' && codeLang !== 'Share Chatbot' && codeLang !== 'Configuration' && ( <> )} + {codeLang === 'Configuration' && } ))} diff --git a/packages/ui/src/views/chatflows/Configuration.js b/packages/ui/src/views/chatflows/Configuration.js new file mode 100644 index 00000000..d5f4bbf9 --- /dev/null +++ b/packages/ui/src/views/chatflows/Configuration.js @@ -0,0 +1,145 @@ +import { useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from 'store/actions' +import PropTypes from 'prop-types' + +import { Box, Typography, Button, OutlinedInput } from '@mui/material' + +// Project import +import { StyledButton } from 'ui-component/button/StyledButton' + +// Icons +import { IconX } from '@tabler/icons' + +// API +import chatflowsApi from 'api/chatflows' + +// utils +import useNotifier from 'utils/useNotifier' + +const Configuration = () => { + const dispatch = useDispatch() + const chatflow = useSelector((state) => state.canvas.chatflow) + const chatflowid = chatflow.id + const apiConfig = chatflow.apiConfig ? JSON.parse(chatflow.apiConfig) : {} + + useNotifier() + + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + + const [limitMax, setLimitMax] = useState(apiConfig?.rateLimit?.limitMax ?? 20) + const [limitDuration, setLimitDuration] = useState(apiConfig?.rateLimit?.limitDuration ?? 120) + const [limitMsg, setLimitMsg] = useState(apiConfig?.rateLimit?.limitMsg ?? "Please don't spam me") + + const formatObj = () => { + const obj = { + rateLimit: {} + } + + if (limitMax && limitDuration && limitMsg) + obj.rateLimit = { + limitMax, + limitDuration, + limitMsg + } + + return obj + } + + const onSave = async () => { + try { + const saveResp = await chatflowsApi.updateChatflow(chatflowid, { + apiConfig: JSON.stringify(formatObj()) + }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'API 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) + const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}` + enqueueSnackbar({ + message: `Failed to save API Configuration: ${errorData}`, + options: { + key: new Date().getTime() + Math.random(), + variant: 'error', + persist: true, + action: (key) => ( + + ) + } + }) + } + } + + const onTextChanged = (value, fieldName) => { + switch (fieldName) { + case 'limitMax': + setLimitMax(value) + break + case 'limitDuration': + setLimitDuration(value) + break + case 'limitMsg': + setLimitMsg(value) + break + } + } + + const textField = (message, fieldName, fieldLabel, fieldType = 'string', placeholder = '') => { + return ( + +
+ {fieldLabel} + { + onTextChanged(e.target.value, fieldName) + }} + /> +
+
+ ) + } + + return ( + <> + {/*Rate Limit*/} + + Rate Limit + + {textField(limitMax, 'Limit Max', 'Max Limit', 'number')} + {textField(limitDuration, 'Limit Duration', 'Font Size', 'number')} + {textField(limitMsg, 'limitMsg', 'Limit Message', 'string')} + + onSave()}> + Save Changes + + + ) +} + +Configuration.propTypes = { + isSessionMemory: PropTypes.bool +} + +export default Configuration