feat(web): workflow variable type

This commit is contained in:
zhaoying
2026-04-10 16:24:36 +08:00
parent d517bceda2
commit 9a926a8398
11 changed files with 356 additions and 67 deletions

View File

@@ -2342,6 +2342,11 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
"eq": 'Is',
"ne": 'Is Not',
},
file: {
"empty": 'Not Exist',
"not_empty": 'Exists',
eq: 'All Are'
},
else_desc: 'Used to define the logic that should be executed when the if condition is not met.',
unset: 'Condition Not Set',
set: 'Set',

View File

@@ -2303,6 +2303,11 @@ export const zh = {
"eq": '是',
"ne": '不是',
},
file: {
"empty": '不存在',
"not_empty": '存在',
eq: '全都是'
},
else_desc: '用于定义当 if 条件不满足时应执行的逻辑。',
unset: '条件未设置',
set: '已设置',

View File

@@ -5,7 +5,7 @@
* @Last Modified time: 2026-04-08 11:05:34
*/
import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react';
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex, Spin } from 'antd';
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';

View File

@@ -328,7 +328,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
};
const content = (
<Flex vertical gap={16} className="rb:max-h-[300px] rb:overflow-y-auto rb:p-3" style={{ minWidth: `${nodeWidth}px` }}>
<Flex vertical gap={16} className="rb:max-h-75 rb:overflow-y-auto rb:p-3" style={{ minWidth: `${nodeWidth}px` }}>
{nodeLibrary.map((category) => {
const sourceNodeData = sourceNode?.getData();
const isChildOfLoop = sourceNodeData?.cycle && graph?.getNodes().find((n: any) => n.getData()?.id === sourceNodeData.cycle && n.getData()?.type === 'loop');

View File

@@ -4,7 +4,7 @@
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 15:23:45
*/
import { type FC } from 'react'
import { useMemo, type FC } from 'react'
import clsx from 'clsx'
import { useTranslation } from 'react-i18next';
import { Form, Button, Select, Space, Divider, InputNumber, type SelectProps, Flex, Row, Col } from 'antd'
@@ -15,7 +15,7 @@ import Editor from '../../Editor'
import { edgeAttrs, nodeWidth } from '../../../constant'
import RbButton from '@/components/RbButton';
import RadioGroupBtn from '../RadioGroupBtn'
import { calcConditionNodeTotalHeight, getConditionNodeCasePortY } from '../../../utils'
import { calcConditionNodeTotalHeight, getConditionNodeCasePortY } from '../../../utils';
interface CaseListProps {
value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>;
@@ -49,6 +49,34 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
boolean: [
{ value: 'eq', label: 'workflow.config.if-else.boolean.eq' },
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
],
object: [
{ value: 'eq', label: 'workflow.config.if-else.boolean.eq' },
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
],
file: [
{ value: 'empty', label: 'workflow.config.if-else.file.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.file.not_empty' },
],
// TODO包含、不包含、全都是
'array[file]': [
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
// { value: 'eq', label: 'workflow.config.if-else.eq' },
// { value: 'contains', label: 'workflow.config.if-else.contains' },
// { value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
],
'array': [
{ value: 'contains', label: 'workflow.config.if-else.contains' },
{ value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
],
'array[object]': [
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
]
}
@@ -247,6 +275,22 @@ const CaseList: FC<CaseListProps> = ({
form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], undefined);
};
const filterNumberOptions = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(vo => {
if (vo.children && vo.children?.length > 0) {
filterList.push({
...vo,
children: vo.children.filter(child => child.dataType === 'number')
})
} else if (vo.dataType === 'number') {
filterList.push(vo)
}
})
return filterList
}, [options])
return (
<>
<Form.List name={name}>
@@ -284,11 +328,15 @@ const CaseList: FC<CaseListProps> = ({
const currentCase = cases[caseIndex] || {};
const currentExpression = currentCase.expressions?.[conditionIndex] || {};
const currentOperator = currentExpression.operator;
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty';
const leftFieldValue = currentExpression.left;
const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue);
const leftFieldType = leftFieldOption?.dataType;
const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || [];
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty' || leftFieldType === 'file' || leftFieldType === 'array[object]' || leftFieldType === 'array[file]';
const operatorList = leftFieldType && operatorsObj[leftFieldType]
? operatorsObj[leftFieldType]
: leftFieldType && leftFieldType?.includes('array')
? operatorsObj.array
: operatorsObj.default;
const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined;
return (
<Flex key={conditionField.key} gap={4} align="start" className="rb:mb-2!">
@@ -312,7 +360,7 @@ const CaseList: FC<CaseListProps> = ({
<Col flex="1">
<Form.Item name={[conditionField.name, 'operator']} noStyle>
<Select
options={operatorList.map(vo => ({
options={(operatorList ?? []).map(vo => ({
...vo,
label: t(String(vo?.label || ''))
}))}
@@ -328,7 +376,9 @@ const CaseList: FC<CaseListProps> = ({
{!hideRightField && (
<div className="rb:py-1 rb:px-1.5">
{leftFieldType === 'number'
{leftFieldType === 'array[file]'
? <>TODO</>
: leftFieldType === 'number'
? <Flex align="center">
<Form.Item name={[conditionField.name, 'input_type']} noStyle>
<Select
@@ -345,24 +395,24 @@ const CaseList: FC<CaseListProps> = ({
{inputType === 'variable'
? <VariableSelect
placeholder={t('common.pleaseSelect')}
options={options.filter(vo => vo.dataType === 'number')}
options={filterNumberOptions}
allowClear={false}
variant="borderless"
size="small"
/>
: <InputNumber
placeholder={t('common.pleaseEnter')}
variant="borderless"
className="rb:w-full!"
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
/>
placeholder={t('common.pleaseEnter')}
variant="borderless"
className="rb:w-full!"
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
/>
}
</Form.Item>
</Flex>
: (
<Form.Item name={[conditionField.name, 'right']} noStyle>
{leftFieldType === 'boolean'
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
{['boolean', 'array[boolean]'].includes(leftFieldType as string)
? <RadioGroupBtn options={[{ value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
: <Editor options={options} size="small" type="input" />
}
</Form.Item>

View File

@@ -1,4 +1,4 @@
import { type FC } from 'react'
import { type FC, useMemo } from 'react'
import clsx from 'clsx'
import { useTranslation } from 'react-i18next';
import { Form, Button, Select, InputNumber, Input, Divider, type SelectProps, Flex, Space, Row, Col } from 'antd'
@@ -47,6 +47,18 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
],
// 为空、不为空
object: [
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
],
// 包含、不包含、为空、不为空
'array': [
{ value: 'contains', label: 'workflow.config.if-else.contains' },
{ value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
]
}
@@ -81,6 +93,23 @@ const ConditionList: FC<CaseListProps> = ({
const currentValue = form.getFieldValue([parentName, 'logical_operator']);
form.setFieldValue([parentName, 'logical_operator'], currentValue === 'and' ? 'or' : 'and');
};
const getNumVariable = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(variable => {
if (variable.dataType === 'number') {
filterList.push(variable)
} else if (variable.dataType === 'file') {
filterList.push({
...variable,
disabled: true,
children: variable.children?.filter(child => child.dataType === 'number')
})
}
})
return filterList
}, [options])
return (
<>
<Form.List name={[parentName, 'expressions']}>
@@ -125,11 +154,19 @@ const ConditionList: FC<CaseListProps> = ({
const expressions = form.getFieldValue([parentName, 'expressions']) || [];
const currentExpression = expressions[index] || {};
const currentOperator = currentExpression.operator;
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty';
const leftFieldValue = currentExpression.left;
const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue);
const leftFieldType = leftFieldOption?.dataType;
const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || [];
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty' || ['array[object]', 'object'].includes(leftFieldType as string);
const operatorList = leftFieldType && ['array[object]', 'object'].includes(leftFieldType)
? operatorsObj.object
: leftFieldType && ['array[boolean]', 'boolean'].includes(leftFieldType)
? operatorsObj.boolean
: leftFieldType && operatorsObj[leftFieldType]
? operatorsObj[leftFieldType]
: leftFieldType?.includes('array')
? operatorsObj.array
: operatorsObj.default
const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined;
return (
<Flex
@@ -146,10 +183,11 @@ const ConditionList: FC<CaseListProps> = ({
<Form.Item name={[field.name, 'left']} noStyle>
<VariableSelect
options={options.filter(vo =>
vo.value.includes('sys.') ||
!['file', 'array[file]'].includes(vo.dataType) &&
(vo.value.includes('sys.') ||
vo.value.includes('conv.') ||
vo.nodeData.type === 'loop' ||
(vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id)
(vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id))
)}
size="small"
allowClear={false}
@@ -163,7 +201,7 @@ const ConditionList: FC<CaseListProps> = ({
<Col flex="96px">
<Form.Item name={[field.name, 'operator']} noStyle>
<Select
options={operatorList.map(vo => ({
options={(operatorList??[]).map(vo => ({
...vo,
label: t(String(vo?.label || ''))
}))}
@@ -198,7 +236,7 @@ const ConditionList: FC<CaseListProps> = ({
? (
<VariableSelect
placeholder={t('common.pleaseSelect')}
options={options.filter(vo => vo.dataType === 'number')}
options={getNumVariable}
allowClear={false}
variant="borderless"
size="small"
@@ -219,7 +257,7 @@ const ConditionList: FC<CaseListProps> = ({
: (
<Form.Item name={[field.name, 'right']} noStyle>
{leftFieldType === 'boolean'
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} />
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
: <Input variant="borderless" placeholder={t('common.pleaseEnter')} />
}
</Form.Item>

View File

@@ -6,6 +6,7 @@ import VariableSelect from '../VariableSelect'
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
import RadioGroupBtn from '../RadioGroupBtn'
import { getChildNodeVariables } from '../hooks/useVariableList'
import CodeMirrorEditor from '@/components/CodeMirrorEditor';
interface CycleVar {
name: string;
@@ -28,11 +29,17 @@ const types = [
'string',
'number',
'boolean',
'object',
'array[string]',
'array[number]',
'array[boolean]',
'array[object]'
]
const object_placeholder = `# example
# {
# "name": "redbear",
# "age": 2
# }`
const CycleVarsList: FC<CycleVarsListProps> = ({
value = [],
@@ -144,6 +151,13 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
{ value: true, label: 'True' },
{ value: false, label: 'False' }]}
/>
: currentType === 'object'
? <CodeMirrorEditor
language="json"
placeholder={object_placeholder}
variant="outlined"
size="small"
/>
: (
<Input.TextArea
placeholder={t('common.pleaseEnter')}

View File

@@ -45,6 +45,45 @@ const EditableTable: FC<EditableTableProps> = ({
: options
}, [options, filterBooleanType])
const namefilterOptions = useMemo(() => {
const filterList: Suggestion[] = [];
options.forEach(vo => {
if (vo.dataType === 'file') {
filterList.push({
...vo,
disabled: true,
children: vo.children?.filter(child => child.dataType !== 'boolean')
})
} else if (vo.dataType !== 'array[file]') {
filterList.push(vo)
}
})
return filterList
}, [options])
const valueFilterOptions = (type?: string) => {
let filterOptions: Suggestion[] = []
options.forEach(vo => {
if (type === 'file' && vo.dataType === 'file') {
filterOptions.push({
...vo,
children: []
})
} else if (type === 'file' && vo.dataType === 'array[file]') {
filterOptions.push(vo)
} else if (vo.dataType === 'file') {
filterOptions.push({
...vo,
disabled: true
})
} else {
filterOptions.push(vo)
}
})
return filterOptions
}
const getColumns = (remove: (index: number) => void): TableProps<TableRow>['columns'] => {
const hasType = typeOptions.length > 0;
const contentClassName = hasType ? 'rb:w-[110px]!' : "rb:w-[148px]!"
@@ -57,7 +96,7 @@ const EditableTable: FC<EditableTableProps> = ({
render: (_: any, __: TableRow, index: number) => (
<Form.Item name={[index, 'name']} className={formClassName}>
<Editor
options={booleanFilterOptions.filter(option => !option.dataType.includes('file'))}
options={namefilterOptions}
type="input"
className={contentClassName}
size={size}
@@ -105,9 +144,7 @@ const EditableTable: FC<EditableTableProps> = ({
>
{(form) => {
const currentType = form.getFieldValue([...Array.isArray(parentName) ? parentName : [parentName], index, 'type']);
const filteredOptions = currentType === 'file'
? booleanFilterOptions.filter(option => option.dataType.includes('file'))
: booleanFilterOptions.filter(option => !option.dataType.includes('file'));
const filteredOptions = valueFilterOptions(currentType)
return (
<Form.Item name={[index, 'value']} className={formClassName}>

View File

@@ -4,7 +4,7 @@
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-04-02 17:17:06
*/
import { type FC, useRef, useState } from "react";
import { type FC, useMemo, useRef, useState } from "react";
import { useTranslation } from 'react-i18next'
import { Form, Row, Col, Select, Button, Divider, InputNumber, Switch, Input, Flex, Radio } from 'antd'
import { CaretDownOutlined, CaretRightOutlined, SettingOutlined } from '@ant-design/icons';
@@ -84,6 +84,64 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
setCollapsed((prev: boolean) => !prev)
}
const filterVariables = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(variable => {
if (['number', 'string'].includes(variable.dataType)) {
filterList.push(variable)
} else if (variable.dataType === 'file') {
filterList.push({
...variable,
disabled: true,
children: variable.children?.filter(child => ['number', 'string'].includes(child.dataType))
})
}
})
return filterList
}, [options])
const filterVariablesWithFile = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(variable => {
if (['number', 'string', 'file', 'array[file]'].includes(variable.dataType)) {
filterList.push(variable)
}
})
return filterList
}, [options])
const jsonRawFilterVariables = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(variable => {
if (['number', 'string', 'array[string]', 'array[number]'].includes(variable.dataType)) {
filterList.push(variable)
} else if (variable.dataType === 'file') {
filterList.push({
...variable,
disabled: true,
children: variable.children?.filter(child => ['number', 'string', 'file', 'array[string]', 'array[number]'].includes(child.dataType))
})
}
})
return filterList
}, [options])
const fileFilterVariables = useMemo(() => {
const filterList: Suggestion[] = []
options.forEach(variable => {
if (['array[file]'].includes(variable.dataType)) {
filterList.push(variable)
} else if (variable.dataType === 'file') {
filterList.push({
...variable,
children: []
})
}
})
return filterList
}, [options])
return (
<>
<Flex align="center" justify="space-between" className="rb:mb-1!">
@@ -117,7 +175,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<Form.Item name="url">
<Editor
key="url"
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={filterVariables}
variant="outlined"
type="input"
size="small"
@@ -134,7 +192,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
size="small"
parentName="headers"
title="HEADERS"
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={filterVariables}
/>
</Form.Item>
@@ -143,7 +201,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
size="small"
parentName="params"
title="PARAMS"
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={filterVariables}
/>
</Form.Item>
@@ -167,7 +225,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<EditableTable
size="small"
parentName={['body', 'data']}
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number' || vo.dataType.includes('file'))}
options={filterVariablesWithFile}
typeOptions={[
{ label: 'text', value: 'text' },
{ label: 'file', value: 'file' }
@@ -180,7 +238,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<EditableTable
size="small"
parentName={['body', 'data']}
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={filterVariablesWithFile}
filterBooleanType={true}
/>
</Form.Item>
@@ -190,7 +248,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<MessageEditor
key="json"
parentName={['body', 'data']}
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={jsonRawFilterVariables}
isArray={false}
title="JSON"
titleVariant="borderless"
@@ -204,7 +262,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<MessageEditor
key="raw"
parentName={['body', 'data']}
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
options={jsonRawFilterVariables}
isArray={false}
title="RAW TEXT"
titleVariant="borderless"
@@ -220,7 +278,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
<Editor
key={['body', 'data'].join('_')}
placeholder={t('common.pleaseSelect')}
options={options.filter(vo => vo.dataType.includes('file'))}
options={fileFilterVariables}
type="input"
size="small"
height={28}

View File

@@ -163,25 +163,45 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
form.setFieldsValue(inititalValue)
}
const getNumberOptions = useMemo(() => {
const list: Suggestion[] = []
// string -> string
// integer -> number
// number -> number
// boolean -> boolean【只能选true/false】
// array -> array[file]/array[object]/array[string]/array[number]/array[boolean]
// object -> object/file
const getFilterOptions = (type: string) => {
const filterList: Suggestion[] = [];
options.forEach(vo => {
if (vo.children && vo?.children?.length > 0) {
const filterChild = vo.children.filter(child => child.dataType === 'number')
if (vo.children && vo.children?.length > 0) {
const childOptions = vo.children?.filter(child => child.dataType === type || (type === 'integer' && child.dataType === 'number'))
if (filterChild.length > 0) {
list.push({ ...vo, disabled: vo.dataType !== 'number', children: filterChild })
} else if (vo.dataType === 'number') {
list.push({ ...vo, children: [] })
if (vo.dataType === type
|| (type === 'integer' && vo.dataType === 'number')
|| (type === 'array' && vo.dataType.includes(type))
|| (type === 'object' && vo.dataType === 'object')
) {
filterList.push({
...vo,
children: childOptions
})
} else if (childOptions.length > 0) {
filterList.push({
...vo,
disabled: true,
children: childOptions
})
}
} else if (vo.dataType === 'number') {
list.push({ ...vo })
} else if (vo.dataType === type
|| (type === 'integer' && vo.dataType === 'number')
|| (type === 'array' && vo.dataType.includes(type))
|| (type === 'object' && vo.dataType === 'object')) {
filterList.push(vo)
}
})
console.log('options', options, list)
return list
}, [options])
return filterList
}
return (
<>
@@ -205,7 +225,7 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
<Form.Item
name={['tool_parameters', parameter.name]}
label={<>
{parameter.name}
{parameter.name} <span className="rb:text-[#5B6167] rb:mx-1">({parameter.type})</span>
<Tooltip title={parameter.description} placement="right">
<div className="rb:size-3 rb:ml-0.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/question.svg')]"></div>
</Tooltip>
@@ -220,21 +240,12 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
? <Select size="small" options={parameter.enum.map(vo => ({ value: vo, label: vo }))} placeholder={t('common.pleaseSelect')} />
: parameter.type === 'boolean'
? <Switch size="small" />
: parameter.type === 'integer' || parameter.type === 'number'
? <Editor
variant="outlined"
type="input"
size="small"
height={28}
options={getNumberOptions}
placeholder={t('common.pleaseEnter')}
/>
: <Editor
variant="outlined"
type="input"
size="small"
height={28}
options={options}
options={getFilterOptions(parameter.type)}
placeholder={t('common.pleaseEnter')}
/>
}

View File

@@ -248,7 +248,7 @@ const Properties: FC<PropertiesProps> = ({
return null;
})() : null;
let filteredList = variableList.filter(variable => variable.dataType !== 'boolean');
let filteredList = variableList.filter(variable => !['boolean', 'object', 'array[boolean]'].includes(variable.dataType));
// If this LLM node is a child of iteration/loop, ensure parent variables are included
if (parentLoopNode) {
@@ -315,7 +315,46 @@ const Properties: FC<PropertiesProps> = ({
return filteredList;
}
if (nodeType === 'knowledge-retrieval' || nodeType === 'parameter-extractor' && key !== 'prompt' || nodeType === 'memory-read' || nodeType === 'question-classifier') {
if (nodeType === 'knowledge-retrieval'
|| (nodeType === 'parameter-extractor' && key === 'text')
|| (nodeType === 'question-classifier' && ['input_variable', 'categories'].includes(key as string))
) {
const allList = addParentIterationVars(variableList);
let filteredList: Suggestion[] = []
allList.forEach(variable => {
if (variable.dataType === 'string') {
filteredList.push(variable)
} else if (variable.dataType === 'file') {
filteredList.push({
...variable,
children: variable.children.filter((child: Suggestion) => child.dataType === 'string')
})
}
})
return filteredList
}
if ((nodeType === 'parameter-extractor' && key === 'prompt')
|| (nodeType === 'question-classifier' && key === 'user_supplement_prompt')
) {
const allList = addParentIterationVars(variableList);
let filteredList: Suggestion[] = []
allList.forEach(variable => {
if (['string', 'number'].includes(variable.dataType)) {
filteredList.push(variable)
} else if (variable.dataType === 'file') {
filteredList.push({
...variable,
disabled: true,
children: variable.children.filter((child: Suggestion) => ['string', 'number'].includes(child.dataType))
})
}
})
return filteredList
}
if (nodeType === 'memory-read') {
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string');
return filteredList;
}
@@ -327,11 +366,24 @@ const Properties: FC<PropertiesProps> = ({
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
return filteredList;
}
if (nodeType === 'iteration' && key === 'output' || nodeType === 'loop' && key === 'condition') {
if ((nodeType === 'iteration' && key === 'output')) {
if (!selectedNode) return [];
let filteredList = nodeType === 'iteration'
? variableList.filter(variable => variable.value.includes('sys.'))
: addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop');
let filteredList = variableList.filter(variable => variable.value.includes('sys.'))
const childVariables = getChildNodeVariables(selectedNode, graphRef);
const existingKeys = new Set(filteredList.map(v => v.key));
childVariables.forEach(v => {
if (!existingKeys.has(v.key)) {
filteredList.push(v);
existingKeys.add(v.key);
}
});
return filteredList.filter(variable => variable.dataType !== 'array[file]');
}
if (nodeType === 'loop' && key === 'condition') {
if (!selectedNode) return [];
let filteredList = addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop');
const childVariables = getChildNodeVariables(selectedNode, graphRef);
const existingKeys = new Set(filteredList.map(v => v.key));
@@ -348,6 +400,25 @@ const Properties: FC<PropertiesProps> = ({
return variableList.filter(variable => variable.dataType.includes('array'));
}
if (nodeType === 'code'
|| (nodeType === 'if-else' && key === 'cases')
) {
const allList = addParentIterationVars(variableList);
let filteredList: Suggestion[] = []
allList.forEach(variable => {
if (variable.dataType === 'file') {
filteredList.push({
...variable,
disabled: true,
})
} else {
filteredList.push(variable)
}
})
return filteredList
}
// For all other node types, add parent iteration variables if applicable
let baseList = variableList;
return addParentIterationVars(baseList);