Skip to content

Commit 68d3c83

Browse files
changes & fixes for various issues in chatflow config (#4314)
* Chatflow config - rate limit and override config were overwriting changes. * prevent post processing JS function from escaping as JSON... * non-streaming follow up prompts need to be escaped twice * prevent post processing JS function from escaping as JSON... * Adding file mimetypes for full file upload... * lint.. * fixing the issue with storing only filtered nodes.. * return doc store processing response without await when queue mode and request is from UI --------- Co-authored-by: Henry <[email protected]>
1 parent 9c16525 commit 68d3c83

File tree

9 files changed

+149
-46
lines changed

9 files changed

+149
-46
lines changed

packages/server/src/controllers/documentstore/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ const processLoader = async (req: Request, res: Response, next: NextFunction) =>
201201
}
202202
const docLoaderId = req.params.loaderId
203203
const body = req.body
204-
const apiResponse = await documentStoreService.processLoaderMiddleware(body, docLoaderId)
204+
const isInternalRequest = req.headers['x-request-from'] === 'internal'
205+
const apiResponse = await documentStoreService.processLoaderMiddleware(body, docLoaderId, isInternalRequest)
205206
return res.json(apiResponse)
206207
} catch (error) {
207208
next(error)

packages/server/src/services/documentstore/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ export const processLoader = async ({ appDataSource, componentNodes, data, docLo
740740
return getDocumentStoreFileChunks(appDataSource, data.storeId as string, docLoaderId)
741741
}
742742

743-
const processLoaderMiddleware = async (data: IDocumentStoreLoaderForPreview, docLoaderId: string) => {
743+
const processLoaderMiddleware = async (data: IDocumentStoreLoaderForPreview, docLoaderId: string, isInternalRequest = false) => {
744744
try {
745745
const appServer = getRunningExpressApp()
746746
const appDataSource = appServer.AppDataSource
@@ -761,6 +761,12 @@ const processLoaderMiddleware = async (data: IDocumentStoreLoaderForPreview, doc
761761
const job = await upsertQueue.addJob(omit(executeData, OMIT_QUEUE_JOB_DATA))
762762
logger.debug(`[server]: Job added to queue: ${job.id}`)
763763

764+
if (isInternalRequest) {
765+
return {
766+
jobId: job.id
767+
}
768+
}
769+
764770
const queueEvents = upsertQueue.getQueueEvents()
765771
const result = await job.waitUntilFinished(queueEvents)
766772

packages/server/src/utils/buildChatflow.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
mapMimeTypeToInputField,
1515
mapExtToInputField,
1616
getFileFromUpload,
17-
removeSpecificFileFromUpload
17+
removeSpecificFileFromUpload,
18+
handleEscapeCharacters
1819
} from 'flowise-components'
1920
import { StatusCodes } from 'http-status-codes'
2021
import {
@@ -665,6 +666,7 @@ export const executeFlow = async ({
665666
const postProcessingFunction = JSON.parse(chatflowConfig?.postProcessing?.customFunction)
666667
const nodeInstanceFilePath = componentNodes['customFunction'].filePath as string
667668
const nodeModule = await import(nodeInstanceFilePath)
669+
//set the outputs.output to EndingNode to prevent json escaping of content...
668670
const nodeData = {
669671
inputs: { javascriptFunction: postProcessingFunction },
670672
outputs: { output: 'output' }
@@ -681,7 +683,13 @@ export const executeFlow = async ({
681683
}
682684
const customFuncNodeInstance = new nodeModule.nodeClass()
683685
let moderatedResponse = await customFuncNodeInstance.init(nodeData, question, options)
684-
result.text = moderatedResponse
686+
if (typeof moderatedResponse === 'string') {
687+
result.text = handleEscapeCharacters(moderatedResponse, true)
688+
} else if (typeof moderatedResponse === 'object') {
689+
result.text = '```json\n' + JSON.stringify(moderatedResponse, null, 2) + '\n```'
690+
} else {
691+
result.text = moderatedResponse
692+
}
685693
resultText = result.text
686694
} catch (e) {
687695
logger.log('[server]: Post Processing Error:', e)

packages/ui/src/ui-component/cards/FollowUpPromptsCard.jsx

+20-19
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,26 @@ const FollowUpPromptsCard = ({ isGrid, followUpPrompts, sx, onPromptClick }) =>
1212
className={'button-container'}
1313
sx={{ width: '100%', maxWidth: isGrid ? 'inherit' : '400px', p: 1.5, display: 'flex', gap: 1, ...sx }}
1414
>
15-
{followUpPrompts.map((fp, index) => (
16-
<Chip
17-
label={fp}
18-
className={'button'}
19-
key={index}
20-
onClick={(e) => onPromptClick(fp, e)}
21-
sx={{
22-
backgroundColor: 'transparent',
23-
border: '1px solid',
24-
boxShadow: '0px 2px 1px -1px rgba(0,0,0,0.2)',
25-
color: '#2196f3',
26-
transition: 'all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
27-
'&:hover': {
28-
backgroundColor: customization.isDarkMode ? 'rgba(0, 0, 0, 0.12)' : 'rgba(0, 0, 0, 0.05)',
29-
border: '1px solid'
30-
}
31-
}}
32-
/>
33-
))}
15+
{Array.isArray(followUpPrompts) &&
16+
followUpPrompts.map((fp, index) => (
17+
<Chip
18+
label={fp}
19+
className={'button'}
20+
key={index}
21+
onClick={(e) => onPromptClick(fp, e)}
22+
sx={{
23+
backgroundColor: 'transparent',
24+
border: '1px solid',
25+
boxShadow: '0px 2px 1px -1px rgba(0,0,0,0.2)',
26+
color: '#2196f3',
27+
transition: 'all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
28+
'&:hover': {
29+
backgroundColor: customization.isDarkMode ? 'rgba(0, 0, 0, 0.12)' : 'rgba(0, 0, 0, 0.05)',
30+
border: '1px solid'
31+
}
32+
}}
33+
/>
34+
))}
3435
</Box>
3536
)
3637
}

packages/ui/src/ui-component/extended/FileUpload.jsx

+70-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackba
55
import parser from 'html-react-parser'
66

77
// material-ui
8-
import { Button, Box } from '@mui/material'
8+
import { Button, Box, Typography } from '@mui/material'
99
import { IconX, IconBulb } from '@tabler/icons-react'
1010

1111
// Project import
@@ -22,6 +22,18 @@ const message = `Uploaded files will be parsed as strings and sent to the LLM. I
2222
<br />
2323
Refer <a href='https://docs.flowiseai.com/using-flowise/uploads#files' target='_blank'>docs</a> for more details.`
2424

25+
const availableFileTypes = [
26+
{ name: 'CSS', ext: 'text/css' },
27+
{ name: 'CSV', ext: 'text/csv' },
28+
{ name: 'HTML', ext: 'text/html' },
29+
{ name: 'JSON', ext: 'application/json' },
30+
{ name: 'Markdown', ext: 'text/markdown' },
31+
{ name: 'PDF', ext: 'application/pdf' },
32+
{ name: 'SQL', ext: 'application/sql' },
33+
{ name: 'Text File', ext: 'text/plain' },
34+
{ name: 'XML', ext: 'application/xml' }
35+
]
36+
2537
const FileUpload = ({ dialogProps }) => {
2638
const dispatch = useDispatch()
2739

@@ -31,16 +43,27 @@ const FileUpload = ({ dialogProps }) => {
3143
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
3244

3345
const [fullFileUpload, setFullFileUpload] = useState(false)
46+
const [allowedFileTypes, setAllowedFileTypes] = useState([])
3447
const [chatbotConfig, setChatbotConfig] = useState({})
3548

3649
const handleChange = (value) => {
3750
setFullFileUpload(value)
3851
}
3952

53+
const handleAllowedFileTypesChange = (event) => {
54+
const { checked, value } = event.target
55+
if (checked) {
56+
setAllowedFileTypes((prev) => [...prev, value])
57+
} else {
58+
setAllowedFileTypes((prev) => prev.filter((item) => item !== value))
59+
}
60+
}
61+
4062
const onSave = async () => {
4163
try {
4264
const value = {
43-
status: fullFileUpload
65+
status: fullFileUpload,
66+
allowedUploadFileTypes: allowedFileTypes.join(',')
4467
}
4568
chatbotConfig.fullFileUpload = value
4669

@@ -82,6 +105,9 @@ const FileUpload = ({ dialogProps }) => {
82105
}
83106

84107
useEffect(() => {
108+
/* backward compatibility - by default, allow all */
109+
const allowedFileTypes = availableFileTypes.map((fileType) => fileType.ext)
110+
setAllowedFileTypes(allowedFileTypes)
85111
if (dialogProps.chatflow) {
86112
if (dialogProps.chatflow.chatbotConfig) {
87113
try {
@@ -90,6 +116,10 @@ const FileUpload = ({ dialogProps }) => {
90116
if (chatbotConfig.fullFileUpload) {
91117
setFullFileUpload(chatbotConfig.fullFileUpload.status)
92118
}
119+
if (chatbotConfig.fullFileUpload?.allowedUploadFileTypes) {
120+
const allowedFileTypes = chatbotConfig.fullFileUpload.allowedUploadFileTypes.split(',')
121+
setAllowedFileTypes(allowedFileTypes)
122+
}
93123
} catch (e) {
94124
setChatbotConfig({})
95125
}
@@ -135,8 +165,44 @@ const FileUpload = ({ dialogProps }) => {
135165
</div>
136166
<SwitchInput label='Enable Full File Upload' onChange={handleChange} value={fullFileUpload} />
137167
</Box>
138-
{/* TODO: Allow selection of allowed file types*/}
139-
<StyledButton style={{ marginBottom: 10, marginTop: 10 }} variant='contained' onClick={onSave}>
168+
169+
<Typography sx={{ fontSize: 14, fontWeight: 500, marginBottom: 1 }}>Allow Uploads of Type</Typography>
170+
<div
171+
style={{
172+
display: 'grid',
173+
gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
174+
gap: 15,
175+
padding: 10,
176+
width: '100%',
177+
marginBottom: '10px'
178+
}}
179+
>
180+
{availableFileTypes.map((fileType) => (
181+
<div
182+
key={fileType.ext}
183+
style={{
184+
display: 'flex',
185+
flexDirection: 'row',
186+
alignItems: 'center',
187+
justifyContent: 'start'
188+
}}
189+
>
190+
<input
191+
type='checkbox'
192+
id={fileType.ext}
193+
name={fileType.ext}
194+
checked={allowedFileTypes.indexOf(fileType.ext) !== -1}
195+
value={fileType.ext}
196+
disabled={!fullFileUpload}
197+
onChange={handleAllowedFileTypesChange}
198+
/>
199+
<label htmlFor={fileType.ext} style={{ marginLeft: 10 }}>
200+
{fileType.name} ({fileType.ext})
201+
</label>
202+
</div>
203+
))}
204+
</div>
205+
<StyledButton style={{ marginBottom: 10, marginTop: 20 }} variant='contained' onClick={onSave}>
140206
Save
141207
</StyledButton>
142208
</>

packages/ui/src/ui-component/extended/OverrideConfig.jsx

+9-7
Original file line numberDiff line numberDiff line change
@@ -116,25 +116,27 @@ const OverrideConfig = ({ dialogProps }) => {
116116
}
117117

118118
const formatObj = () => {
119-
const obj = {
120-
overrideConfig: { status: overrideConfigStatus }
119+
let apiConfig = JSON.parse(dialogProps.chatflow.apiConfig)
120+
if (apiConfig === null || apiConfig === undefined) {
121+
apiConfig = {}
121122
}
122123

124+
let overrideConfig = { status: overrideConfigStatus }
123125
if (overrideConfigStatus) {
124-
// loop through each key in nodeOverrides and filter out the enabled ones
125126
const filteredNodeOverrides = {}
126127
for (const key in nodeOverrides) {
127128
filteredNodeOverrides[key] = nodeOverrides[key].filter((node) => node.enabled)
128129
}
129130

130-
obj.overrideConfig = {
131-
...obj.overrideConfig,
131+
overrideConfig = {
132+
...overrideConfig,
132133
nodes: filteredNodeOverrides,
133134
variables: variableOverrides.filter((node) => node.enabled)
134135
}
135136
}
137+
apiConfig.overrideConfig = overrideConfig
136138

137-
return obj
139+
return apiConfig
138140
}
139141

140142
const onNodeOverrideToggle = (node, property, status) => {
@@ -206,7 +208,7 @@ const OverrideConfig = ({ dialogProps }) => {
206208
if (!overrideConfigStatus) {
207209
setNodeOverrides(newNodeOverrides)
208210
} else {
209-
const updatedNodeOverrides = { ...nodeOverrides }
211+
const updatedNodeOverrides = { ...newNodeOverrides }
210212

211213
Object.keys(updatedNodeOverrides).forEach((node) => {
212214
if (!seenNodes.has(node)) {

packages/ui/src/ui-component/extended/RateLimit.jsx

+11-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import chatflowsApi from '@/api/chatflows'
1919
// utils
2020
import useNotifier from '@/utils/useNotifier'
2121

22-
const RateLimit = () => {
22+
const RateLimit = ({ dialogProps }) => {
2323
const dispatch = useDispatch()
2424
const chatflow = useSelector((state) => state.canvas.chatflow)
2525
const chatflowid = chatflow.id
@@ -36,26 +36,28 @@ const RateLimit = () => {
3636
const [limitMsg, setLimitMsg] = useState(apiConfig?.rateLimit?.limitMsg ?? '')
3737

3838
const formatObj = () => {
39-
const obj = {
40-
rateLimit: { status: rateLimitStatus }
39+
let apiConfig = JSON.parse(dialogProps.chatflow.apiConfig)
40+
if (apiConfig === null || apiConfig === undefined) {
41+
apiConfig = {}
4142
}
43+
let obj = { status: rateLimitStatus }
4244

4345
if (rateLimitStatus) {
4446
const rateLimitValuesBoolean = [!limitMax, !limitDuration, !limitMsg]
4547
const rateLimitFilledValues = rateLimitValuesBoolean.filter((value) => value === false)
4648
if (rateLimitFilledValues.length >= 1 && rateLimitFilledValues.length <= 2) {
4749
throw new Error('Need to fill all rate limit input fields')
4850
} else if (rateLimitFilledValues.length === 3) {
49-
obj.rateLimit = {
50-
...obj.rateLimit,
51+
obj = {
52+
...obj,
5153
limitMax,
5254
limitDuration,
5355
limitMsg
5456
}
5557
}
5658
}
57-
58-
return obj
59+
apiConfig.rateLimit = obj
60+
return apiConfig
5961
}
6062

6163
const handleChange = (value) => {
@@ -173,7 +175,8 @@ const RateLimit = () => {
173175
}
174176

175177
RateLimit.propTypes = {
176-
isSessionMemory: PropTypes.bool
178+
isSessionMemory: PropTypes.bool,
179+
dialogProps: PropTypes.object
177180
}
178181

179182
export default RateLimit

packages/ui/src/ui-component/extended/Security.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const Security = ({ dialogProps }) => {
1212

1313
return (
1414
<Stack direction='column' divider={<Divider sx={{ my: 0.5, borderColor: theme.palette.grey[900] + 25 }} />} spacing={4}>
15-
<RateLimit />
15+
<RateLimit dialogProps={dialogProps} />
1616
<AllowedDomains dialogProps={dialogProps} />
1717
<OverrideConfig dialogProps={dialogProps} />
1818
</Stack>

packages/ui/src/views/chatmessage/ChatMessage.jsx

+19-3
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
200200

201201
// full file upload
202202
const [fullFileUpload, setFullFileUpload] = useState(false)
203+
const [fullFileUploadAllowedTypes, setFullFileUploadAllowedTypes] = useState('*')
203204

204205
// feedback
205206
const [chatFeedbackStatus, setChatFeedbackStatus] = useState(false)
@@ -693,7 +694,11 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
693694

694695
if (data.followUpPrompts) {
695696
const followUpPrompts = JSON.parse(data.followUpPrompts)
696-
setFollowUpPrompts(followUpPrompts)
697+
if (typeof followUpPrompts === 'string') {
698+
setFollowUpPrompts(JSON.parse(followUpPrompts))
699+
} else {
700+
setFollowUpPrompts(followUpPrompts)
701+
}
697702
}
698703
}
699704

@@ -981,7 +986,9 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
981986
}
982987

983988
const getFileUploadAllowedTypes = () => {
984-
if (fullFileUpload) return '*'
989+
if (fullFileUpload) {
990+
return fullFileUploadAllowedTypes === '' ? '*' : fullFileUploadAllowedTypes
991+
}
985992
return fileUploadAllowedTypes.includes('*') ? '*' : fileUploadAllowedTypes || '*'
986993
}
987994

@@ -1118,6 +1125,9 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
11181125

11191126
if (config.fullFileUpload) {
11201127
setFullFileUpload(config.fullFileUpload.status)
1128+
if (config.fullFileUpload?.allowedUploadFileTypes) {
1129+
setFullFileUploadAllowedTypes(config.fullFileUpload?.allowedUploadFileTypes)
1130+
}
11211131
}
11221132
}
11231133
}
@@ -1198,7 +1208,13 @@ export const ChatMessage = ({ open, chatflowid, isAgentCanvas, isDialog, preview
11981208
if (followUpPromptsStatus && messages.length > 0) {
11991209
const lastMessage = messages[messages.length - 1]
12001210
if (lastMessage.type === 'apiMessage' && lastMessage.followUpPrompts) {
1201-
setFollowUpPrompts(lastMessage.followUpPrompts)
1211+
if (Array.isArray(lastMessage.followUpPrompts)) {
1212+
setFollowUpPrompts(lastMessage.followUpPrompts)
1213+
}
1214+
if (typeof lastMessage.followUpPrompts === 'string') {
1215+
const followUpPrompts = JSON.parse(lastMessage.followUpPrompts)
1216+
setFollowUpPrompts(followUpPrompts)
1217+
}
12021218
} else if (lastMessage.type === 'userMessage') {
12031219
setFollowUpPrompts([])
12041220
}

0 commit comments

Comments
 (0)