mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
New Feature: Add post postprocessing of response from LLM using custom JS Function (#4079)
* New Feature: Add post postprocessing of response from LLM using custom Javascript functions * Disable Save when there is no content * add post processing ui changes, disable streaming --------- Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { useState, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { IconButton, Button, Box, Typography } from '@mui/material'
|
||||
import { IconArrowsMaximize, IconBulb, IconX } from '@tabler/icons-react'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
// Project import
|
||||
import { StyledButton } from '@/ui-component/button/StyledButton'
|
||||
import { SwitchInput } from '@/ui-component/switch/Switch'
|
||||
import { CodeEditor } from '@/ui-component/editor/CodeEditor'
|
||||
import ExpandTextDialog from '@/ui-component/dialog/ExpandTextDialog'
|
||||
|
||||
// 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 sampleFunction = `return $flow.rawOutput + " This is a post processed response!";`
|
||||
|
||||
const PostProcessing = ({ dialogProps }) => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
useNotifier()
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const [postProcessingEnabled, setPostProcessingEnabled] = useState(false)
|
||||
const [postProcessingFunction, setPostProcessingFunction] = useState('')
|
||||
const [chatbotConfig, setChatbotConfig] = useState({})
|
||||
const [showExpandDialog, setShowExpandDialog] = useState(false)
|
||||
const [expandDialogProps, setExpandDialogProps] = useState({})
|
||||
|
||||
const handleChange = (value) => {
|
||||
setPostProcessingEnabled(value)
|
||||
}
|
||||
|
||||
const onExpandDialogClicked = (value) => {
|
||||
const dialogProps = {
|
||||
value,
|
||||
inputParam: {
|
||||
label: 'Post Processing Function',
|
||||
name: 'postProcessingFunction',
|
||||
type: 'code',
|
||||
placeholder: sampleFunction,
|
||||
hideCodeExecute: true
|
||||
},
|
||||
languageType: 'js',
|
||||
confirmButtonName: 'Save',
|
||||
cancelButtonName: 'Cancel'
|
||||
}
|
||||
setExpandDialogProps(dialogProps)
|
||||
setShowExpandDialog(true)
|
||||
}
|
||||
|
||||
const onSave = async () => {
|
||||
try {
|
||||
let value = {
|
||||
postProcessing: {
|
||||
enabled: postProcessingEnabled,
|
||||
customFunction: JSON.stringify(postProcessingFunction)
|
||||
}
|
||||
}
|
||||
chatbotConfig.postProcessing = value.postProcessing
|
||||
const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, {
|
||||
chatbotConfig: JSON.stringify(chatbotConfig)
|
||||
})
|
||||
if (saveResp.data) {
|
||||
enqueueSnackbar({
|
||||
message: 'Post Processing 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) {
|
||||
enqueueSnackbar({
|
||||
message: `Failed to save Post Processing Settings: ${
|
||||
typeof error.response.data === 'object' ? error.response.data.message : error.response.data
|
||||
}`,
|
||||
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.postProcessing) {
|
||||
setPostProcessingEnabled(chatbotConfig.postProcessing.enabled)
|
||||
if (chatbotConfig.postProcessing.customFunction) {
|
||||
setPostProcessingFunction(JSON.parse(chatbotConfig.postProcessing.customFunction))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return () => {}
|
||||
}, [dialogProps])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<SwitchInput label='Enable Post Processing' onChange={handleChange} value={postProcessingEnabled} />
|
||||
</Box>
|
||||
<Box sx={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1 }}>
|
||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
|
||||
<Typography>JS Function</Typography>
|
||||
<Button
|
||||
sx={{ ml: 2 }}
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
setPostProcessingFunction(sampleFunction)
|
||||
}}
|
||||
>
|
||||
See Example
|
||||
</Button>
|
||||
<div style={{ flex: 1 }} />
|
||||
<IconButton
|
||||
size='small'
|
||||
sx={{
|
||||
height: 25,
|
||||
width: 25
|
||||
}}
|
||||
title='Expand'
|
||||
color='primary'
|
||||
onClick={() => onExpandDialogClicked(postProcessingFunction)}
|
||||
>
|
||||
<IconArrowsMaximize />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: '10px',
|
||||
border: '1px solid',
|
||||
borderColor: theme.palette.grey['300'],
|
||||
borderRadius: '6px',
|
||||
height: '200px',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<CodeEditor
|
||||
value={postProcessingFunction}
|
||||
height='200px'
|
||||
theme={customization.isDarkMode ? 'dark' : 'light'}
|
||||
lang={'js'}
|
||||
placeholder={sampleFunction}
|
||||
onValueChange={(code) => setPostProcessingFunction(code)}
|
||||
basicSetup={{ highlightActiveLine: false, highlightActiveLineGutter: false }}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: 10,
|
||||
background: '#d8f3dc',
|
||||
padding: 10,
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingTop: 10
|
||||
}}
|
||||
>
|
||||
<IconBulb size={30} color='#2d6a4f' />
|
||||
<span style={{ color: '#2d6a4f', marginLeft: 10, fontWeight: 500 }}>
|
||||
The following variables are available to use in the custom function:{' '}
|
||||
<pre>$flow.rawOutput, $flow.input, $flow.chatflowId, $flow.sessionId, $flow.chatId</pre>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<StyledButton
|
||||
style={{ marginBottom: 10, marginTop: 10 }}
|
||||
variant='contained'
|
||||
disabled={!postProcessingFunction || postProcessingFunction?.trim().length === 0}
|
||||
onClick={onSave}
|
||||
>
|
||||
Save
|
||||
</StyledButton>
|
||||
<ExpandTextDialog
|
||||
show={showExpandDialog}
|
||||
dialogProps={expandDialogProps}
|
||||
onCancel={() => setShowExpandDialog(false)}
|
||||
onConfirm={(newValue) => {
|
||||
setPostProcessingFunction(newValue)
|
||||
setShowExpandDialog(false)
|
||||
}}
|
||||
></ExpandTextDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
PostProcessing.propTypes = {
|
||||
dialogProps: PropTypes.object
|
||||
}
|
||||
|
||||
export default PostProcessing
|
||||
Reference in New Issue
Block a user