Feature/Add multi select option to composio (#4122)

* add multi select option to composio

* Update pnpm-lock.yaml
This commit is contained in:
Henry Heng
2025-03-06 09:59:33 +00:00
committed by GitHub
parent fc9d6e7a16
commit 9c22bee991
6 changed files with 77 additions and 24 deletions
@@ -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
}
}
+1
View File
@@ -25,6 +25,7 @@ export const OMIT_QUEUE_JOB_DATA = ['componentNodes', 'appDataSource', 'sseStrea
export const INPUT_PARAMS_TYPE = [
'asyncOptions',
'asyncMultiOptions',
'options',
'multiOptions',
'datagrid',
@@ -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
}
+1
View File
@@ -35,6 +35,7 @@ export const initNode = (nodeData, newNodeId) => {
const whitelistTypes = [
'asyncOptions',
'asyncMultiOptions',
'options',
'multiOptions',
'datagrid',
@@ -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 && <div style={{ marginTop: 10 }} />}
<div key={reloadTimestamp} style={{ display: 'flex', flexDirection: 'row', alignContent: 'center' }}>
<div key={reloadTimestamp} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 1 }}>
<AsyncDropdown
disabled={disabled}
name={inputParam.name}
nodeData={data}
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
freeSolo={inputParam.freeSolo}
multiple={inputParam.type === 'asyncMultiOptions'}
isCreateNewOption={EDITABLE_OPTIONS.includes(inputParam.name)}
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
onCreateNew={() => addAsyncOption(inputParam.name)}
@@ -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 && <div style={{ marginTop: 10 }} />}
<div style={{ display: 'flex', flexDirection: 'row' }}>
<AsyncDropdown
key={JSON.stringify(inputParam)}
disabled={disabled}
name={inputParam.name}
nodeData={data}
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
onCreateNew={() => addAsyncOption(inputParam.name)}
/>
<div key={reloadTimestamp} style={{ flex: 1 }}>
<AsyncDropdown
key={JSON.stringify(inputParam)}
disabled={disabled}
name={inputParam.name}
nodeData={data}
freeSolo={inputParam.freeSolo}
multiple={inputParam.type === 'asyncMultiOptions'}
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
onCreateNew={() => addAsyncOption(inputParam.name)}
/>
</div>
{inputParam.refresh && (
<IconButton
title='Refresh'
color='primary'
size='small'
onClick={() => setReloadTimestamp(Date.now().toString())}
>
<IconRefresh />
</IconButton>
)}
</div>
</>
)}