import PropTypes from 'prop-types' import { Handle, Position, useUpdateNodeInternals } from 'reactflow' import { useEffect, useRef, useState, useContext } from 'react' import { useSelector } from 'react-redux' // material-ui import { useTheme, styled } from '@mui/material/styles' import { Box, Typography, Tooltip, IconButton, Button } from '@mui/material' import IconAutoFixHigh from '@mui/icons-material/AutoFixHigh' import { tooltipClasses } from '@mui/material/Tooltip' import { IconArrowsMaximize, IconEdit, IconAlertTriangle } from '@tabler/icons' // project import import { Dropdown } from 'ui-component/dropdown/Dropdown' import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown' import { AsyncDropdown } from 'ui-component/dropdown/AsyncDropdown' import { Input } from 'ui-component/input/Input' import { DataGrid } from 'ui-component/grid/DataGrid' import { File } from 'ui-component/file/File' import { SwitchInput } from 'ui-component/switch/Switch' import { flowContext } from 'store/context/ReactFlowContext' import { isValidConnection } from 'utils/genericHelper' import { JsonEditorInput } from 'ui-component/json/JsonEditor' import { TooltipWithParser } from 'ui-component/tooltip/TooltipWithParser' import { CodeEditor } from 'ui-component/editor/CodeEditor' import ToolDialog from 'views/tools/ToolDialog' import AssistantDialog from 'views/assistants/AssistantDialog' import ExpandTextDialog from 'ui-component/dialog/ExpandTextDialog' import FormatPromptValuesDialog from 'ui-component/dialog/FormatPromptValuesDialog' import PromptLangsmithHubDialog from 'ui-component/dialog/PromptLangsmithHubDialog' import ManageScrapedLinksDialog from 'ui-component/dialog/ManageScrapedLinksDialog' import CredentialInputHandler from './CredentialInputHandler' // utils import { getInputVariables } from 'utils/genericHelper' // const import { FLOWISE_CREDENTIAL_ID } from 'store/constant' const EDITABLE_OPTIONS = ['selectedTool', 'selectedAssistant'] const CustomWidthTooltip = styled(({ className, ...props }) => )({ [`& .${tooltipClasses.tooltip}`]: { maxWidth: 500 } }) // ===========================|| NodeInputHandler ||=========================== // const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isAdditionalParams = false }) => { const theme = useTheme() const customization = useSelector((state) => state.customization) const ref = useRef(null) const { reactFlowInstance } = useContext(flowContext) const updateNodeInternals = useUpdateNodeInternals() const [position, setPosition] = useState(0) const [showExpandDialog, setShowExpandDialog] = useState(false) const [expandDialogProps, setExpandDialogProps] = useState({}) const [showAsyncOptionDialog, setAsyncOptionEditDialog] = useState('') const [asyncOptionEditDialogProps, setAsyncOptionEditDialogProps] = useState({}) const [reloadTimestamp, setReloadTimestamp] = useState(Date.now().toString()) const [showFormatPromptValuesDialog, setShowFormatPromptValuesDialog] = useState(false) const [formatPromptValuesDialogProps, setFormatPromptValuesDialogProps] = useState({}) const [showPromptHubDialog, setShowPromptHubDialog] = useState(false) const [showManageScrapedLinksDialog, setShowManageScrapedLinksDialog] = useState(false) const [manageScrapedLinksDialogProps, setManageScrapedLinksDialogProps] = useState({}) const onExpandDialogClicked = (value, inputParam) => { const dialogProps = { value, inputParam, disabled, confirmButtonName: 'Save', cancelButtonName: 'Cancel' } setExpandDialogProps(dialogProps) setShowExpandDialog(true) } const onShowPromptHubButtonClicked = () => { setShowPromptHubDialog(true) } const onShowPromptHubButtonSubmit = (templates) => { setShowPromptHubDialog(false) for (const t of templates) { if (Object.prototype.hasOwnProperty.call(data.inputs, t.type)) { data.inputs[t.type] = t.template } } } const onManageLinksDialogClicked = (url, selectedLinks) => { const dialogProps = { url, selectedLinks, confirmButtonName: 'Save', cancelButtonName: 'Cancel' } setManageScrapedLinksDialogProps(dialogProps) setShowManageScrapedLinksDialog(true) } const onManageLinksDialogSave = (url, links) => { setShowManageScrapedLinksDialog(false) data.inputs.url = url data.inputs.selectedLinks = links } const onEditJSONClicked = (value, inputParam) => { // Preset values if the field is format prompt values let inputValue = value if (inputParam.name === 'promptValues' && !value) { const obj = {} const templateValue = (data.inputs['template'] ?? '') + (data.inputs['systemMessagePrompt'] ?? '') + (data.inputs['humanMessagePrompt'] ?? '') const inputVariables = getInputVariables(templateValue) for (const inputVariable of inputVariables) { obj[inputVariable] = '' } if (Object.keys(obj).length) inputValue = JSON.stringify(obj) } const dialogProp = { value: inputValue, inputParam, nodes: reactFlowInstance.getNodes(), edges: reactFlowInstance.getEdges(), nodeId: data.id } setFormatPromptValuesDialogProps(dialogProp) setShowFormatPromptValuesDialog(true) } const onExpandDialogSave = (newValue, inputParamName) => { setShowExpandDialog(false) data.inputs[inputParamName] = newValue } const editAsyncOption = (inputParamName, inputValue) => { if (inputParamName === 'selectedTool') { setAsyncOptionEditDialogProps({ title: 'Edit Tool', type: 'EDIT', cancelButtonName: 'Cancel', confirmButtonName: 'Save', toolId: inputValue }) } else if (inputParamName === 'selectedAssistant') { setAsyncOptionEditDialogProps({ title: 'Edit Assistant', type: 'EDIT', cancelButtonName: 'Cancel', confirmButtonName: 'Save', assistantId: inputValue }) } setAsyncOptionEditDialog(inputParamName) } const addAsyncOption = (inputParamName) => { if (inputParamName === 'selectedTool') { setAsyncOptionEditDialogProps({ title: 'Add New Tool', type: 'ADD', cancelButtonName: 'Cancel', confirmButtonName: 'Add' }) } else if (inputParamName === 'selectedAssistant') { setAsyncOptionEditDialogProps({ title: 'Add New Assistant', type: 'ADD', cancelButtonName: 'Cancel', confirmButtonName: 'Add' }) } setAsyncOptionEditDialog(inputParamName) } const onConfirmAsyncOption = (selectedOptionId = '') => { if (!selectedOptionId) { data.inputs[showAsyncOptionDialog] = '' } else { data.inputs[showAsyncOptionDialog] = selectedOptionId setReloadTimestamp(Date.now().toString()) } setAsyncOptionEditDialogProps({}) setAsyncOptionEditDialog('') } useEffect(() => { if (ref.current && ref.current.offsetTop && ref.current.clientHeight) { setPosition(ref.current.offsetTop + ref.current.clientHeight / 2) updateNodeInternals(data.id) } }, [data.id, ref, updateNodeInternals]) useEffect(() => { updateNodeInternals(data.id) }, [data.id, position, updateNodeInternals]) return (
{inputAnchor && ( <> isValidConnection(connection, reactFlowInstance)} style={{ height: 10, width: 10, backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary, top: position }} /> {inputAnchor.label} {!inputAnchor.optional &&  *} {inputAnchor.description && } )} {((inputParam && !inputParam.additionalParams) || isAdditionalParams) && ( <> {inputParam.acceptVariable && ( isValidConnection(connection, reactFlowInstance)} style={{ height: 10, width: 10, backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary, top: position }} /> )} {(data.name === 'promptTemplate' || data.name === 'chatPromptTemplate') && (inputParam.name === 'template' || inputParam.name === 'systemMessagePrompt') && ( <> setShowPromptHubDialog(false)} onSubmit={onShowPromptHubButtonSubmit} > )}
{inputParam.label} {!inputParam.optional &&  *} {inputParam.description && }
{((inputParam.type === 'string' && inputParam.rows) || inputParam.type === 'code') && ( onExpandDialogClicked(data.inputs[inputParam.name] ?? inputParam.default ?? '', inputParam) } > )}
{inputParam.warning && (
{inputParam.warning}
)} {inputParam.type === 'credential' && ( { data.credential = newValue data.inputs[FLOWISE_CREDENTIAL_ID] = newValue // in case data.credential is not updated }} /> )} {inputParam.type === 'file' && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? 'Choose a file to upload'} /> )} {inputParam.type === 'boolean' && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? false} /> )} {inputParam.type === 'datagrid' && ( (data.inputs[inputParam.name] = newValue)} /> )} {inputParam.type === 'code' && ( <>
(data.inputs[inputParam.name] = code)} basicSetup={{ highlightActiveLine: false, highlightActiveLineGutter: false }} />
)} {(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? ''} nodes={inputParam?.acceptVariable && reactFlowInstance ? reactFlowInstance.getNodes() : []} edges={inputParam?.acceptVariable && reactFlowInstance ? reactFlowInstance.getEdges() : []} nodeId={data.id} /> )} {inputParam.type === 'json' && ( <> {!inputParam?.acceptVariable && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? ''} isDarkMode={customization.isDarkMode} /> )} {inputParam?.acceptVariable && ( <> setShowFormatPromptValuesDialog(false)} onChange={(newValue) => (data.inputs[inputParam.name] = newValue)} > )} )} {inputParam.type === 'options' && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'} /> )} {inputParam.type === 'multiOptions' && ( (data.inputs[inputParam.name] = newValue)} value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'} /> )} {inputParam.type === 'asyncOptions' && ( <> {data.inputParams.length === 1 &&
}
(data.inputs[inputParam.name] = newValue)} onCreateNew={() => addAsyncOption(inputParam.name)} /> {EDITABLE_OPTIONS.includes(inputParam.name) && data.inputs[inputParam.name] && ( editAsyncOption(inputParam.name, data.inputs[inputParam.name])} > )}
)} {(data.name === 'cheerioWebScraper' || data.name === 'puppeteerWebScraper' || data.name === 'playwrightWebScraper') && inputParam.name === 'url' && ( <> setShowManageScrapedLinksDialog(false)} onSave={onManageLinksDialogSave} /> )} )} setAsyncOptionEditDialog('')} onConfirm={onConfirmAsyncOption} > setAsyncOptionEditDialog('')} onConfirm={onConfirmAsyncOption} > setShowExpandDialog(false)} onConfirm={(newValue, inputParamName) => onExpandDialogSave(newValue, inputParamName)} >
) } NodeInputHandler.propTypes = { inputAnchor: PropTypes.object, inputParam: PropTypes.object, data: PropTypes.object, disabled: PropTypes.bool, isAdditionalParams: PropTypes.bool } export default NodeInputHandler