mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 07:01:04 +03:00
Feature/Add multi select option to composio (#4122)
* add multi select option to composio * Update pnpm-lock.yaml
This commit is contained in:
@@ -41,7 +41,7 @@ class Composio_Tools implements INode {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.label = 'Composio'
|
this.label = 'Composio'
|
||||||
this.name = 'composio'
|
this.name = 'composio'
|
||||||
this.version = 1.0
|
this.version = 2.0
|
||||||
this.type = 'Composio'
|
this.type = 'Composio'
|
||||||
this.icon = 'composio.svg'
|
this.icon = 'composio.svg'
|
||||||
this.category = 'Tools'
|
this.category = 'Tools'
|
||||||
@@ -73,7 +73,7 @@ class Composio_Tools implements INode {
|
|||||||
{
|
{
|
||||||
label: 'Actions to Use',
|
label: 'Actions to Use',
|
||||||
name: 'actions',
|
name: 'actions',
|
||||||
type: 'asyncOptions',
|
type: 'asyncMultiOptions',
|
||||||
loadMethod: 'listActions',
|
loadMethod: 'listActions',
|
||||||
description: 'Select the actions you want to use',
|
description: 'Select the actions you want to use',
|
||||||
refresh: true
|
refresh: true
|
||||||
@@ -216,8 +216,18 @@ class Composio_Tools implements INode {
|
|||||||
throw new Error('API Key Required')
|
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 toolset = new LangchainToolSet({ apiKey: composioApiKey })
|
||||||
const tools = await toolset.getTools({ actions: [nodeData.inputs?.actions as string] })
|
const tools = await toolset.getTools({ actions })
|
||||||
return tools
|
return tools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const OMIT_QUEUE_JOB_DATA = ['componentNodes', 'appDataSource', 'sseStrea
|
|||||||
|
|
||||||
export const INPUT_PARAMS_TYPE = [
|
export const INPUT_PARAMS_TYPE = [
|
||||||
'asyncOptions',
|
'asyncOptions',
|
||||||
|
'asyncMultiOptions',
|
||||||
'options',
|
'options',
|
||||||
'multiOptions',
|
'multiOptions',
|
||||||
'datagrid',
|
'datagrid',
|
||||||
|
|||||||
@@ -59,15 +59,27 @@ export const AsyncDropdown = ({
|
|||||||
credentialNames = [],
|
credentialNames = [],
|
||||||
disabled = false,
|
disabled = false,
|
||||||
freeSolo = false,
|
freeSolo = false,
|
||||||
disableClearable = false
|
disableClearable = false,
|
||||||
|
multiple = false
|
||||||
}) => {
|
}) => {
|
||||||
const customization = useSelector((state) => state.customization)
|
const customization = useSelector((state) => state.customization)
|
||||||
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [options, setOptions] = useState([])
|
const [options, setOptions] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value)
|
const findMatchingOptions = (options = [], value) => {
|
||||||
const getDefaultOptionValue = () => ''
|
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-' }]
|
const addNewOption = [{ label: '- Create New -', name: '-create-' }]
|
||||||
let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
|
let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
|
||||||
|
|
||||||
@@ -118,6 +130,8 @@ export const AsyncDropdown = ({
|
|||||||
freeSolo={freeSolo}
|
freeSolo={freeSolo}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
disableClearable={disableClearable}
|
disableClearable={disableClearable}
|
||||||
|
multiple={multiple}
|
||||||
|
filterSelectedOptions={multiple}
|
||||||
size='small'
|
size='small'
|
||||||
sx={{ mt: 1, width: '100%' }}
|
sx={{ mt: 1, width: '100%' }}
|
||||||
open={open}
|
open={open}
|
||||||
@@ -130,12 +144,22 @@ export const AsyncDropdown = ({
|
|||||||
options={options}
|
options={options}
|
||||||
value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()}
|
value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()}
|
||||||
onChange={(e, selection) => {
|
onChange={(e, selection) => {
|
||||||
const value = selection ? selection.name : ''
|
if (multiple) {
|
||||||
if (isCreateNewOption && value === '-create-') {
|
let value = ''
|
||||||
onCreateNew()
|
if (selection.length) {
|
||||||
} else {
|
const selectionNames = selection.map((item) => item.name)
|
||||||
|
value = JSON.stringify(selectionNames)
|
||||||
|
}
|
||||||
setInternalValue(value)
|
setInternalValue(value)
|
||||||
onSelect(value)
|
onSelect(value)
|
||||||
|
} else {
|
||||||
|
const value = selection ? selection.name : ''
|
||||||
|
if (isCreateNewOption && value === '-create-') {
|
||||||
|
onCreateNew()
|
||||||
|
} else {
|
||||||
|
setInternalValue(value)
|
||||||
|
onSelect(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
PopperComponent={StyledPopper}
|
PopperComponent={StyledPopper}
|
||||||
@@ -181,5 +205,6 @@ AsyncDropdown.propTypes = {
|
|||||||
freeSolo: PropTypes.bool,
|
freeSolo: PropTypes.bool,
|
||||||
credentialNames: PropTypes.array,
|
credentialNames: PropTypes.array,
|
||||||
disableClearable: PropTypes.bool,
|
disableClearable: PropTypes.bool,
|
||||||
isCreateNewOption: PropTypes.bool
|
isCreateNewOption: PropTypes.bool,
|
||||||
|
multiple: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const initNode = (nodeData, newNodeId) => {
|
|||||||
|
|
||||||
const whitelistTypes = [
|
const whitelistTypes = [
|
||||||
'asyncOptions',
|
'asyncOptions',
|
||||||
|
'asyncMultiOptions',
|
||||||
'options',
|
'options',
|
||||||
'multiOptions',
|
'multiOptions',
|
||||||
'datagrid',
|
'datagrid',
|
||||||
|
|||||||
@@ -795,16 +795,17 @@ const NodeInputHandler = ({
|
|||||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
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 }} />}
|
{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
|
<AsyncDropdown
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
name={inputParam.name}
|
name={inputParam.name}
|
||||||
nodeData={data}
|
nodeData={data}
|
||||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
||||||
freeSolo={inputParam.freeSolo}
|
freeSolo={inputParam.freeSolo}
|
||||||
|
multiple={inputParam.type === 'asyncMultiOptions'}
|
||||||
isCreateNewOption={EDITABLE_OPTIONS.includes(inputParam.name)}
|
isCreateNewOption={EDITABLE_OPTIONS.includes(inputParam.name)}
|
||||||
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
||||||
onCreateNew={() => addAsyncOption(inputParam.name)}
|
onCreateNew={() => addAsyncOption(inputParam.name)}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useSelector } from 'react-redux'
|
|||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { Box, Typography, IconButton, Button } from '@mui/material'
|
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
|
// project import
|
||||||
import { Dropdown } from '@/ui-component/dropdown/Dropdown'
|
import { Dropdown } from '@/ui-component/dropdown/Dropdown'
|
||||||
@@ -33,6 +33,7 @@ const DocStoreInputHandler = ({ inputParam, data, disabled = false }) => {
|
|||||||
const [expandDialogProps, setExpandDialogProps] = useState({})
|
const [expandDialogProps, setExpandDialogProps] = useState({})
|
||||||
const [showManageScrapedLinksDialog, setShowManageScrapedLinksDialog] = useState(false)
|
const [showManageScrapedLinksDialog, setShowManageScrapedLinksDialog] = useState(false)
|
||||||
const [manageScrapedLinksDialogProps, setManageScrapedLinksDialogProps] = useState({})
|
const [manageScrapedLinksDialogProps, setManageScrapedLinksDialogProps] = useState({})
|
||||||
|
const [reloadTimestamp, setReloadTimestamp] = useState(Date.now().toString())
|
||||||
|
|
||||||
const onExpandDialogClicked = (value, inputParam) => {
|
const onExpandDialogClicked = (value, inputParam) => {
|
||||||
const dialogProps = {
|
const dialogProps = {
|
||||||
@@ -216,19 +217,33 @@ const DocStoreInputHandler = ({ inputParam, data, disabled = false }) => {
|
|||||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
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 }} />}
|
{data.inputParams?.length === 1 && <div style={{ marginTop: 10 }} />}
|
||||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||||
<AsyncDropdown
|
<div key={reloadTimestamp} style={{ flex: 1 }}>
|
||||||
key={JSON.stringify(inputParam)}
|
<AsyncDropdown
|
||||||
disabled={disabled}
|
key={JSON.stringify(inputParam)}
|
||||||
name={inputParam.name}
|
disabled={disabled}
|
||||||
nodeData={data}
|
name={inputParam.name}
|
||||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
|
nodeData={data}
|
||||||
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
freeSolo={inputParam.freeSolo}
|
||||||
onCreateNew={() => addAsyncOption(inputParam.name)}
|
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>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user