diff --git a/packages/components/nodes/tools/Composio/Composio.ts b/packages/components/nodes/tools/Composio/Composio.ts index 8b666fd1..e08ad9ac 100644 --- a/packages/components/nodes/tools/Composio/Composio.ts +++ b/packages/components/nodes/tools/Composio/Composio.ts @@ -41,7 +41,7 @@ class Composio_Tools implements INode { constructor() { this.label = 'Composio' this.name = 'composio' - this.version = 1.0 + this.version = 2.0 this.type = 'Composio' this.icon = 'composio.svg' this.category = 'Tools' @@ -73,7 +73,7 @@ class Composio_Tools implements INode { { label: 'Actions to Use', name: 'actions', - type: 'asyncOptions', + type: 'asyncMultiOptions', loadMethod: 'listActions', description: 'Select the actions you want to use', refresh: true @@ -216,8 +216,18 @@ class Composio_Tools implements INode { throw new Error('API Key Required') } + const _actions = nodeData.inputs?.actions + let actions = [] + if (_actions) { + try { + actions = typeof _actions === 'string' ? JSON.parse(_actions) : _actions + } catch (error) { + console.error('Error parsing actions:', error) + } + } + const toolset = new LangchainToolSet({ apiKey: composioApiKey }) - const tools = await toolset.getTools({ actions: [nodeData.inputs?.actions as string] }) + const tools = await toolset.getTools({ actions }) return tools } } diff --git a/packages/server/src/utils/constants.ts b/packages/server/src/utils/constants.ts index f84587b7..1290cbd2 100644 --- a/packages/server/src/utils/constants.ts +++ b/packages/server/src/utils/constants.ts @@ -25,6 +25,7 @@ export const OMIT_QUEUE_JOB_DATA = ['componentNodes', 'appDataSource', 'sseStrea export const INPUT_PARAMS_TYPE = [ 'asyncOptions', + 'asyncMultiOptions', 'options', 'multiOptions', 'datagrid', diff --git a/packages/ui/src/ui-component/dropdown/AsyncDropdown.jsx b/packages/ui/src/ui-component/dropdown/AsyncDropdown.jsx index 4469fdf9..243fd013 100644 --- a/packages/ui/src/ui-component/dropdown/AsyncDropdown.jsx +++ b/packages/ui/src/ui-component/dropdown/AsyncDropdown.jsx @@ -59,15 +59,27 @@ export const AsyncDropdown = ({ credentialNames = [], disabled = false, freeSolo = false, - disableClearable = false + disableClearable = false, + multiple = false }) => { const customization = useSelector((state) => state.customization) const [open, setOpen] = useState(false) const [options, setOptions] = useState([]) const [loading, setLoading] = useState(false) - const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value) - const getDefaultOptionValue = () => '' + const findMatchingOptions = (options = [], value) => { + if (multiple) { + let values = [] + if ('choose an option' !== value && value && typeof value === 'string') { + values = JSON.parse(value) + } else { + values = value + } + return options.filter((option) => values.includes(option.name)) + } + return options.find((option) => option.name === value) + } + const getDefaultOptionValue = () => (multiple ? [] : '') const addNewOption = [{ label: '- Create New -', name: '-create-' }] let [internalValue, setInternalValue] = useState(value ?? 'choose an option') @@ -118,6 +130,8 @@ export const AsyncDropdown = ({ freeSolo={freeSolo} disabled={disabled} disableClearable={disableClearable} + multiple={multiple} + filterSelectedOptions={multiple} size='small' sx={{ mt: 1, width: '100%' }} open={open} @@ -130,12 +144,22 @@ export const AsyncDropdown = ({ options={options} value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()} onChange={(e, selection) => { - const value = selection ? selection.name : '' - if (isCreateNewOption && value === '-create-') { - onCreateNew() - } else { + if (multiple) { + let value = '' + if (selection.length) { + const selectionNames = selection.map((item) => item.name) + value = JSON.stringify(selectionNames) + } setInternalValue(value) onSelect(value) + } else { + const value = selection ? selection.name : '' + if (isCreateNewOption && value === '-create-') { + onCreateNew() + } else { + setInternalValue(value) + onSelect(value) + } } }} PopperComponent={StyledPopper} @@ -181,5 +205,6 @@ AsyncDropdown.propTypes = { freeSolo: PropTypes.bool, credentialNames: PropTypes.array, disableClearable: PropTypes.bool, - isCreateNewOption: PropTypes.bool + isCreateNewOption: PropTypes.bool, + multiple: PropTypes.bool } diff --git a/packages/ui/src/utils/genericHelper.js b/packages/ui/src/utils/genericHelper.js index 5b6e87a9..32cfddb5 100644 --- a/packages/ui/src/utils/genericHelper.js +++ b/packages/ui/src/utils/genericHelper.js @@ -35,6 +35,7 @@ export const initNode = (nodeData, newNodeId) => { const whitelistTypes = [ 'asyncOptions', + 'asyncMultiOptions', 'options', 'multiOptions', 'datagrid', diff --git a/packages/ui/src/views/canvas/NodeInputHandler.jsx b/packages/ui/src/views/canvas/NodeInputHandler.jsx index 87adb2c9..b21592f0 100644 --- a/packages/ui/src/views/canvas/NodeInputHandler.jsx +++ b/packages/ui/src/views/canvas/NodeInputHandler.jsx @@ -795,16 +795,17 @@ const NodeInputHandler = ({ value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'} /> )} - {inputParam.type === 'asyncOptions' && ( + {(inputParam.type === 'asyncOptions' || inputParam.type === 'asyncMultiOptions') && ( <> {data.inputParams.length === 1 &&
} -
+
(data.inputs[inputParam.name] = newValue)} onCreateNew={() => addAsyncOption(inputParam.name)} diff --git a/packages/ui/src/views/docstore/DocStoreInputHandler.jsx b/packages/ui/src/views/docstore/DocStoreInputHandler.jsx index 97c307ff..7c6b3e52 100644 --- a/packages/ui/src/views/docstore/DocStoreInputHandler.jsx +++ b/packages/ui/src/views/docstore/DocStoreInputHandler.jsx @@ -4,7 +4,7 @@ import { useSelector } from 'react-redux' // material-ui import { Box, Typography, IconButton, Button } from '@mui/material' -import { IconArrowsMaximize, IconAlertTriangle } from '@tabler/icons-react' +import { IconRefresh, IconArrowsMaximize, IconAlertTriangle } from '@tabler/icons-react' // project import import { Dropdown } from '@/ui-component/dropdown/Dropdown' @@ -33,6 +33,7 @@ const DocStoreInputHandler = ({ inputParam, data, disabled = false }) => { const [expandDialogProps, setExpandDialogProps] = useState({}) const [showManageScrapedLinksDialog, setShowManageScrapedLinksDialog] = useState(false) const [manageScrapedLinksDialogProps, setManageScrapedLinksDialogProps] = useState({}) + const [reloadTimestamp, setReloadTimestamp] = useState(Date.now().toString()) const onExpandDialogClicked = (value, inputParam) => { const dialogProps = { @@ -216,19 +217,33 @@ const DocStoreInputHandler = ({ inputParam, data, disabled = false }) => { value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'} /> )} - {inputParam.type === 'asyncOptions' && ( + {(inputParam.type === 'asyncOptions' || inputParam.type === 'asyncMultiOptions') && ( <> {data.inputParams?.length === 1 &&
}
- (data.inputs[inputParam.name] = newValue)} - onCreateNew={() => addAsyncOption(inputParam.name)} - /> +
+ (data.inputs[inputParam.name] = newValue)} + onCreateNew={() => addAsyncOption(inputParam.name)} + /> +
+ {inputParam.refresh && ( + setReloadTimestamp(Date.now().toString())} + > + + + )}
)}