mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 01:01:11 +03:00
Remove unused dialog files
This commit is contained in:
@@ -1,220 +0,0 @@
|
|||||||
import { createPortal } from 'react-dom'
|
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
OutlinedInput,
|
|
||||||
DialogTitle,
|
|
||||||
DialogActions,
|
|
||||||
Box,
|
|
||||||
List,
|
|
||||||
InputAdornment
|
|
||||||
} from '@mui/material'
|
|
||||||
import { IconX, IconTrash, IconPlus } from '@tabler/icons'
|
|
||||||
|
|
||||||
// Project import
|
|
||||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
|
||||||
|
|
||||||
// store
|
|
||||||
import {
|
|
||||||
enqueueSnackbar as enqueueSnackbarAction,
|
|
||||||
closeSnackbar as closeSnackbarAction,
|
|
||||||
SET_CHATFLOW,
|
|
||||||
HIDE_CANVAS_DIALOG,
|
|
||||||
SHOW_CANVAS_DIALOG
|
|
||||||
} from '@/store/actions'
|
|
||||||
import useNotifier from '@/utils/useNotifier'
|
|
||||||
|
|
||||||
// API
|
|
||||||
import chatflowsApi from '@/api/chatflows'
|
|
||||||
|
|
||||||
const AllowedDomainsDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
|
||||||
const portalElement = document.getElementById('portal')
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
useNotifier()
|
|
||||||
|
|
||||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
|
||||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
|
||||||
|
|
||||||
const [inputFields, setInputFields] = useState([''])
|
|
||||||
|
|
||||||
const [chatbotConfig, setChatbotConfig] = useState({})
|
|
||||||
|
|
||||||
const addInputField = () => {
|
|
||||||
setInputFields([...inputFields, ''])
|
|
||||||
}
|
|
||||||
const removeInputFields = (index) => {
|
|
||||||
const rows = [...inputFields]
|
|
||||||
rows.splice(index, 1)
|
|
||||||
setInputFields(rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleChange = (index, evnt) => {
|
|
||||||
const { value } = evnt.target
|
|
||||||
const list = [...inputFields]
|
|
||||||
list[index] = value
|
|
||||||
setInputFields(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSave = async () => {
|
|
||||||
try {
|
|
||||||
let value = {
|
|
||||||
allowedOrigins: [...inputFields]
|
|
||||||
}
|
|
||||||
chatbotConfig.allowedOrigins = value.allowedOrigins
|
|
||||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
|
||||||
chatbotConfig: JSON.stringify(chatbotConfig)
|
|
||||||
})
|
|
||||||
if (saveResp.data) {
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: 'Allowed Origins 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 })
|
|
||||||
}
|
|
||||||
onConfirm()
|
|
||||||
} catch (error) {
|
|
||||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: `Failed to save Allowed Origins: ${errorData}`,
|
|
||||||
options: {
|
|
||||||
key: new Date().getTime() + Math.random(),
|
|
||||||
variant: 'error',
|
|
||||||
persist: true,
|
|
||||||
action: (key) => (
|
|
||||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
|
||||||
<IconX />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) {
|
|
||||||
try {
|
|
||||||
let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig)
|
|
||||||
setChatbotConfig(chatbotConfig || {})
|
|
||||||
if (chatbotConfig.allowedOrigins) {
|
|
||||||
let inputFields = [...chatbotConfig.allowedOrigins]
|
|
||||||
setInputFields(inputFields)
|
|
||||||
} else {
|
|
||||||
setInputFields([''])
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setInputFields([''])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {}
|
|
||||||
}, [dialogProps])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
|
||||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
}, [show, dispatch])
|
|
||||||
|
|
||||||
const component = show ? (
|
|
||||||
<Dialog
|
|
||||||
onClose={onCancel}
|
|
||||||
open={show}
|
|
||||||
fullWidth
|
|
||||||
maxWidth='sm'
|
|
||||||
aria-labelledby='alert-dialog-title'
|
|
||||||
aria-describedby='alert-dialog-description'
|
|
||||||
>
|
|
||||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
|
||||||
{dialogProps.title || 'Allowed Origins'}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>Your chatbot will only work when used from the following domains.</span>
|
|
||||||
</div>
|
|
||||||
<Box sx={{ '& > :not(style)': { m: 1 }, pt: 2 }}>
|
|
||||||
<List>
|
|
||||||
{inputFields.map((origin, index) => {
|
|
||||||
return (
|
|
||||||
<div key={index} style={{ display: 'flex', width: '100%' }}>
|
|
||||||
<Box sx={{ width: '100%', mb: 1 }}>
|
|
||||||
<OutlinedInput
|
|
||||||
sx={{ width: '100%' }}
|
|
||||||
key={index}
|
|
||||||
type='text'
|
|
||||||
onChange={(e) => handleChange(index, e)}
|
|
||||||
size='small'
|
|
||||||
value={origin}
|
|
||||||
name='origin'
|
|
||||||
placeholder='https://example.com'
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position='end' sx={{ padding: '2px' }}>
|
|
||||||
{inputFields.length > 1 && (
|
|
||||||
<IconButton
|
|
||||||
sx={{ height: 30, width: 30 }}
|
|
||||||
size='small'
|
|
||||||
color='error'
|
|
||||||
disabled={inputFields.length === 1}
|
|
||||||
onClick={() => removeInputFields(index)}
|
|
||||||
edge='end'
|
|
||||||
>
|
|
||||||
<IconTrash />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ width: '5%', mb: 1 }}>
|
|
||||||
{index === inputFields.length - 1 && (
|
|
||||||
<IconButton color='primary' onClick={addInputField}>
|
|
||||||
<IconPlus />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onCancel}>Cancel</Button>
|
|
||||||
<StyledButton variant='contained' onClick={onSave}>
|
|
||||||
Save
|
|
||||||
</StyledButton>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
return createPortal(component, portalElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
AllowedDomainsDialog.propTypes = {
|
|
||||||
show: PropTypes.bool,
|
|
||||||
dialogProps: PropTypes.object,
|
|
||||||
onCancel: PropTypes.func,
|
|
||||||
onConfirm: PropTypes.func
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AllowedDomainsDialog
|
|
||||||
@@ -1,358 +0,0 @@
|
|||||||
import { createPortal } from 'react-dom'
|
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import {
|
|
||||||
Typography,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
DialogActions,
|
|
||||||
Accordion,
|
|
||||||
AccordionSummary,
|
|
||||||
AccordionDetails,
|
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText
|
|
||||||
} from '@mui/material'
|
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
||||||
import { IconX } from '@tabler/icons'
|
|
||||||
|
|
||||||
// Project import
|
|
||||||
import CredentialInputHandler from '@/views/canvas/CredentialInputHandler'
|
|
||||||
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
|
|
||||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
|
||||||
import { Input } from '@/ui-component/input/Input'
|
|
||||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
|
||||||
import langsmithPNG from '@/assets/images/langchain.png'
|
|
||||||
import langfuseSVG from '@/assets/images/langfuse.svg'
|
|
||||||
import lunarySVG from '@/assets/images/lunary.svg'
|
|
||||||
|
|
||||||
// store
|
|
||||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
|
||||||
import useNotifier from '@/utils/useNotifier'
|
|
||||||
|
|
||||||
// API
|
|
||||||
import chatflowsApi from '@/api/chatflows'
|
|
||||||
|
|
||||||
const analyticProviders = [
|
|
||||||
{
|
|
||||||
label: 'LangSmith',
|
|
||||||
name: 'langSmith',
|
|
||||||
icon: langsmithPNG,
|
|
||||||
url: 'https://smith.langchain.com',
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
label: 'Connect Credential',
|
|
||||||
name: 'credential',
|
|
||||||
type: 'credential',
|
|
||||||
credentialNames: ['langsmithApi']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Project Name',
|
|
||||||
name: 'projectName',
|
|
||||||
type: 'string',
|
|
||||||
optional: true,
|
|
||||||
description: 'If not provided, default will be used',
|
|
||||||
placeholder: 'default'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'On/Off',
|
|
||||||
name: 'status',
|
|
||||||
type: 'boolean',
|
|
||||||
optional: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'LangFuse',
|
|
||||||
name: 'langFuse',
|
|
||||||
icon: langfuseSVG,
|
|
||||||
url: 'https://langfuse.com',
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
label: 'Connect Credential',
|
|
||||||
name: 'credential',
|
|
||||||
type: 'credential',
|
|
||||||
credentialNames: ['langfuseApi']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Release',
|
|
||||||
name: 'release',
|
|
||||||
type: 'string',
|
|
||||||
optional: true,
|
|
||||||
description: 'The release number/hash of the application to provide analytics grouped by release'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'On/Off',
|
|
||||||
name: 'status',
|
|
||||||
type: 'boolean',
|
|
||||||
optional: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Lunary',
|
|
||||||
name: 'lunary',
|
|
||||||
icon: lunarySVG,
|
|
||||||
url: 'https://lunary.ai',
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
label: 'Connect Credential',
|
|
||||||
name: 'credential',
|
|
||||||
type: 'credential',
|
|
||||||
credentialNames: ['lunaryApi']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'On/Off',
|
|
||||||
name: 'status',
|
|
||||||
type: 'boolean',
|
|
||||||
optional: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const AnalyseFlowDialog = ({ show, dialogProps, onCancel }) => {
|
|
||||||
const portalElement = document.getElementById('portal')
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
useNotifier()
|
|
||||||
|
|
||||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
|
||||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
|
||||||
|
|
||||||
const [analytic, setAnalytic] = useState({})
|
|
||||||
const [providerExpanded, setProviderExpanded] = useState({})
|
|
||||||
|
|
||||||
const onSave = async () => {
|
|
||||||
try {
|
|
||||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
|
||||||
analytic: JSON.stringify(analytic)
|
|
||||||
})
|
|
||||||
if (saveResp.data) {
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: 'Analytic 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 })
|
|
||||||
}
|
|
||||||
onCancel()
|
|
||||||
} catch (error) {
|
|
||||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: `Failed to save Analytic 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 setValue = (value, providerName, inputParamName) => {
|
|
||||||
let newVal = {}
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(analytic, providerName)) {
|
|
||||||
newVal = { ...analytic, [providerName]: {} }
|
|
||||||
} else {
|
|
||||||
newVal = { ...analytic }
|
|
||||||
}
|
|
||||||
|
|
||||||
newVal[providerName][inputParamName] = value
|
|
||||||
setAnalytic(newVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAccordionChange = (providerName) => (event, isExpanded) => {
|
|
||||||
const accordianProviders = { ...providerExpanded }
|
|
||||||
accordianProviders[providerName] = isExpanded
|
|
||||||
setProviderExpanded(accordianProviders)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dialogProps.chatflow && dialogProps.chatflow.analytic) {
|
|
||||||
try {
|
|
||||||
setAnalytic(JSON.parse(dialogProps.chatflow.analytic))
|
|
||||||
} catch (e) {
|
|
||||||
setAnalytic({})
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setAnalytic({})
|
|
||||||
setProviderExpanded({})
|
|
||||||
}
|
|
||||||
}, [dialogProps])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
|
||||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
}, [show, dispatch])
|
|
||||||
|
|
||||||
const component = show ? (
|
|
||||||
<Dialog
|
|
||||||
onClose={onCancel}
|
|
||||||
open={show}
|
|
||||||
fullWidth
|
|
||||||
maxWidth='sm'
|
|
||||||
aria-labelledby='alert-dialog-title'
|
|
||||||
aria-describedby='alert-dialog-description'
|
|
||||||
>
|
|
||||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
|
||||||
Analyse Chatflow
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
{analyticProviders.map((provider, index) => (
|
|
||||||
<Accordion
|
|
||||||
expanded={providerExpanded[provider.name] || false}
|
|
||||||
onChange={handleAccordionChange(provider.name)}
|
|
||||||
disableGutters
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls={provider.name} id={provider.name}>
|
|
||||||
<ListItem style={{ padding: 0, margin: 0 }} alignItems='center'>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
borderRadius: '50%',
|
|
||||||
backgroundColor: 'white'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
padding: 10,
|
|
||||||
objectFit: 'contain'
|
|
||||||
}}
|
|
||||||
alt='AI'
|
|
||||||
src={provider.icon}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
sx={{ ml: 1 }}
|
|
||||||
primary={provider.label}
|
|
||||||
secondary={
|
|
||||||
<a target='_blank' rel='noreferrer' href={provider.url}>
|
|
||||||
{provider.url}
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{analytic[provider.name] && analytic[provider.name].status && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
background: '#d8f3dc',
|
|
||||||
borderRadius: 15,
|
|
||||||
padding: 5,
|
|
||||||
paddingLeft: 7,
|
|
||||||
paddingRight: 7,
|
|
||||||
marginRight: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 15,
|
|
||||||
height: 15,
|
|
||||||
borderRadius: '50%',
|
|
||||||
backgroundColor: '#70e000'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span style={{ color: '#006400', marginLeft: 10 }}>ON</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
{provider.inputs.map((inputParam, index) => (
|
|
||||||
<Box key={index} sx={{ p: 2 }}>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
|
||||||
<Typography>
|
|
||||||
{inputParam.label}
|
|
||||||
{!inputParam.optional && <span style={{ color: 'red' }}> *</span>}
|
|
||||||
{inputParam.description && (
|
|
||||||
<TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
{providerExpanded[provider.name] && inputParam.type === 'credential' && (
|
|
||||||
<CredentialInputHandler
|
|
||||||
data={analytic[provider.name] ? { credential: analytic[provider.name].credentialId } : {}}
|
|
||||||
inputParam={inputParam}
|
|
||||||
onSelect={(newValue) => setValue(newValue, provider.name, 'credentialId')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{providerExpanded[provider.name] && inputParam.type === 'boolean' && (
|
|
||||||
<SwitchInput
|
|
||||||
onChange={(newValue) => setValue(newValue, provider.name, inputParam.name)}
|
|
||||||
value={
|
|
||||||
analytic[provider.name]
|
|
||||||
? analytic[provider.name][inputParam.name]
|
|
||||||
: inputParam.default ?? false
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{providerExpanded[provider.name] &&
|
|
||||||
(inputParam.type === 'string' ||
|
|
||||||
inputParam.type === 'password' ||
|
|
||||||
inputParam.type === 'number') && (
|
|
||||||
<Input
|
|
||||||
inputParam={inputParam}
|
|
||||||
onChange={(newValue) => setValue(newValue, provider.name, inputParam.name)}
|
|
||||||
value={
|
|
||||||
analytic[provider.name]
|
|
||||||
? analytic[provider.name][inputParam.name]
|
|
||||||
: inputParam.default ?? ''
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
))}
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<StyledButton variant='contained' onClick={onSave}>
|
|
||||||
Save
|
|
||||||
</StyledButton>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
return createPortal(component, portalElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
AnalyseFlowDialog.propTypes = {
|
|
||||||
show: PropTypes.bool,
|
|
||||||
dialogProps: PropTypes.object,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AnalyseFlowDialog
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import { Button, Box } from '@mui/material'
|
|
||||||
import { IconX } from '@tabler/icons'
|
|
||||||
|
|
||||||
// Project import
|
|
||||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
|
||||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
|
||||||
|
|
||||||
// store
|
|
||||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
|
||||||
import useNotifier from '@/utils/useNotifier'
|
|
||||||
|
|
||||||
// API
|
|
||||||
import chatflowsApi from '@/api/chatflows'
|
|
||||||
|
|
||||||
const ChatFeedback = ({ dialogProps }) => {
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
useNotifier()
|
|
||||||
|
|
||||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
|
||||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
|
||||||
|
|
||||||
const [chatFeedbackStatus, setChatFeedbackStatus] = useState(false)
|
|
||||||
const [chatbotConfig, setChatbotConfig] = useState({})
|
|
||||||
|
|
||||||
const handleChange = (value) => {
|
|
||||||
setChatFeedbackStatus(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSave = async () => {
|
|
||||||
try {
|
|
||||||
let value = {
|
|
||||||
chatFeedback: {
|
|
||||||
status: chatFeedbackStatus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chatbotConfig.chatFeedback = value.chatFeedback
|
|
||||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
|
||||||
chatbotConfig: JSON.stringify(chatbotConfig)
|
|
||||||
})
|
|
||||||
if (saveResp.data) {
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: 'Chat Feedback Settings 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) {
|
|
||||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: `Failed to save Chat Feedback Settings: ${errorData}`,
|
|
||||||
options: {
|
|
||||||
key: new Date().getTime() + Math.random(),
|
|
||||||
variant: 'error',
|
|
||||||
persist: true,
|
|
||||||
action: (key) => (
|
|
||||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
|
||||||
<IconX />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dialogProps.chatflow && dialogProps.chatflow.chatbotConfig) {
|
|
||||||
let chatbotConfig = JSON.parse(dialogProps.chatflow.chatbotConfig)
|
|
||||||
setChatbotConfig(chatbotConfig || {})
|
|
||||||
if (chatbotConfig.chatFeedback) {
|
|
||||||
setChatFeedbackStatus(chatbotConfig.chatFeedback.status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {}
|
|
||||||
}, [dialogProps])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
|
||||||
<SwitchInput label='Enable chat feedback' onChange={handleChange} value={chatFeedbackStatus} />
|
|
||||||
</Box>
|
|
||||||
<StyledButton style={{ marginBottom: 10, marginTop: 10 }} variant='contained' onClick={onSave}>
|
|
||||||
Save
|
|
||||||
</StyledButton>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatFeedback.propTypes = {
|
|
||||||
dialogProps: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChatFeedback
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
import { createPortal } from 'react-dom'
|
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions'
|
|
||||||
|
|
||||||
// material-ui
|
|
||||||
import {
|
|
||||||
Typography,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
DialogActions,
|
|
||||||
FormControl,
|
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText,
|
|
||||||
MenuItem,
|
|
||||||
Select
|
|
||||||
} from '@mui/material'
|
|
||||||
import { IconX } from '@tabler/icons'
|
|
||||||
|
|
||||||
// Project import
|
|
||||||
import CredentialInputHandler from '@/views/canvas/CredentialInputHandler'
|
|
||||||
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
|
|
||||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
|
||||||
import { Input } from '@/ui-component/input/Input'
|
|
||||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
|
||||||
import { Dropdown } from '@/ui-component/dropdown/Dropdown'
|
|
||||||
import openAISVG from '@/assets/images/openai.svg'
|
|
||||||
import assemblyAIPng from '@/assets/images/assemblyai.png'
|
|
||||||
|
|
||||||
// store
|
|
||||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
|
||||||
import useNotifier from '@/utils/useNotifier'
|
|
||||||
|
|
||||||
// API
|
|
||||||
import chatflowsApi from '@/api/chatflows'
|
|
||||||
|
|
||||||
const speechToTextProviders = {
|
|
||||||
openAIWhisper: {
|
|
||||||
label: 'OpenAI Whisper',
|
|
||||||
name: 'openAIWhisper',
|
|
||||||
icon: openAISVG,
|
|
||||||
url: 'https://platform.openai.com/docs/guides/speech-to-text',
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
label: 'Connect Credential',
|
|
||||||
name: 'credential',
|
|
||||||
type: 'credential',
|
|
||||||
credentialNames: ['openAIApi']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Language',
|
|
||||||
name: 'language',
|
|
||||||
type: 'string',
|
|
||||||
description:
|
|
||||||
'The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency.',
|
|
||||||
placeholder: 'en',
|
|
||||||
optional: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Prompt',
|
|
||||||
name: 'prompt',
|
|
||||||
type: 'string',
|
|
||||||
rows: 4,
|
|
||||||
description: `An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.`,
|
|
||||||
optional: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Temperature',
|
|
||||||
name: 'temperature',
|
|
||||||
type: 'number',
|
|
||||||
step: 0.1,
|
|
||||||
description: `The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`,
|
|
||||||
optional: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
assemblyAiTranscribe: {
|
|
||||||
label: 'Assembly AI',
|
|
||||||
name: 'assemblyAiTranscribe',
|
|
||||||
icon: assemblyAIPng,
|
|
||||||
url: 'https://www.assemblyai.com/',
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
label: 'Connect Credential',
|
|
||||||
name: 'credential',
|
|
||||||
type: 'credential',
|
|
||||||
credentialNames: ['assemblyAIApi']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SpeechToTextDialog = ({ show, dialogProps, onCancel }) => {
|
|
||||||
const portalElement = document.getElementById('portal')
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
useNotifier()
|
|
||||||
|
|
||||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
|
||||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
|
||||||
|
|
||||||
const [speechToText, setSpeechToText] = useState({})
|
|
||||||
const [selectedProvider, setSelectedProvider] = useState('none')
|
|
||||||
|
|
||||||
const onSave = async () => {
|
|
||||||
const speechToText = setValue(true, selectedProvider, 'status')
|
|
||||||
try {
|
|
||||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
|
||||||
speechToText: JSON.stringify(speechToText)
|
|
||||||
})
|
|
||||||
if (saveResp.data) {
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: 'Speech To Text 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 })
|
|
||||||
}
|
|
||||||
onCancel()
|
|
||||||
} catch (error) {
|
|
||||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
|
||||||
enqueueSnackbar({
|
|
||||||
message: `Failed to save Speech To Text 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 setValue = (value, providerName, inputParamName) => {
|
|
||||||
let newVal = {}
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(speechToText, providerName)) {
|
|
||||||
newVal = { ...speechToText, [providerName]: {} }
|
|
||||||
} else {
|
|
||||||
newVal = { ...speechToText }
|
|
||||||
}
|
|
||||||
|
|
||||||
newVal[providerName][inputParamName] = value
|
|
||||||
if (inputParamName === 'status' && value === true) {
|
|
||||||
// ensure that the others are turned off
|
|
||||||
Object.keys(speechToTextProviders).forEach((key) => {
|
|
||||||
const provider = speechToTextProviders[key]
|
|
||||||
if (provider.name !== providerName) {
|
|
||||||
newVal[provider.name] = { ...speechToText[provider.name], status: false }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setSpeechToText(newVal)
|
|
||||||
return newVal
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleProviderChange = (event) => {
|
|
||||||
setSelectedProvider(event.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dialogProps.chatflow && dialogProps.chatflow.speechToText) {
|
|
||||||
try {
|
|
||||||
const speechToText = JSON.parse(dialogProps.chatflow.speechToText)
|
|
||||||
let selectedProvider = 'none'
|
|
||||||
Object.keys(speechToTextProviders).forEach((key) => {
|
|
||||||
const providerConfig = speechToText[key]
|
|
||||||
if (providerConfig && providerConfig.status) {
|
|
||||||
selectedProvider = key
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setSelectedProvider(selectedProvider)
|
|
||||||
setSpeechToText(speechToText)
|
|
||||||
} catch (e) {
|
|
||||||
setSpeechToText({})
|
|
||||||
setSelectedProvider('none')
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setSpeechToText({})
|
|
||||||
setSelectedProvider('none')
|
|
||||||
}
|
|
||||||
}, [dialogProps])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
|
||||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
|
||||||
}, [show, dispatch])
|
|
||||||
|
|
||||||
const component = (
|
|
||||||
<Dialog
|
|
||||||
onClose={onCancel}
|
|
||||||
open={show}
|
|
||||||
fullWidth
|
|
||||||
maxWidth='sm'
|
|
||||||
aria-labelledby='alert-dialog-title'
|
|
||||||
aria-describedby='alert-dialog-description'
|
|
||||||
>
|
|
||||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
|
||||||
Speech To Text Configuration
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<Box fullWidth sx={{ my: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
||||||
<Typography>Speech To Text Providers</Typography>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<Select value={selectedProvider} onChange={handleProviderChange}>
|
|
||||||
<MenuItem value='none'>None</MenuItem>
|
|
||||||
<MenuItem value='openAIWhisper'>OpenAI Whisper</MenuItem>
|
|
||||||
<MenuItem value='assemblyAiTranscribe'>Assembly AI</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
|
||||||
{selectedProvider !== 'none' && (
|
|
||||||
<>
|
|
||||||
<ListItem style={{ padding: 0, margin: 0 }} alignItems='center'>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
borderRadius: '50%',
|
|
||||||
backgroundColor: 'white'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
padding: 10,
|
|
||||||
objectFit: 'contain'
|
|
||||||
}}
|
|
||||||
alt='AI'
|
|
||||||
src={speechToTextProviders[selectedProvider].icon}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
sx={{ ml: 1 }}
|
|
||||||
primary={speechToTextProviders[selectedProvider].label}
|
|
||||||
secondary={
|
|
||||||
<a target='_blank' rel='noreferrer' href={speechToTextProviders[selectedProvider].url}>
|
|
||||||
{speechToTextProviders[selectedProvider].url}
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{speechToTextProviders[selectedProvider].inputs.map((inputParam, index) => (
|
|
||||||
<Box key={index} sx={{ p: 2 }}>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
|
||||||
<Typography>
|
|
||||||
{inputParam.label}
|
|
||||||
{!inputParam.optional && <span style={{ color: 'red' }}> *</span>}
|
|
||||||
{inputParam.description && (
|
|
||||||
<TooltipWithParser style={{ marginLeft: 10 }} title={inputParam.description} />
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
{inputParam.type === 'credential' && (
|
|
||||||
<CredentialInputHandler
|
|
||||||
key={speechToText[selectedProvider]?.credentialId}
|
|
||||||
data={
|
|
||||||
speechToText[selectedProvider]?.credentialId
|
|
||||||
? { credential: speechToText[selectedProvider].credentialId }
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
inputParam={inputParam}
|
|
||||||
onSelect={(newValue) => setValue(newValue, selectedProvider, 'credentialId')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{inputParam.type === 'boolean' && (
|
|
||||||
<SwitchInput
|
|
||||||
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
|
|
||||||
value={
|
|
||||||
speechToText[selectedProvider]
|
|
||||||
? speechToText[selectedProvider][inputParam.name]
|
|
||||||
: inputParam.default ?? false
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (
|
|
||||||
<Input
|
|
||||||
inputParam={inputParam}
|
|
||||||
onChange={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
|
|
||||||
value={
|
|
||||||
speechToText[selectedProvider]
|
|
||||||
? speechToText[selectedProvider][inputParam.name]
|
|
||||||
: inputParam.default ?? ''
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{inputParam.type === 'options' && (
|
|
||||||
<Dropdown
|
|
||||||
name={inputParam.name}
|
|
||||||
options={inputParam.options}
|
|
||||||
onSelect={(newValue) => setValue(newValue, selectedProvider, inputParam.name)}
|
|
||||||
value={
|
|
||||||
speechToText[selectedProvider]
|
|
||||||
? speechToText[selectedProvider][inputParam.name]
|
|
||||||
: inputParam.default ?? 'choose an option'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<StyledButton
|
|
||||||
disabled={selectedProvider !== 'none' && !speechToText[selectedProvider]?.credentialId}
|
|
||||||
variant='contained'
|
|
||||||
onClick={onSave}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</StyledButton>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
|
|
||||||
return createPortal(component, portalElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeechToTextDialog.propTypes = {
|
|
||||||
show: PropTypes.bool,
|
|
||||||
dialogProps: PropTypes.object,
|
|
||||||
onCancel: PropTypes.func
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SpeechToTextDialog
|
|
||||||
Reference in New Issue
Block a user