Skip to content

Commit ed86d1e

Browse files
VanRitzOwenHenryHengZJtianwei-liu
authored
Add validation for Agentflow generation (#5607)
* Add validation for Agentflow generation * Update based on feedbacks * Fix lint issues * Apply suggestion from @tianwei-liu Co-authored-by: tianwei-liu <[email protected]> * Apply suggestion from @tianwei-liu Co-authored-by: tianwei-liu <[email protected]> * Lint fix AgentflowGeneratorDialog.jsx --------- Co-authored-by: Henry Heng <[email protected]> Co-authored-by: tianwei-liu <[email protected]> Co-authored-by: Henry Heng <[email protected]>
1 parent a6282b9 commit ed86d1e

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

packages/ui/src/ui-component/dialog/AgentflowGeneratorDialog.jsx

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createPortal } from 'react-dom'
22
import { cloneDeep } from 'lodash'
3-
import { useState, useEffect, useContext } from 'react'
3+
import { useState, useEffect, useContext, useCallback } from 'react'
44
import { useDispatch, useSelector } from 'react-redux'
55
import PropTypes from 'prop-types'
66
import { Box, Typography, OutlinedInput, DialogActions, Button, Dialog, DialogContent, DialogTitle, LinearProgress } from '@mui/material'
@@ -14,7 +14,7 @@ import { flowContext } from '@/store/context/ReactFlowContext'
1414
import { Dropdown } from '@/ui-component/dropdown/Dropdown'
1515
import { useTheme } from '@mui/material/styles'
1616
import assistantsApi from '@/api/assistants'
17-
import { baseURL } from '@/store/constant'
17+
import { baseURL, FLOWISE_CREDENTIAL_ID } from '@/store/constant'
1818
import { initNode, showHideInputParams } from '@/utils/genericHelper'
1919
import DocStoreInputHandler from '@/views/docstore/DocStoreInputHandler'
2020
import useApi from '@/hooks/useApi'
@@ -58,12 +58,83 @@ const AgentflowGeneratorDialog = ({ show, dialogProps, onCancel, onConfirm }) =>
5858
const handleChatModelDataChange = ({ inputParam, newValue }) => {
5959
setSelectedChatModel((prevData) => {
6060
const updatedData = { ...prevData }
61-
updatedData.inputs[inputParam.name] = newValue
61+
if (inputParam.type === 'credential') {
62+
updatedData.credential = newValue
63+
updatedData.inputs = { ...updatedData.inputs, [FLOWISE_CREDENTIAL_ID]: newValue }
64+
} else {
65+
updatedData.inputs = { ...updatedData.inputs, [inputParam.name]: newValue }
66+
}
6267
updatedData.inputParams = showHideInputParams(updatedData)
6368
return updatedData
6469
})
6570
}
6671

72+
/**
73+
* Check if all mandatory fields are filled for the selected chat model.
74+
*
75+
* @param value
76+
* @returns {boolean}
77+
*/
78+
const isMissingRequiredValue = (value) => {
79+
if (value === undefined || value === null) return true
80+
81+
// Empty / whitespace-only string should be treated as missing
82+
if (typeof value === 'string') return value.trim() === ''
83+
84+
// Empty array should be treated as missing (common for multi-select inputs)
85+
if (Array.isArray(value)) return value.length === 0
86+
87+
// IMPORTANT: boolean false and number 0 are valid values, so not missing
88+
return false
89+
}
90+
91+
/**
92+
* Check Mandatory Fields
93+
* @returns { isValid: boolean, missingFields: string[] }
94+
*/
95+
const checkMandatoryFields = useCallback(() => {
96+
if (!selectedChatModel || Object.keys(selectedChatModel).length === 0) {
97+
return { isValid: false, missingFields: [] }
98+
}
99+
100+
const inputParams = showHideInputParams(selectedChatModel).filter(
101+
(inputParam) => !inputParam.hidden && inputParam.display !== false
102+
)
103+
104+
const missingFields = []
105+
106+
for (const inputParam of inputParams) {
107+
if (!inputParam.optional) {
108+
if (inputParam.type === 'credential') {
109+
// Check for credential in both possible locations
110+
const credential = selectedChatModel.credential || selectedChatModel.inputs?.[FLOWISE_CREDENTIAL_ID]
111+
if (!credential) {
112+
missingFields.push(inputParam.label || 'Credential')
113+
}
114+
} else if (isMissingRequiredValue(selectedChatModel.inputs?.[inputParam.name])) {
115+
missingFields.push(inputParam.label || inputParam.name)
116+
}
117+
}
118+
}
119+
120+
return { isValid: missingFields.length === 0, missingFields }
121+
}, [selectedChatModel])
122+
123+
const displayWarning = (message) => {
124+
enqueueSnackbar({
125+
message: message || 'Please fill in all mandatory fields.',
126+
options: {
127+
key: new Date().getTime() + Math.random(),
128+
variant: 'warning',
129+
action: (key) => (
130+
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
131+
<IconX />
132+
</Button>
133+
)
134+
}
135+
})
136+
}
137+
67138
useEffect(() => {
68139
if (getChatModelsApi.data) {
69140
setChatModelsComponents(getChatModelsApi.data)
@@ -113,6 +184,17 @@ const AgentflowGeneratorDialog = ({ show, dialogProps, onCancel, onConfirm }) =>
113184
const onGenerate = async () => {
114185
if (!customAssistantInstruction.trim()) return
115186

187+
// Validate all mandatory fields before proceeding
188+
const { isValid, missingFields } = checkMandatoryFields()
189+
if (!isValid) {
190+
const message =
191+
missingFields.length > 0
192+
? `Please fill in the following required fields: ${missingFields.join(', ')}`
193+
: 'Please fill in all mandatory fields for the selected model.'
194+
displayWarning(message)
195+
return
196+
}
197+
116198
try {
117199
setLoading(true)
118200

@@ -346,7 +428,8 @@ const AgentflowGeneratorDialog = ({ show, dialogProps, onCancel, onConfirm }) =>
346428
loading ||
347429
!customAssistantInstruction.trim() ||
348430
!selectedChatModel ||
349-
!Object.keys(selectedChatModel).length
431+
!Object.keys(selectedChatModel).length ||
432+
!checkMandatoryFields().isValid
350433
}
351434
>
352435
Generate

packages/ui/src/views/docstore/DocStoreInputHandler.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ const DocStoreInputHandler = ({ inputParam, data, disabled = false, onNodeDataCh
146146
onSelect={(newValue) => {
147147
data.credential = newValue
148148
data.inputs[FLOWISE_CREDENTIAL_ID] = newValue // in case data.credential is not updated
149+
if (nodeDataChangeHandler) {
150+
nodeDataChangeHandler({ nodeId: data.id, inputParam, newValue })
151+
}
149152
}}
150153
/>
151154
)}

0 commit comments

Comments
 (0)