From 8ac6bc0b5a69dcc62dbd89e5f2a2581ed10773f8 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sat, 8 Feb 2025 17:11:24 +0800 Subject: [PATCH 1/8] tool setting support variable --- web/app/components/base/switch/index.tsx | 7 +- .../base/tab-slider-plain/index.tsx | 16 +- .../model-provider-page/model-modal/Form.tsx | 4 + .../multiple-tool-selector/index.tsx | 14 + .../tool-selector/index.tsx | 155 ++++++++--- .../tool-selector/reasoning-config-form.tsx | 258 ++++++++++++++++++ .../components/tools/utils/to-form-schema.ts | 31 +++ .../workflow/block-selector/types.ts | 1 + .../nodes/_base/components/agent-strategy.tsx | 6 + web/i18n/en-US/plugin.ts | 6 +- web/i18n/zh-Hans/plugin.ts | 6 +- 11 files changed, 455 insertions(+), 49 deletions(-) create mode 100644 web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx diff --git a/web/app/components/base/switch/index.tsx b/web/app/components/base/switch/index.tsx index 48e5c0cd8c..cbff726c76 100644 --- a/web/app/components/base/switch/index.tsx +++ b/web/app/components/base/switch/index.tsx @@ -5,7 +5,7 @@ import classNames from '@/utils/classnames' type SwitchProps = { onChange?: (value: boolean) => void - size?: 'sm' | 'md' | 'lg' | 'l' + size?: 'xs' | 'sm' | 'md' | 'lg' | 'l' defaultValue?: boolean disabled?: boolean className?: string @@ -23,6 +23,7 @@ const Switch = React.forwardRef( l: 'h-5 w-9', md: 'h-4 w-7', sm: 'h-3 w-5', + xs: 'h-2.5 w-3.5', } const circleStyle = { @@ -30,6 +31,7 @@ const Switch = React.forwardRef( l: 'h-4 w-4', md: 'h-3 w-3', sm: 'h-2 w-2', + xs: 'h-1.5 w-1', } const translateLeft = { @@ -37,6 +39,7 @@ const Switch = React.forwardRef( l: 'translate-x-4', md: 'translate-x-3', sm: 'translate-x-2', + xs: 'translate-x-1.5', } return ( @@ -61,6 +65,7 @@ const Switch = React.forwardRef( className={classNames( circleStyle[size], enabled ? translateLeft[size] : 'translate-x-0', + size === 'xs' && 'rounded-[1px]', 'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out', )} /> diff --git a/web/app/components/base/tab-slider-plain/index.tsx b/web/app/components/base/tab-slider-plain/index.tsx index 194b6ad650..22a6c197d8 100644 --- a/web/app/components/base/tab-slider-plain/index.tsx +++ b/web/app/components/base/tab-slider-plain/index.tsx @@ -3,47 +3,51 @@ import type { FC } from 'react' import React from 'react' import cn from '@/utils/classnames' -interface Option { +type Option = { value: string text: string | JSX.Element } -interface ItemProps { +type ItemProps = { className?: string isActive: boolean onClick: (v: string) => void option: Option + smallItem?: boolean } const Item: FC = ({ className, isActive, onClick, option, + smallItem, }) => { return (
!isActive && onClick(option.value)} >
{option.text}
{isActive && ( -
+
)}
) } -interface Props { +type Props = { className?: string value: string onChange: (v: string) => void options: Option[] noBorderBottom?: boolean + smallItem?: boolean itemClassName?: string } @@ -54,6 +58,7 @@ const TabSlider: FC = ({ options, noBorderBottom, itemClassName, + smallItem, }) => { return (
@@ -64,6 +69,7 @@ const TabSlider: FC = ({ onClick={onChange} key={option.value} className={itemClassName} + smallItem={smallItem} /> ))}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index 74e295cf0e..d3b61a8d0f 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -326,6 +326,8 @@ function Form< void + supportVariables?: boolean + nodeOutputVars: NodeOutPutVar[], + availableNodes: Node[], } const MultipleToolSelector = ({ @@ -32,6 +37,9 @@ const MultipleToolSelector = ({ supportCollapse, scope, onChange, + supportVariables, + nodeOutputVars, + availableNodes, }: Props) => { const { t } = useTranslation() const enabledCount = value.filter(item => item.enabled).length @@ -121,6 +129,9 @@ const MultipleToolSelector = ({ {!collapse && ( <> 0 && value.map((item, index) => (
parameters?: Record extra?: Record }) => void @@ -65,6 +70,9 @@ type Props = { onControlledStateChange?: (state: boolean) => void panelShowState?: boolean onPanelShowStateChange?: (state: boolean) => void + supportVariables?: boolean + nodeOutputVars: NodeOutPutVar[], + availableNodes: Node[], } const ToolSelector: FC = ({ value, @@ -81,6 +89,8 @@ const ToolSelector: FC = ({ onControlledStateChange, panelShowState, onPanelShowStateChange, + nodeOutputVars, + availableNodes, }) => { const { t } = useTranslation() const [isShow, onShowChange] = useState(false) @@ -107,12 +117,14 @@ const ToolSelector: FC = ({ const [isShowChooseTool, setIsShowChooseTool] = useState(false) const handleSelectTool = (tool: ToolDefaultValue) => { - const paramValues = addDefaultValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form !== 'llm') as any)) + const settingValues = generateFormValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form !== 'llm') as any)) + const paramValues = generateFormValue(tool.params, toolParametersToFormSchemas(tool.paramSchemas.filter(param => param.form === 'llm') as any), true) const toolValue = { provider_name: tool.provider_id, type: tool.provider_type, tool_name: tool.tool_name, tool_label: tool.tool_label, + settings: settingValues, parameters: paramValues, enabled: tool.is_team_authorization, extra: { @@ -133,14 +145,33 @@ const ToolSelector: FC = ({ } as any) } - const currentToolParams = useMemo(() => { + // tool settings & params + const currentToolSettings = useMemo(() => { if (!currentProvider) return [] return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form !== 'llm') || [] }, [currentProvider, value]) + const currentToolParams = useMemo(() => { + if (!currentProvider) return [] + return currentProvider.tools.find(tool => tool.name === value?.tool_name)?.parameters.filter(param => param.form === 'llm') || [] + }, [currentProvider, value]) + const [currType, setCurrType] = useState('settings') + const showTabSlider = currentToolSettings.length > 0 && currentToolParams.length > 0 + const userSettingsOnly = currentToolSettings.length > 0 && !currentToolParams.length + const reasoningConfigOnly = currentToolParams.length > 0 && !currentToolSettings.length - const formSchemas = useMemo(() => toolParametersToFormSchemas(currentToolParams), [currentToolParams]) + const settingsFormSchemas = useMemo(() => toolParametersToFormSchemas(currentToolSettings), [currentToolSettings]) + const paramsFormSchemas = useMemo(() => toolParametersToFormSchemas(currentToolParams), [currentToolParams]) - const handleFormChange = (v: Record) => { + const handleSettingsFormChange = (v: Record) => { + const newValue = getStructureValue(v) + + const toolValue = { + ...value, + settings: newValue, + } + onSelect(toolValue as any) + } + const handleParamsFormChange = (v: Record) => { const toolValue = { ...value, parameters: v, @@ -281,12 +312,9 @@ const ToolSelector: FC = ({
{/* authorization */} {currentProvider && currentProvider.type === CollectionType.builtIn && currentProvider.allow_delete && ( -
-
-
{t('plugin.detailPanel.toolSelector.auth')}
- -
-
+ <> + +
{!currentProvider.is_team_authorization && ( )}
-
+ )} {/* tool settings */} - {currentToolParams.length > 0 && currentProvider?.is_team_authorization && ( -
-
-
{t('plugin.detailPanel.toolSelector.settings')}
- -
-
-
item.url - ? ( - {t('tools.howToGet')} - - ) - : null} + {(currentToolSettings.length > 0 || currentToolParams.length > 0) && currentProvider?.is_team_authorization && ( + <> + + {/* tabs */} + {showTabSlider && ( + { + setCurrType(value) + }} + options={[ + { value: 'settings', text: t('plugin.detailPanel.toolSelector.settings')! }, + { value: 'params', text: t('plugin.detailPanel.toolSelector.params')! }, + ]} /> -
-
+ )} + {showTabSlider && currType === 'params' && ( +
+
{t('plugin.detailPanel.toolSelector.paramsTip1')}
+
{t('plugin.detailPanel.toolSelector.paramsTip2')}
+
+ )} + {/* user settings only */} + {userSettingsOnly && ( +
+
{t('plugin.detailPanel.toolSelector.settings')}
+
+ )} + {/* reasoning config only */} + {reasoningConfigOnly && ( +
+
{t('plugin.detailPanel.toolSelector.params')}
+
+
{t('plugin.detailPanel.toolSelector.paramsTip1')}
+
{t('plugin.detailPanel.toolSelector.paramsTip2')}
+
+
+ )} + {/* user settings form */} + {(currType === 'settings' || userSettingsOnly) && ( +
+ item.url + ? ( + {t('tools.howToGet')} + + ) + : null} + /> +
+ )} + {/* reasoning config form */} + {(currType === 'params' || reasoningConfigOnly) && ( + + )} + )} )} diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx new file mode 100644 index 0000000000..38d83542e6 --- /dev/null +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx @@ -0,0 +1,258 @@ +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import produce from 'immer' +import { + RiArrowRightUpLine, +} from '@remixicon/react' +import Tooltip from '@/app/components/base/tooltip' +import Switch from '@/app/components/base/switch' +import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' +import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Node } from 'reactflow' +import type { NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types' +import type { ToolVarInputs } from '@/app/components/workflow/nodes/tool/types' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import cn from '@/utils/classnames' + +type Props = { + value: Record + onChange: (val: Record) => void + schemas: any[] + nodeOutputVars: NodeOutPutVar[], + availableNodes: Node[], +} + +const ReasoningConfigForm: React.FC = ({ + value, + onChange, + schemas, + nodeOutputVars, + availableNodes, +}) => { + const { t } = useTranslation() + const language = useLanguage() + const handleFormChange = (key: string, val: string | boolean) => { + onChange({ ...value, [key]: val }) + } + const handleAutomatic = (key: string, val: any) => { + onChange({ + ...value, + [key]: { + ...value[key], + auto: val ? 1 : 0, + }, + }) + } + + const [inputsIsFocus, setInputsIsFocus] = useState>({}) + const handleInputFocus = useCallback((variable: string) => { + return (value: boolean) => { + setInputsIsFocus((prev) => { + return { + ...prev, + [variable]: value, + } + }) + } + }, []) + const handleNotMixedTypeChange = useCallback((variable: string) => { + return (varValue: ValueSelector | string, varKindType: VarKindType) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + const target = draft[variable] + if (target) { + target.type = varKindType + target.value = varValue + } + else { + draft[variable] = { + type: varKindType, + value: varValue, + } + } + }) + onChange(newValue) + } + }, [value, onChange]) + const handleMixedTypeChange = useCallback((variable: string) => { + return (itemValue: string) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + const target = draft[variable] + if (target) { + target.value = itemValue + } + else { + draft[variable] = { + type: VarKindType.mixed, + value: itemValue, + } + } + }) + onChange(newValue) + } + }, [value, onChange]) + const handleFileChange = useCallback((variable: string) => { + return (varValue: ValueSelector | string) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + draft[variable] = { + type: VarKindType.variable, + value: varValue, + } + }) + onChange(newValue) + } + }, [value, onChange]) + + const handleAppChange = useCallback((variable: string) => { + return (app: { + app_id: string + inputs: Record + files?: any[] + }) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + draft[variable] = app as any + }) + onChange(newValue) + } + }, [onChange, value]) + const handleModelChange = useCallback((variable: string) => { + return (model: any) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + draft[variable] = { + ...draft[variable], + ...model, + } as any + }) + onChange(newValue) + } + }, [onChange, value]) + + const renderField = (schema: any) => { + const { + variable, + label, + required, + tooltip, + type, + scope, + url, + } = schema + const auto = value[variable]?.auto + const tooltipContent = (tooltip && ( + + {tooltip[language] || tooltip.en_US} +
} + triggerClassName='ml-1 w-4 h-4' + asChild={false} /> + )) + const varInput = value[variable].value + const isNumber = type === FormTypeEnum.textNumber + const isSelect = type === FormTypeEnum.select + const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files + const isAppSelector = type === FormTypeEnum.appSelector + const isModelSelector = type === FormTypeEnum.modelSelector + // const isToolSelector = type === FormTypeEnum.toolSelector + const isString = !isNumber && !isSelect && !isFile && !isAppSelector && !isModelSelector + return ( +
+
+
+ {label[language] || label.en_US} + {required && ( + * + )} + {tooltipContent} +
+
handleAutomatic(variable, !auto)}> + automatic + handleAutomatic(variable, val)} + /> +
+
+ {auto === 0 && ( + <> + {/* {isString && ( + + )} */} + {/* {(isNumber || isSelect) && ( + + )} */} + {/* {isFile && ( + varPayload.type === VarType.file || varPayload.type === VarType.arrayFile} + /> + )} */} + {isAppSelector && ( + + )} + {isModelSelector && ( + + )} + + )} + {url && ( + + {t('tools.howToGet')} + + + )} +
+ ) + } + return ( +
+ {schemas.map(schema => renderField(schema))} +
+ ) +} + +export default ReasoningConfigForm diff --git a/web/app/components/tools/utils/to-form-schema.ts b/web/app/components/tools/utils/to-form-schema.ts index 7086c903d1..6dc51e16ad 100644 --- a/web/app/components/tools/utils/to-form-schema.ts +++ b/web/app/components/tools/utils/to-form-schema.ts @@ -63,3 +63,34 @@ export const addDefaultValue = (value: Record, formSchemas: { varia }) return newValues } + +export const generateFormValue = (value: Record, formSchemas: { variable: string; default?: any }[], isReasoning = false) => { + const newValues = {} as any + formSchemas.forEach((formSchema) => { + const itemValue = value[formSchema.variable] + if ((formSchema.default !== undefined) && (value === undefined || itemValue === null || itemValue === '' || itemValue === undefined)) { + newValues[formSchema.variable] = { + ...(isReasoning ? { value: null, auto: 1 } : { value: formSchema.default }), + } + } + }) + return newValues +} + +export const getPlainValue = (value: Record) => { + const plainValue = { ...value } as any + Object.keys(plainValue).forEach((key) => { + plainValue[key] = value[key].value + }) + return plainValue +} + +export const getStructureValue = (value: Record) => { + const newValue = { ...value } as any + Object.keys(newValue).forEach((key) => { + newValue[key] = { + value: value[key], + } + }) + return newValue +} diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index 48679801ec..93a3242222 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -35,6 +35,7 @@ export type ToolValue = { provider_name: string tool_name: string tool_label: string + settings?: Record parameters?: Record enabled?: boolean extra?: Record diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 01a1aba24e..6be841b9f0 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -154,6 +154,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { tooltip={schema.tooltip && renderI18nObject(schema.tooltip)} > onChange(item)} @@ -169,6 +172,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { } return ( Date: Sat, 8 Feb 2025 21:09:27 +0800 Subject: [PATCH 2/8] fix: crash --- .../nodes/_base/components/variable/utils.ts | 2 +- web/app/components/workflow/nodes/constants.ts | 16 ++++++++++++++++ .../components/condition-files-list-value.tsx | 2 +- .../components/condition-list/condition-item.tsx | 4 ++-- .../nodes/if-else/components/condition-value.tsx | 4 ++-- .../nodes/if-else/components/condition-wrap.tsx | 2 +- .../components/workflow/nodes/if-else/default.ts | 16 ---------------- .../components/filter-condition.tsx | 2 +- .../components/sub-variable-picker.tsx | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index cb1a170011..2e4ea8a355 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -14,7 +14,7 @@ import type { ToolNodeType } from '../../../tool/types' import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types' import type { IterationNodeType } from '../../../iteration/types' import type { ListFilterNodeType } from '../../../list-operator/types' -import { OUTPUT_FILE_SUB_VARIABLES } from '../../../if-else/default' +import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants' import type { DocExtractorNodeType } from '../../../document-extractor/types' import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' diff --git a/web/app/components/workflow/nodes/constants.ts b/web/app/components/workflow/nodes/constants.ts index dc202acc28..d765b89f86 100644 --- a/web/app/components/workflow/nodes/constants.ts +++ b/web/app/components/workflow/nodes/constants.ts @@ -36,6 +36,7 @@ import ListFilterNode from './list-operator/node' import ListFilterPanel from './list-operator/panel' import AgentNode from './agent/node' import AgentPanel from './agent/panel' +import { TransferMethod } from '@/types/app' export const NodeComponentMap: Record> = { [BlockEnum.Start]: StartNode, @@ -82,3 +83,18 @@ export const PanelComponentMap: Record> = { } export const CUSTOM_NODE_TYPE = 'custom' + +export const FILE_TYPE_OPTIONS = [ + { value: 'image', i18nKey: 'image' }, + { value: 'document', i18nKey: 'doc' }, + { value: 'audio', i18nKey: 'audio' }, + { value: 'video', i18nKey: 'video' }, +] + +export const TRANSFER_METHOD = [ + { value: TransferMethod.local_file, i18nKey: 'localUpload' }, + { value: TransferMethod.remote_url, i18nKey: 'url' }, +] + +export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method'] +export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method') diff --git a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx index f21a3fac10..b0cfdeaf28 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx @@ -9,7 +9,7 @@ import { isComparisonOperatorNeedTranslate, isEmptyRelatedOperator, } from '../utils' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' import type { ValueSelector } from '../../../types' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index 2e89c73074..670f766bea 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -21,7 +21,7 @@ import { } from '../../types' import { comparisonOperatorNotRequireValue, getOperators } from '../../utils' import ConditionNumberInput from '../condition-number-input' -import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../default' +import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../../constants' import ConditionWrap from '../condition-wrap' import ConditionOperator from './condition-operator' import ConditionInput from './condition-input' @@ -39,7 +39,7 @@ import { SimpleSelect as Select } from '@/app/components/base/select' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' -interface ConditionItemProps { +type ConditionItemProps = { className?: string disabled?: boolean caseId: string diff --git a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx index 3a71b85a0e..8bfa77acc1 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx @@ -9,7 +9,7 @@ import { comparisonOperatorNotRequireValue, isComparisonOperatorNeedTranslate, } from '../utils' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../default' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import cn from '@/utils/classnames' @@ -20,7 +20,7 @@ import type { Node, } from '@/app/components/workflow/types' -interface ConditionValueProps { +type ConditionValueProps = { variableSelector: string[] labelName?: string operator: ComparisonOperator diff --git a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx index 39c03c9b38..a09cb5fa29 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx @@ -12,7 +12,7 @@ import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, Handl import type { Node, NodeOutPutVar, Var } from '../../../types' import { VarType } from '../../../types' import { useGetAvailableVars } from '../../variable-assigner/hooks' -import { SUB_VARIABLES } from '../default' +import { SUB_VARIABLES } from '../../constants' import ConditionList from './condition-list' import ConditionAdd from './condition-add' import cn from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/if-else/default.ts b/web/app/components/workflow/nodes/if-else/default.ts index 8d98f694bd..1be80592e5 100644 --- a/web/app/components/workflow/nodes/if-else/default.ts +++ b/web/app/components/workflow/nodes/if-else/default.ts @@ -1,7 +1,6 @@ import { BlockEnum, type NodeDefault } from '../../types' import { type IfElseNodeType, LogicalOperator } from './types' import { isEmptyRelatedOperator } from './utils' -import { TransferMethod } from '@/types/app' import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' const i18nPrefix = 'workflow.errorMsg' @@ -79,18 +78,3 @@ const nodeDefault: NodeDefault = { } export default nodeDefault - -export const FILE_TYPE_OPTIONS = [ - { value: 'image', i18nKey: 'image' }, - { value: 'document', i18nKey: 'doc' }, - { value: 'audio', i18nKey: 'audio' }, - { value: 'video', i18nKey: 'video' }, -] - -export const TRANSFER_METHOD = [ - { value: TransferMethod.local_file, i18nKey: 'localUpload' }, - { value: TransferMethod.remote_url, i18nKey: 'url' }, -] - -export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method'] -export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method') diff --git a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx index b64f753514..0c261a70d6 100644 --- a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx @@ -9,7 +9,7 @@ import { ComparisonOperator } from '../../if-else/types' import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils' import SubVariablePicker from './sub-variable-picker' import Input from '@/app/components/base/input' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/if-else/default' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/constants' import { SimpleSelect as Select } from '@/app/components/base/select' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' diff --git a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx index 0a210504cf..c3a8708603 100644 --- a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { SUB_VARIABLES } from '../../if-else/default' +import { SUB_VARIABLES } from '../../constants' import type { Item } from '@/app/components/base/select' import { SimpleSelect as Select } from '@/app/components/base/select' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' From 5d7400c8bb2ebe93f5fe205cd02795f564e0577f Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sat, 8 Feb 2025 18:06:21 +0800 Subject: [PATCH 3/8] support variable for number, select, file --- .../multiple-tool-selector/index.tsx | 4 ++ .../tool-selector/index.tsx | 3 ++ .../tool-selector/reasoning-config-form.tsx | 52 ++++++++++--------- .../nodes/_base/components/agent-strategy.tsx | 5 +- .../components/workflow/nodes/agent/panel.tsx | 1 + 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx index f2626a9b41..35c7842c0e 100644 --- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx @@ -26,6 +26,7 @@ type Props = { supportVariables?: boolean nodeOutputVars: NodeOutPutVar[], availableNodes: Node[], + nodeId?: string } const MultipleToolSelector = ({ @@ -40,6 +41,7 @@ const MultipleToolSelector = ({ supportVariables, nodeOutputVars, availableNodes, + nodeId, }: Props) => { const { t } = useTranslation() const enabledCount = value.filter(item => item.enabled).length @@ -130,6 +132,7 @@ const MultipleToolSelector = ({ <> = ({ value, @@ -91,6 +92,7 @@ const ToolSelector: FC = ({ onPanelShowStateChange, nodeOutputVars, availableNodes, + nodeId = '', }) => { const { t } = useTranslation() const [isShow, onShowChange] = useState(false) @@ -414,6 +416,7 @@ const ToolSelector: FC = ({ schemas={paramsFormSchemas as any} nodeOutputVars={nodeOutputVars} availableNodes={availableNodes} + nodeId={nodeId} /> )} diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx index 38d83542e6..3780658564 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx @@ -6,14 +6,20 @@ import { } from '@remixicon/react' import Tooltip from '@/app/components/base/tooltip' import Switch from '@/app/components/base/switch' +import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Node } from 'reactflow' -import type { NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types' +import type { + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' import type { ToolVarInputs } from '@/app/components/workflow/nodes/tool/types' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { VarType } from '@/app/components/workflow/types' import cn from '@/utils/classnames' type Props = { @@ -22,6 +28,7 @@ type Props = { schemas: any[] nodeOutputVars: NodeOutPutVar[], availableNodes: Node[], + nodeId: string } const ReasoningConfigForm: React.FC = ({ @@ -30,17 +37,15 @@ const ReasoningConfigForm: React.FC = ({ schemas, nodeOutputVars, availableNodes, + nodeId, }) => { const { t } = useTranslation() const language = useLanguage() - const handleFormChange = (key: string, val: string | boolean) => { - onChange({ ...value, [key]: val }) - } const handleAutomatic = (key: string, val: any) => { onChange({ ...value, [key]: { - ...value[key], + value: val ? null : value[key]?.value, auto: val ? 1 : 0, }, }) @@ -60,13 +65,13 @@ const ReasoningConfigForm: React.FC = ({ const handleNotMixedTypeChange = useCallback((variable: string) => { return (varValue: ValueSelector | string, varKindType: VarKindType) => { const newValue = produce(value, (draft: ToolVarInputs) => { - const target = draft[variable] + const target = draft[variable].value if (target) { target.type = varKindType target.value = varValue } else { - draft[variable] = { + draft[variable].value = { type: varKindType, value: varValue, } @@ -78,12 +83,12 @@ const ReasoningConfigForm: React.FC = ({ const handleMixedTypeChange = useCallback((variable: string) => { return (itemValue: string) => { const newValue = produce(value, (draft: ToolVarInputs) => { - const target = draft[variable] + const target = draft[variable].value if (target) { target.value = itemValue } else { - draft[variable] = { + draft[variable].value = { type: VarKindType.mixed, value: itemValue, } @@ -95,7 +100,7 @@ const ReasoningConfigForm: React.FC = ({ const handleFileChange = useCallback((variable: string) => { return (varValue: ValueSelector | string) => { const newValue = produce(value, (draft: ToolVarInputs) => { - draft[variable] = { + draft[variable].value = { type: VarKindType.variable, value: varValue, } @@ -103,7 +108,6 @@ const ReasoningConfigForm: React.FC = ({ onChange(newValue) } }, [value, onChange]) - const handleAppChange = useCallback((variable: string) => { return (app: { app_id: string @@ -111,7 +115,7 @@ const ReasoningConfigForm: React.FC = ({ files?: any[] }) => { const newValue = produce(value, (draft: ToolVarInputs) => { - draft[variable] = app as any + draft[variable].value = app as any }) onChange(newValue) } @@ -119,8 +123,8 @@ const ReasoningConfigForm: React.FC = ({ const handleModelChange = useCallback((variable: string) => { return (model: any) => { const newValue = produce(value, (draft: ToolVarInputs) => { - draft[variable] = { - ...draft[variable], + draft[variable].value = { + ...draft[variable].value, ...model, } as any }) @@ -188,33 +192,31 @@ const ReasoningConfigForm: React.FC = ({ placeholderClassName='!leading-[21px]' /> )} */} - {/* {(isNumber || isSelect) && ( + {(isNumber || isSelect) && ( varPayload.type === schema._type : undefined} + availableVars={isSelect ? nodeOutputVars : undefined} schema={schema} /> - )} */} - {/* {isFile && ( + )} + {isFile && ( varPayload.type === VarType.file || varPayload.type === VarType.arrayFile} /> - )} */} + )} {isAppSelector && ( void nodeOutputVars?: NodeOutPutVar[], availableNodes?: Node[], + nodeId?: string } type CustomSchema = Omit & { type: Type } & Field @@ -46,7 +47,7 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'> type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema export const AgentStrategy = memo((props: AgentStrategyProps) => { - const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes } = props + const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props const { t } = useTranslation() const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration) const renderI18nObject = useRenderI18nObject() @@ -155,6 +156,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { > { return ( > = (props) => { onFormValueChange={onFormChange} nodeOutputVars={availableVars} availableNodes={availableNodesWithParent} + nodeId={props.id} />
From d8779b0da2c90755a0c74bf418223a38e5cb2cf2 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sat, 8 Feb 2025 19:16:50 +0800 Subject: [PATCH 4/8] support variable for string --- .../model-provider-page/model-modal/Form.tsx | 20 ++++- .../tool-selector/reasoning-config-form.tsx | 80 +++++++++++-------- .../nodes/_base/components/agent-strategy.tsx | 15 ++-- .../variable/var-reference-picker.tsx | 4 +- 4 files changed, 75 insertions(+), 44 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index d3b61a8d0f..baf6b194ce 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -22,6 +22,10 @@ import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-sele import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector' import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' import RadioE from '@/app/components/base/radio/ui' +import type { + NodeOutPutVar, +} from '@/app/components/workflow/types' +import type { Node } from 'reactflow' type FormProps< CustomFormSchema extends Omit & { type: string } = never, @@ -47,6 +51,9 @@ type FormProps< ) => ReactNode // If return falsy value, this field will fallback to default render override?: [Array, (formSchema: CredentialFormSchema, props: Omit, 'override' | 'customRenderField'>) => ReactNode] + nodeId?: string + nodeOutputVars?: NodeOutPutVar[], + availableNodes?: Node[], } function Form< @@ -69,6 +76,9 @@ function Form< fieldMoreInfo, customRenderField, override, + nodeId, + nodeOutputVars, + availableNodes, }: FormProps) { const language = useLanguage() const [changeKey, setChangeKey] = useState('') @@ -326,8 +336,9 @@ function Form<
= ({ onChange, schemas, nodeOutputVars, - availableNodes, + // availableNodes, nodeId, }) => { const { t } = useTranslation() @@ -51,52 +51,52 @@ const ReasoningConfigForm: React.FC = ({ }) } - const [inputsIsFocus, setInputsIsFocus] = useState>({}) - const handleInputFocus = useCallback((variable: string) => { - return (value: boolean) => { - setInputsIsFocus((prev) => { - return { - ...prev, - [variable]: value, - } - }) - } - }, []) - const handleNotMixedTypeChange = useCallback((variable: string) => { + // const [inputsIsFocus, setInputsIsFocus] = useState>({}) + // const handleInputFocus = useCallback((variable: string) => { + // return (value: boolean) => { + // setInputsIsFocus((prev) => { + // return { + // ...prev, + // [variable]: value, + // } + // }) + // } + // }, []) + const handleNotMixedTypeChange = useCallback((variable: string, toString = false) => { return (varValue: ValueSelector | string, varKindType: VarKindType) => { const newValue = produce(value, (draft: ToolVarInputs) => { const target = draft[variable].value if (target) { target.type = varKindType - target.value = varValue + target.value = toString ? `${varValue}` : varValue } else { draft[variable].value = { type: varKindType, - value: varValue, - } - } - }) - onChange(newValue) - } - }, [value, onChange]) - const handleMixedTypeChange = useCallback((variable: string) => { - return (itemValue: string) => { - const newValue = produce(value, (draft: ToolVarInputs) => { - const target = draft[variable].value - if (target) { - target.value = itemValue - } - else { - draft[variable].value = { - type: VarKindType.mixed, - value: itemValue, + value: toString ? `${varValue}` : varValue, } } }) onChange(newValue) } }, [value, onChange]) + // const handleMixedTypeChange = useCallback((variable: string) => { + // return (itemValue: string) => { + // const newValue = produce(value, (draft: ToolVarInputs) => { + // const target = draft[variable].value + // if (target) { + // target.value = itemValue + // } + // else { + // draft[variable].value = { + // type: VarKindType.mixed, + // value: itemValue, + // } + // } + // }) + // onChange(newValue) + // } + // }, [value, onChange]) const handleFileChange = useCallback((variable: string) => { return (varValue: ValueSelector | string) => { const newValue = produce(value, (draft: ToolVarInputs) => { @@ -192,8 +192,21 @@ const ReasoningConfigForm: React.FC = ({ placeholderClassName='!leading-[21px]' /> )} */} + {isString && ( + varPayload.type === VarType.number || varPayload.type === VarType.secret || varPayload.type === VarType.string} + /> + )} {(isNumber || isSelect) && ( = ({ )} {isFile && ( { > onChange(item)} @@ -175,9 +175,9 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { return ( { fieldLabelClassName='uppercase' customRenderField={renderField} override={override} + nodeId={nodeId} + nodeOutputVars={nodeOutputVars || []} + availableNodes={availableNodes || []} /> : = ({ @@ -90,6 +91,7 @@ const VarReferencePicker: FC = ({ placeholder, minWidth, popupFor, + zIndex, }) => { const { t } = useTranslation() const store = useStoreApi() @@ -386,7 +388,7 @@ const VarReferencePicker: FC = ({ {!isConstant && ( Date: Sat, 8 Feb 2025 19:53:47 +0800 Subject: [PATCH 5/8] hide params outside --- .../multiple-tool-selector/index.tsx | 4 ---- .../plugin-detail-panel/tool-selector/index.tsx | 9 ++++----- .../tool-selector/reasoning-config-form.tsx | 12 ++++++------ .../nodes/_base/components/agent-strategy.tsx | 2 -- web/i18n/en-US/plugin.ts | 1 + web/i18n/zh-Hans/plugin.ts | 1 + 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx index 35c7842c0e..13a48dbabe 100644 --- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx @@ -23,7 +23,6 @@ type Props = { supportCollapse?: boolean scope?: string onChange: (value: ToolValue[]) => void - supportVariables?: boolean nodeOutputVars: NodeOutPutVar[], availableNodes: Node[], nodeId?: string @@ -38,7 +37,6 @@ const MultipleToolSelector = ({ supportCollapse, scope, onChange, - supportVariables, nodeOutputVars, availableNodes, nodeId, @@ -131,7 +129,6 @@ const MultipleToolSelector = ({ {!collapse && ( <> 0 && value.map((item, index) => (
void panelShowState?: boolean onPanelShowStateChange?: (state: boolean) => void - supportVariables?: boolean nodeOutputVars: NodeOutPutVar[], availableNodes: Node[], nodeId?: string, @@ -346,7 +345,7 @@ const ToolSelector: FC = ({ <> {/* tabs */} - {showTabSlider && ( + {nodeId && showTabSlider && ( = ({ ]} /> )} - {showTabSlider && currType === 'params' && ( + {nodeId && showTabSlider && currType === 'params' && (
{t('plugin.detailPanel.toolSelector.paramsTip1')}
{t('plugin.detailPanel.toolSelector.paramsTip2')}
@@ -375,7 +374,7 @@ const ToolSelector: FC = ({
)} {/* reasoning config only */} - {reasoningConfigOnly && ( + {nodeId && reasoningConfigOnly && (
{t('plugin.detailPanel.toolSelector.params')}
@@ -409,7 +408,7 @@ const ToolSelector: FC = ({
)} {/* reasoning config form */} - {(currType === 'params' || reasoningConfigOnly) && ( + {nodeId && (currType === 'params' || reasoningConfigOnly) && ( = ({ // }) // } // }, []) - const handleNotMixedTypeChange = useCallback((variable: string, toString = false) => { + const handleNotMixedTypeChange = useCallback((variable: string) => { return (varValue: ValueSelector | string, varKindType: VarKindType) => { const newValue = produce(value, (draft: ToolVarInputs) => { const target = draft[variable].value if (target) { target.type = varKindType - target.value = toString ? `${varValue}` : varValue + target.value = varValue } else { draft[variable].value = { type: varKindType, - value: toString ? `${varValue}` : varValue, + value: varValue, } } }) @@ -170,7 +170,7 @@ const ReasoningConfigForm: React.FC = ({ {tooltipContent}
handleAutomatic(variable, !auto)}> - automatic + {t('plugin.detailPanel.toolSelector.auto')} = ({ readonly={false} isShowNodeName nodeId={nodeId} - value={varInput?.value || []} - onChange={handleNotMixedTypeChange(variable, true)} + value={varInput?.value || ''} + onChange={handleNotMixedTypeChange(variable)} defaultVarKindType={VarKindType.variable} filterVar={(varPayload: Var) => varPayload.type === VarType.number || varPayload.type === VarType.secret || varPayload.type === VarType.string} /> diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 70f40995f9..b7553d8450 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -155,7 +155,6 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { tooltip={schema.tooltip && renderI18nObject(schema.tooltip)} > { } return ( Date: Sat, 8 Feb 2025 21:52:50 +0800 Subject: [PATCH 6/8] checklist for tool --- .../tool-selector/index.tsx | 1 + .../workflow/hooks/use-checklist.ts | 2 +- .../nodes/_base/components/agent-strategy.tsx | 4 +- .../workflow/nodes/agent/default.ts | 89 +++++++++++++++++++ web/i18n/en-US/workflow.ts | 2 + web/i18n/zh-Hans/workflow.ts | 3 + 6 files changed, 98 insertions(+), 3 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx index 3d5127f292..0b5e50a683 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx @@ -131,6 +131,7 @@ const ToolSelector: FC = ({ extra: { description: '', }, + schemas: tool.paramSchemas, } onSelect(toolValue) // setIsShowChooseTool(false) diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index 561c70fc30..0ef374493c 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -184,7 +184,7 @@ export const useChecklistBeforePublish = () => { } return true - }, [nodesExtraData, notify, t, store, isChatMode, buildInTools, customTools, workflowTools, language]) + }, [store, isChatMode, notify, t, buildInTools, customTools, workflowTools, language, nodesExtraData, strategyProviders]) return { handleCheckBeforePublish, diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index b7553d8450..a07686a8f5 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -142,7 +142,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { ] const renderField: ComponentProps>['customRenderField'] = (schema, props) => { switch (schema.type) { - case 'tool-selector': { + case FormTypeEnum.toolSelector: { const value = props.value[schema.variable] const onChange = (value: any) => { props.onChange({ ...props.value, [schema.variable]: value }) @@ -166,7 +166,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { ) } - case 'array[tools]': { + case FormTypeEnum.multiToolSelector: { const value = props.value[schema.variable] const onChange = (value: any) => { props.onChange({ ...props.value, [schema.variable]: value }) diff --git a/web/app/components/workflow/nodes/agent/default.ts b/web/app/components/workflow/nodes/agent/default.ts index 486fcbf641..2720589d6e 100644 --- a/web/app/components/workflow/nodes/agent/default.ts +++ b/web/app/components/workflow/nodes/agent/default.ts @@ -2,6 +2,7 @@ import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plug import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks' import type { NodeDefault } from '../../types' import type { AgentNodeType } from './types' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { renderI18nObject } from '@/hooks/use-i18n' const nodeDefault: NodeDefault = { @@ -37,6 +38,94 @@ const nodeDefault: NodeDefault = { } } for (const param of strategy.parameters) { + // single tool + if (param.required && param.type === FormTypeEnum.toolSelector) { + // no value + const toolValue = payload.agent_parameters?.[param.name]?.value + if (!toolValue) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }), + } + } + // not enabled + else if (!toolValue.enabled) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }), + } + } + // check form of tool + else { + const schemas = toolValue.schemas + const userSettings = toolValue.settings + const reasoningConfig = toolValue.parameters + schemas.forEach((schema: any) => { + if (schema.required) { + if (schema.form === 'form' && !userSettings[schema.name]?.value) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }), + } + } + if (schema.form === 'llm' && reasoningConfig[schema.name].auto === 0 && !userSettings[schema.name]?.value) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }), + } + } + } + }) + } + } + // multiple tools + if (param.required && param.type === FormTypeEnum.multiToolSelector) { + const tools = payload.agent_parameters?.[param.name]?.value || [] + // no value + if (!tools.length) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.fieldRequired', { field: renderI18nObject(param.label, language) }), + } + } + // not enabled + else if (tools.every((tool: any) => !tool.enabled)) { + return { + isValid: false, + errorMessage: t('workflow.errorMsg.noValidTool', { field: renderI18nObject(param.label, language) }), + } + } + // check form of tools + else { + let validState = { + isValid: true, + errorMessage: '', + } + for (const tool of tools) { + const schemas = tool.schemas + const userSettings = tool.settings + const reasoningConfig = tool.parameters + schemas.forEach((schema: any) => { + if (schema.required) { + if (schema.form === 'form' && !userSettings[schema.name]?.value) { + return validState = { + isValid: false, + errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }), + } + } + if (schema.form === 'llm' && reasoningConfig[schema.name]?.auto === 0 && !reasoningConfig[schema.name]?.value) { + return validState = { + isValid: false, + errorMessage: t('workflow.errorMsg.toolParameterRequired', { field: renderI18nObject(param.label, language), param: renderI18nObject(schema.label, language) }), + } + } + } + }) + } + return validState + } + } + // common params if (param.required && !payload.agent_parameters?.[param.name]?.value) { return { isValid: false, diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 53c50073c0..89344f01a6 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -195,6 +195,8 @@ const translation = { visionVariable: 'Vision Variable', }, invalidVariable: 'Invalid variable', + noValidTool: '{{field}} no valid tool selected', + toolParameterRequired: '{{field}}: parameter [{{param}}] is required', }, singleRun: { testRun: 'Test Run ', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index c622581e2d..679626cff8 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -195,6 +195,9 @@ const translation = { visionVariable: '视觉变量', }, invalidVariable: '无效的变量', + noValidTool: '{{field}} 无可用工具', + toolParameterRequired: '{{field}}: 参数 [{{param}}] 不能为空', + }, singleRun: { testRun: '测试运行 ', From e1497756169e8627eb3feb5650d10205040a6bb2 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sat, 8 Feb 2025 22:07:28 +0800 Subject: [PATCH 7/8] use mixed type for string --- .../tool-selector/reasoning-config-form.tsx | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx index 175c2e4eeb..6ba8207856 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/reasoning-config-form.tsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import produce from 'immer' import { @@ -6,6 +6,7 @@ import { } from '@remixicon/react' import Tooltip from '@/app/components/base/tooltip' import Switch from '@/app/components/base/switch' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' @@ -36,7 +37,7 @@ const ReasoningConfigForm: React.FC = ({ onChange, schemas, nodeOutputVars, - // availableNodes, + availableNodes, nodeId, }) => { const { t } = useTranslation() @@ -51,17 +52,17 @@ const ReasoningConfigForm: React.FC = ({ }) } - // const [inputsIsFocus, setInputsIsFocus] = useState>({}) - // const handleInputFocus = useCallback((variable: string) => { - // return (value: boolean) => { - // setInputsIsFocus((prev) => { - // return { - // ...prev, - // [variable]: value, - // } - // }) - // } - // }, []) + const [inputsIsFocus, setInputsIsFocus] = useState>({}) + const handleInputFocus = useCallback((variable: string) => { + return (value: boolean) => { + setInputsIsFocus((prev) => { + return { + ...prev, + [variable]: value, + } + }) + } + }, []) const handleNotMixedTypeChange = useCallback((variable: string) => { return (varValue: ValueSelector | string, varKindType: VarKindType) => { const newValue = produce(value, (draft: ToolVarInputs) => { @@ -80,23 +81,23 @@ const ReasoningConfigForm: React.FC = ({ onChange(newValue) } }, [value, onChange]) - // const handleMixedTypeChange = useCallback((variable: string) => { - // return (itemValue: string) => { - // const newValue = produce(value, (draft: ToolVarInputs) => { - // const target = draft[variable].value - // if (target) { - // target.value = itemValue - // } - // else { - // draft[variable].value = { - // type: VarKindType.mixed, - // value: itemValue, - // } - // } - // }) - // onChange(newValue) - // } - // }, [value, onChange]) + const handleMixedTypeChange = useCallback((variable: string) => { + return (itemValue: string) => { + const newValue = produce(value, (draft: ToolVarInputs) => { + const target = draft[variable].value + if (target) { + target.value = itemValue + } + else { + draft[variable].value = { + type: VarKindType.mixed, + value: itemValue, + } + } + }) + onChange(newValue) + } + }, [value, onChange]) const handleFileChange = useCallback((variable: string) => { return (varValue: ValueSelector | string) => { const newValue = produce(value, (draft: ToolVarInputs) => { @@ -180,7 +181,7 @@ const ReasoningConfigForm: React.FC = ({
{auto === 0 && ( <> - {/* {isString && ( + {isString && ( = ({ placeholder={t('workflow.nodes.http.insertVarPlaceholder')!} placeholderClassName='!leading-[21px]' /> - )} */} - {isString && ( + )} + {/* {isString && ( = ({ defaultVarKindType={VarKindType.variable} filterVar={(varPayload: Var) => varPayload.type === VarType.number || varPayload.type === VarType.secret || varPayload.type === VarType.string} /> - )} + )} */} {(isNumber || isSelect) && ( Date: Mon, 10 Feb 2025 12:41:54 +0800 Subject: [PATCH 8/8] chore: fix DSL crash --- web/app/components/workflow/nodes/agent/default.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/components/workflow/nodes/agent/default.ts b/web/app/components/workflow/nodes/agent/default.ts index 2720589d6e..6069f90991 100644 --- a/web/app/components/workflow/nodes/agent/default.ts +++ b/web/app/components/workflow/nodes/agent/default.ts @@ -57,11 +57,11 @@ const nodeDefault: NodeDefault = { } // check form of tool else { - const schemas = toolValue.schemas + const schemas = toolValue.schemas || [] const userSettings = toolValue.settings const reasoningConfig = toolValue.parameters schemas.forEach((schema: any) => { - if (schema.required) { + if (schema?.required) { if (schema.form === 'form' && !userSettings[schema.name]?.value) { return { isValid: false, @@ -102,11 +102,11 @@ const nodeDefault: NodeDefault = { errorMessage: '', } for (const tool of tools) { - const schemas = tool.schemas + const schemas = tool.schemas || [] const userSettings = tool.settings const reasoningConfig = tool.parameters schemas.forEach((schema: any) => { - if (schema.required) { + if (schema?.required) { if (schema.form === 'form' && !userSettings[schema.name]?.value) { return validState = { isValid: false,