mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 09:00:52 +03:00
add API Configuration UI
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-settings" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"></path>
|
||||
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 896 B |
@@ -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 && <EmbedChat chatflowid={dialogProps.chatflowid} />}
|
||||
{codeLang !== 'Embed' && codeLang !== 'Share Chatbot' && (
|
||||
{codeLang !== 'Embed' && codeLang !== 'Share Chatbot' && codeLang !== 'Configuration' && (
|
||||
<>
|
||||
<CopyBlock
|
||||
theme={atomOneDark}
|
||||
@@ -770,6 +774,7 @@ formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")`
|
||||
{codeLang === 'Share Chatbot' && !chatflowApiKeyId && (
|
||||
<ShareChatbot isSessionMemory={dialogProps.isSessionMemory} />
|
||||
)}
|
||||
{codeLang === 'Configuration' && <Configuration />}
|
||||
</TabPanel>
|
||||
))}
|
||||
</DialogContent>
|
||||
|
||||
@@ -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) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
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) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<Box sx={{ pt: 2, pb: 2 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
|
||||
<Typography sx={{ mb: 1 }}>{fieldLabel}</Typography>
|
||||
<OutlinedInput
|
||||
id={fieldName}
|
||||
type={fieldType}
|
||||
fullWidth
|
||||
value={message}
|
||||
placeholder={placeholder}
|
||||
name={fieldName}
|
||||
onChange={(e) => {
|
||||
onTextChanged(e.target.value, fieldName)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*Rate Limit*/}
|
||||
<Typography variant='h4' sx={{ mb: 1, mt: 2 }}>
|
||||
Rate Limit
|
||||
</Typography>
|
||||
{textField(limitMax, 'Limit Max', 'Max Limit', 'number')}
|
||||
{textField(limitDuration, 'Limit Duration', 'Font Size', 'number')}
|
||||
{textField(limitMsg, 'limitMsg', 'Limit Message', 'string')}
|
||||
|
||||
<StyledButton style={{ marginBottom: 10, marginTop: 10 }} variant='contained' onClick={() => onSave()}>
|
||||
Save Changes
|
||||
</StyledButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Configuration.propTypes = {
|
||||
isSessionMemory: PropTypes.bool
|
||||
}
|
||||
|
||||
export default Configuration
|
||||
Reference in New Issue
Block a user