Skip to content

Commit 41dbb7f

Browse files
committed
add multi options
1 parent e80ec27 commit 41dbb7f

File tree

7 files changed

+249
-8
lines changed

7 files changed

+249
-8
lines changed

packages/components/nodes/documentloaders/Unstructured/UnstructuredFile.ts

+72-3
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,80 @@ class UnstructuredFile_DocumentLoaders implements INode {
4545
type: 'string',
4646
default: 'http://localhost:8000/general/v0/general'
4747
},
48+
{
49+
label: 'Element Type',
50+
name: 'elementType',
51+
description:
52+
'Unstructured partition document into different types, select the types to return. If not selected, all types will be returned',
53+
type: 'multiOptions',
54+
options: [
55+
{
56+
label: 'FigureCaption',
57+
name: 'FigureCaption'
58+
},
59+
{
60+
label: 'NarrativeText',
61+
name: 'NarrativeText'
62+
},
63+
{
64+
label: 'ListItem',
65+
name: 'ListItem'
66+
},
67+
{
68+
label: 'Title',
69+
name: 'Title'
70+
},
71+
{
72+
label: 'Address',
73+
name: 'Address'
74+
},
75+
{
76+
label: 'Table',
77+
name: 'Table'
78+
},
79+
{
80+
label: 'PageBreak',
81+
name: 'PageBreak'
82+
},
83+
{
84+
label: 'Header',
85+
name: 'Header'
86+
},
87+
{
88+
label: 'Footer',
89+
name: 'Footer'
90+
},
91+
{
92+
label: 'UncategorizedText',
93+
name: 'UncategorizedText'
94+
},
95+
{
96+
label: 'Image',
97+
name: 'Image'
98+
},
99+
{
100+
label: 'Formula',
101+
name: 'Formula'
102+
}
103+
],
104+
default: [],
105+
optional: true,
106+
additionalParams: true
107+
},
48108
{
49109
label: 'Metadata',
50110
name: 'metadata',
51111
type: 'json',
52112
optional: true,
53113
additionalParams: true
54114
}
55-
/*TODO Add Filter Options*/
56115
]
57116
}
58117

59118
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
60119
const filePath = nodeData.inputs?.filePath as string
61120
const unstructuredAPIUrl = nodeData.inputs?.unstructuredAPIUrl as string
121+
const elementType = nodeData.inputs?.elementType as string
62122
const metadata = nodeData.inputs?.metadata
63123

64124
const obj: UnstructuredLoaderOptions = { apiUrl: unstructuredAPIUrl }
@@ -70,6 +130,15 @@ class UnstructuredFile_DocumentLoaders implements INode {
70130
const loader = new UnstructuredLoader(filePath, obj)
71131
const docs = await loader.load()
72132

133+
let elementTypes: string[] = []
134+
if (elementType) {
135+
try {
136+
elementTypes = JSON.parse(elementType)
137+
} catch (e) {
138+
elementTypes = []
139+
}
140+
}
141+
73142
if (metadata) {
74143
const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata)
75144
let finaldocs = []
@@ -83,10 +152,10 @@ class UnstructuredFile_DocumentLoaders implements INode {
83152
}
84153
finaldocs.push(newdoc)
85154
}
86-
return finaldocs
155+
return elementTypes.length ? finaldocs.filter((doc) => elementTypes.includes(doc.metadata.category)) : finaldocs
87156
}
88157

89-
return docs
158+
return elementTypes.length ? docs.filter((doc) => elementTypes.includes(doc.metadata.category)) : docs
90159
}
91160
}
92161

packages/components/nodes/documentloaders/Unstructured/UnstructuredFolder.ts

+72-3
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,81 @@ class UnstructuredFolder_DocumentLoaders implements INode {
4545
type: 'string',
4646
default: 'http://localhost:8000/general/v0/general'
4747
},
48+
{
49+
label: 'Element Type',
50+
name: 'elementType',
51+
description:
52+
'Unstructured partition document into different types, select the types to return. If not selected, all types will be returned',
53+
type: 'multiOptions',
54+
options: [
55+
{
56+
label: 'FigureCaption',
57+
name: 'FigureCaption'
58+
},
59+
{
60+
label: 'NarrativeText',
61+
name: 'NarrativeText'
62+
},
63+
{
64+
label: 'ListItem',
65+
name: 'ListItem'
66+
},
67+
{
68+
label: 'Title',
69+
name: 'Title'
70+
},
71+
{
72+
label: 'Address',
73+
name: 'Address'
74+
},
75+
{
76+
label: 'Table',
77+
name: 'Table'
78+
},
79+
{
80+
label: 'PageBreak',
81+
name: 'PageBreak'
82+
},
83+
{
84+
label: 'Header',
85+
name: 'Header'
86+
},
87+
{
88+
label: 'Footer',
89+
name: 'Footer'
90+
},
91+
{
92+
label: 'UncategorizedText',
93+
name: 'UncategorizedText'
94+
},
95+
{
96+
label: 'Image',
97+
name: 'Image'
98+
},
99+
{
100+
label: 'Formula',
101+
name: 'Formula'
102+
}
103+
],
104+
default: [],
105+
optional: true,
106+
additionalParams: true
107+
},
48108
{
49109
label: 'Metadata',
50110
name: 'metadata',
51111
type: 'json',
52112
optional: true,
53113
additionalParams: true
54114
}
55-
/*TODO Add Filter Options*/
56115
]
57116
}
58117

59118
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
60119
const folderPath = nodeData.inputs?.folderPath as string
61120
const unstructuredAPIUrl = nodeData.inputs?.unstructuredAPIUrl as string
62121
const metadata = nodeData.inputs?.metadata
122+
const elementType = nodeData.inputs?.elementType as string
63123

64124
const obj: UnstructuredLoaderOptions = { apiUrl: unstructuredAPIUrl }
65125

@@ -70,6 +130,15 @@ class UnstructuredFolder_DocumentLoaders implements INode {
70130
const loader = new UnstructuredDirectoryLoader(folderPath, obj)
71131
const docs = await loader.load()
72132

133+
let elementTypes: string[] = []
134+
if (elementType) {
135+
try {
136+
elementTypes = JSON.parse(elementType)
137+
} catch (e) {
138+
elementTypes = []
139+
}
140+
}
141+
73142
if (metadata) {
74143
const parsedMetadata = typeof metadata === 'object' ? metadata : JSON.parse(metadata)
75144
let finaldocs = []
@@ -83,10 +152,10 @@ class UnstructuredFolder_DocumentLoaders implements INode {
83152
}
84153
finaldocs.push(newdoc)
85154
}
86-
return finaldocs
155+
return elementTypes.length ? finaldocs.filter((doc) => elementTypes.includes(doc.metadata.category)) : finaldocs
87156
}
88157

89-
return docs
158+
return elementTypes.length ? docs.filter((doc) => elementTypes.includes(doc.metadata.category)) : docs
90159
}
91160
}
92161

packages/components/nodes/vectorstores/Elasticsearch/Elasticsearch_Existing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ElasicsearchExisting_VectorStores extends ElasticSearchBase implements INo
1818
async constructVectorStore(
1919
embeddings: Embeddings,
2020
elasticSearchClientArgs: ElasticClientArgs,
21-
docs: Document<Record<string, any>>[] | undefined
21+
_: Document<Record<string, any>>[] | undefined
2222
): Promise<VectorStore> {
2323
return await ElasticVectorSearch.fromExistingIndex(embeddings, elasticSearchClientArgs)
2424
}

packages/components/src/Interface.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
export type NodeParamsType =
66
| 'asyncOptions'
77
| 'options'
8+
| 'multiOptions'
89
| 'string'
910
| 'number'
1011
| 'boolean'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { useState } from 'react'
2+
import { useSelector } from 'react-redux'
3+
4+
import { Popper, FormControl, TextField, Box, Typography } from '@mui/material'
5+
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'
6+
import { styled } from '@mui/material/styles'
7+
import PropTypes from 'prop-types'
8+
9+
const StyledPopper = styled(Popper)({
10+
boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',
11+
borderRadius: '10px',
12+
[`& .${autocompleteClasses.listbox}`]: {
13+
boxSizing: 'border-box',
14+
'& ul': {
15+
padding: 10,
16+
margin: 10
17+
}
18+
}
19+
})
20+
21+
export const MultiDropdown = ({ name, value, options, onSelect, disabled = false, disableClearable = false }) => {
22+
const customization = useSelector((state) => state.customization)
23+
const findMatchingOptions = (options = [], internalValue) => {
24+
let values = []
25+
if (internalValue && typeof internalValue === 'string') values = JSON.parse(internalValue)
26+
else values = internalValue
27+
return options.filter((option) => values.includes(option.name))
28+
}
29+
const getDefaultOptionValue = () => []
30+
let [internalValue, setInternalValue] = useState(value ?? [])
31+
32+
return (
33+
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
34+
<Autocomplete
35+
id={name}
36+
disabled={disabled}
37+
disableClearable={disableClearable}
38+
size='small'
39+
multiple
40+
filterSelectedOptions
41+
options={options || []}
42+
value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()}
43+
onChange={(e, selections) => {
44+
let value = ''
45+
if (selections.length) {
46+
const selectionNames = []
47+
for (let i = 0; i < selections.length; i += 1) {
48+
selectionNames.push(selections[i].name)
49+
}
50+
value = JSON.stringify(selectionNames)
51+
}
52+
setInternalValue(value)
53+
onSelect(value)
54+
}}
55+
PopperComponent={StyledPopper}
56+
renderInput={(params) => <TextField {...params} value={internalValue} />}
57+
renderOption={(props, option) => (
58+
<Box component='li' {...props}>
59+
<div style={{ display: 'flex', flexDirection: 'column' }}>
60+
<Typography variant='h5'>{option.label}</Typography>
61+
{option.description && (
62+
<Typography sx={{ color: customization.isDarkMode ? '#9e9e9e' : '' }}>{option.description}</Typography>
63+
)}
64+
</div>
65+
</Box>
66+
)}
67+
/>
68+
</FormControl>
69+
)
70+
}
71+
72+
MultiDropdown.propTypes = {
73+
name: PropTypes.string,
74+
value: PropTypes.string,
75+
options: PropTypes.array,
76+
onSelect: PropTypes.func,
77+
disabled: PropTypes.bool,
78+
disableClearable: PropTypes.bool
79+
}

packages/ui/src/utils/genericHelper.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,20 @@ export const initNode = (nodeData, newNodeId) => {
3939
const incoming = nodeData.inputs ? nodeData.inputs.length : 0
4040
const outgoing = 1
4141

42-
const whitelistTypes = ['asyncOptions', 'options', 'string', 'number', 'boolean', 'password', 'json', 'code', 'date', 'file', 'folder']
42+
const whitelistTypes = [
43+
'asyncOptions',
44+
'options',
45+
'multiOptions',
46+
'string',
47+
'number',
48+
'boolean',
49+
'password',
50+
'json',
51+
'code',
52+
'date',
53+
'file',
54+
'folder'
55+
]
4356

4457
// Inputs
4558
for (let i = 0; i < incoming; i += 1) {

packages/ui/src/views/canvas/NodeInputHandler.js

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { IconArrowsMaximize, IconEdit, IconAlertTriangle } from '@tabler/icons'
1111

1212
// project import
1313
import { Dropdown } from 'ui-component/dropdown/Dropdown'
14+
import { MultiDropdown } from 'ui-component/dropdown/MultiDropdown'
1415
import { AsyncDropdown } from 'ui-component/dropdown/AsyncDropdown'
1516
import { Input } from 'ui-component/input/Input'
1617
import { File } from 'ui-component/file/File'
@@ -308,6 +309,15 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
308309
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
309310
/>
310311
)}
312+
{inputParam.type === 'multiOptions' && (
313+
<MultiDropdown
314+
disabled={disabled}
315+
name={inputParam.name}
316+
options={inputParam.options}
317+
onSelect={(newValue) => (data.inputs[inputParam.name] = newValue)}
318+
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'choose an option'}
319+
/>
320+
)}
311321
{inputParam.type === 'asyncOptions' && (
312322
<>
313323
{data.inputParams.length === 1 && <div style={{ marginTop: 10 }} />}

0 commit comments

Comments
 (0)