From 6e0f13f269f845a27e7842b66a25cf43cbed882f Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 2 Apr 2024 11:14:01 +0800 Subject: [PATCH] feat: tool new struct --- .../nodes/_base/components/variable/utils.ts | 71 +++++----- .../nodes/tool/components/input-var-list.tsx | 122 ++++++++++++------ .../components/workflow/nodes/tool/default.ts | 12 +- .../components/workflow/nodes/tool/types.ts | 19 ++- .../workflow/nodes/tool/use-config.ts | 69 +++++----- 5 files changed, 168 insertions(+), 125 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 0f9a3d36ae..a9a92388b4 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -8,9 +8,6 @@ import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/ty import type { IfElseNodeType } from '../../../if-else/types' import type { TemplateTransformNodeType } from '../../../template-transform/types' import type { QuestionClassifierNodeType } from '../../../question-classifier/types' -import type { HttpNodeType } from '../../../http/types' -import type { ToolNodeType } from '../../../tool/types' -import { VarType as VarKindType } from '../../../tool/types' import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' @@ -236,9 +233,8 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => { break } case BlockEnum.LLM: { - const inputVars = (data as LLMNodeType)?.variables.map((v) => { - return v.value_selector - }) + // TODO: get var in inputs + const inputVars: ValueSelector[] = [] const contextVar = (data as LLMNodeType).context?.variable_selector ? [(data as LLMNodeType).context?.variable_selector] : [] res = [...inputVars, ...contextVar] @@ -271,17 +267,11 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => { break } case BlockEnum.HttpRequest: { - res = (data as HttpNodeType).variables?.map((v) => { - return v.value_selector - }) + // TODO: get var in inputs break } case BlockEnum.Tool: { - res = (data as ToolNodeType).tool_parameters?.filter((v) => { - return v.variable_type === VarKindType.selector - }).map((v) => { - return v.value_selector || [] - }) + // TODO: get var in inputs break } @@ -331,13 +321,14 @@ export const updateNodeVars = (oldNode: Node, oldVarSelector: ValueSelector, new } case BlockEnum.LLM: { const payload = data as LLMNodeType - if (payload.variables) { - payload.variables = payload.variables.map((v) => { - if (v.value_selector.join('.') === oldVarSelector.join('.')) - v.value_selector = newVarSelector - return v - }) - } + // TODO: update in inputs + // if (payload.variables) { + // payload.variables = payload.variables.map((v) => { + // if (v.value_selector.join('.') === oldVarSelector.join('.')) + // v.value_selector = newVarSelector + // return v + // }) + // } if (payload.context?.variable_selector?.join('.') === oldVarSelector.join('.')) payload.context.variable_selector = newVarSelector @@ -389,28 +380,30 @@ export const updateNodeVars = (oldNode: Node, oldVarSelector: ValueSelector, new break } case BlockEnum.HttpRequest: { - const payload = data as HttpNodeType - if (payload.variables) { - payload.variables = payload.variables.map((v) => { - if (v.value_selector.join('.') === oldVarSelector.join('.')) - v.value_selector = newVarSelector - return v - }) - } + // TODO: update in inputs + // const payload = data as HttpNodeType + // if (payload.variables) { + // payload.variables = payload.variables.map((v) => { + // if (v.value_selector.join('.') === oldVarSelector.join('.')) + // v.value_selector = newVarSelector + // return v + // }) + // } break } case BlockEnum.Tool: { - const payload = data as ToolNodeType - if (payload.tool_parameters) { - payload.tool_parameters = payload.tool_parameters.map((v) => { - if (v.variable_type === VarKindType.static) - return v + // TODO: update in inputs + // const payload = data as ToolNodeType + // if (payload.tool_parameters) { + // payload.tool_parameters = payload.tool_parameters.map((v) => { + // if (v.type === VarKindType.static) + // return v - if (v.value_selector?.join('.') === oldVarSelector.join('.')) - v.value_selector = newVarSelector - return v - }) - } + // if (v.value_selector?.join('.') === oldVarSelector.join('.')) + // v.value_selector = newVarSelector + // return v + // }) + // } break } case BlockEnum.VariableAssigner: { diff --git a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx index e756b8eb67..3216e2b5b5 100644 --- a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx +++ b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx @@ -1,21 +1,25 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' import produce from 'immer' -import type { ToolVarInput } from '../types' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import type { ToolVarInputs } from '../types' import { VarType as VarKindType } from '../types' import type { ValueSelector, Var } from '@/app/components/workflow/types' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' - +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import { VarType } from '@/app/components/workflow/types' type Props = { readOnly: boolean nodeId: string schema: CredentialFormSchema[] - value: ToolVarInput[] - onChange: (value: ToolVarInput[]) => void + value: ToolVarInputs + onChange: (value: ToolVarInputs) => void onOpen?: (index: number) => void isSupportConstantValue?: boolean filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean @@ -33,42 +37,71 @@ const InputVarList: FC = ({ }) => { const language = useLanguage() - const keyValues = (() => { - const res: Record = {} - value.forEach((item) => { - res[item.variable] = item - }) - return res - })() + // const valueList = (() => { + // const list = [] + // Object.keys(value).forEach((key) => { + // list.push({ + // variable: key, + // ...value[key], + // }) + // }) + // })() - const handleChange = useCallback((variable: string) => { + const { t } = useTranslation() + const availableVarList = useAvailableVarList(nodeId, { + onlyLeafNodeVar: false, + filterVar: (varPayload: Var) => { + return [VarType.string, VarType.number].includes(varPayload.type) + }, + }) + + const handleNotMixedTypeChange = useCallback((variable: string) => { return (varValue: ValueSelector | string, varKindType: VarKindType) => { - const newValue = produce(value, (draft: ToolVarInput[]) => { - const target = draft.find(item => item.variable === variable) + const newValue = produce(value, (draft: ToolVarInputs) => { + const target = draft[variable] if (target) { - if (!isSupportConstantValue || varKindType === VarKindType.selector) { + if (!isSupportConstantValue || varKindType === VarKindType.variable) { if (isSupportConstantValue) - target.variable_type = VarKindType.selector + target.type = VarKindType.variable - target.value_selector = varValue as ValueSelector + target.value = varValue as ValueSelector } else { - target.variable_type = VarKindType.static + target.type = VarKindType.constant target.value = varValue as string } } else { - draft.push({ - variable, - variable_type: VarKindType.static, - value: '', - }) + draft[variable] = { + type: varKindType, + value: varValue, + } } }) onChange(newValue) } }, [value, onChange, isSupportConstantValue]) + 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 [isFocus, setIsFocus] = useState(false) + const handleOpen = useCallback((index: number) => { return () => onOpen(index) }, [onOpen]) @@ -82,25 +115,40 @@ const InputVarList: FC = ({ required, tooltip, }, index) => { - const varInput = keyValues[variable] + const varInput = value[variable] + const isString = type !== FormTypeEnum.textNumber return (
{label[language] || label.en_US} - {type === FormTypeEnum.textNumber ? 'Number' : 'String'} + {!isString ? 'Number' : 'String'} {required && Required}
- + {isString + ? () + : ( + + )} + {tooltip &&
{tooltip[language] || tooltip.en_US}
}
) diff --git a/web/app/components/workflow/nodes/tool/default.ts b/web/app/components/workflow/nodes/tool/default.ts index ba83e84774..87011707e9 100644 --- a/web/app/components/workflow/nodes/tool/default.ts +++ b/web/app/components/workflow/nodes/tool/default.ts @@ -8,7 +8,7 @@ const i18nPrefix = 'workflow.errorMsg' const nodeDefault: NodeDefault = { defaultValue: { - tool_parameters: [], + tool_parameters: {}, tool_configurations: {}, }, getAvailablePrevNodes(isChatMode: boolean) { @@ -32,15 +32,15 @@ const nodeDefault: NodeDefault = { toolInputsSchema.filter((field: any) => { return field.required }).forEach((field: any) => { - const targetVar = payload.tool_parameters.find((item: any) => item.variable === field.variable) + const targetVar = payload.tool_parameters[field.variable] if (!targetVar) return - const { variable_type, value, value_selector } = targetVar - if (variable_type === VarKindType.selector) { - if (!errorMessages && (!value_selector || value_selector.length === 0)) + const { type: variable_type, value } = targetVar + if (variable_type === VarKindType.variable) { + if (!errorMessages && (!value || value.length === 0)) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: field.label }) } - if (variable_type === VarKindType.static) { + else { if (!errorMessages && (value === undefined || value === null || value === '')) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: field.label }) } diff --git a/web/app/components/workflow/nodes/tool/types.ts b/web/app/components/workflow/nodes/tool/types.ts index 14b54709bd..b44950bf00 100644 --- a/web/app/components/workflow/nodes/tool/types.ts +++ b/web/app/components/workflow/nodes/tool/types.ts @@ -1,16 +1,15 @@ -import type { CommonNodeType } from '@/app/components/workflow/types' +import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' export enum VarType { - selector = 'selector', - static = 'static', + variable = 'variable', + constant = 'constant', + mixed = 'mixed', } -export type ToolVarInput = { - variable: string - variable_type: VarType - value?: string - value_selector?: string[] -} +export type ToolVarInputs = Record export type ToolNodeType = CommonNodeType & { provider_id: string @@ -18,6 +17,6 @@ export type ToolNodeType = CommonNodeType & { provider_name: string tool_name: string tool_label: string - tool_parameters: ToolVarInput[] + tool_parameters: ToolVarInputs tool_configurations: Record } diff --git a/web/app/components/workflow/nodes/tool/use-config.ts b/web/app/components/workflow/nodes/tool/use-config.ts index d83b4ff3bd..b1fca58ad9 100644 --- a/web/app/components/workflow/nodes/tool/use-config.ts +++ b/web/app/components/workflow/nodes/tool/use-config.ts @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import produce from 'immer' import { useBoolean } from 'ahooks' import { useStore } from '../../store' -import { type ToolNodeType, type ToolVarInput, VarType } from './types' +import { type ToolNodeType, type ToolVarInputs, VarType } from './types' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { CollectionType } from '@/app/components/tools/types' @@ -79,22 +79,15 @@ const useConfig = (id: string, payload: ToolNodeType) => { if (!draft.tool_configurations || Object.keys(draft.tool_configurations).length === 0) draft.tool_configurations = addDefaultValue(tool_configurations, toolSettingSchema) - if (!draft.tool_parameters || draft.tool_parameters.length === 0) { - draft.tool_parameters = toolInputVarSchema.map((item: any) => { - return { - variable: item.variable, - variable_type: VarType.static, - value: '', - } - }) - } + if (!draft.tool_parameters) + draft.tool_parameters = {} }) setInputs(inputsWithDefaultValue) // eslint-disable-next-line react-hooks/exhaustive-deps }, [currTool]) // setting when call - const setInputVar = useCallback((value: ToolVarInput[]) => { + const setInputVar = useCallback((value: ToolVarInputs) => { setInputs({ ...inputs, tool_parameters: value, @@ -126,13 +119,40 @@ const useConfig = (id: string, payload: ToolNodeType) => { // fill single run form variable with constant value first time const inputVarValuesWithConstantValue = () => { const res = produce(inputVarValues, (draft) => { - inputs.tool_parameters.forEach(({ variable, variable_type, value }: any) => { - if (variable_type === VarType.static && (draft[variable] === undefined || draft[variable] === null)) - draft[variable] = value + Object.keys(inputs.tool_parameters).forEach((key: string) => { + const { type, value } = inputs.tool_parameters[key] + if (type === VarType.constant && (value === undefined || value === null)) + draft.tool_parameters[key].value = value }) }) return res } + + const { + isShowSingleRun, + hideSingleRun, + getInputVars, + runningStatus, + setRunInputData, + handleRun, + handleStop, + runResult, + } = useOneStepRun({ + id, + data: inputs, + defaultRunInputData: {}, + moreDataForCheckValid: { + toolInputsSchema: [], + toolSettingSchema, + language, + }, + }) + const hadVarParams = Object.keys(inputs.tool_parameters) + .filter(key => inputs.tool_parameters[key].type === VarType.mixed) + .map(k => inputs.tool_parameters[k]) + + const varInputs = getInputVars(hadVarParams.map(p => p.value as string)) + const singleRunForms = (() => { const formInputs: InputVar[] = [] toolInputVarSchema.forEach((item: any) => { @@ -144,30 +164,12 @@ const useConfig = (id: string, payload: ToolNodeType) => { }) }) const forms: FormProps[] = [{ - inputs: formInputs, + inputs: varInputs, values: inputVarValuesWithConstantValue(), onChange: setInputVarValues, }] return forms })() - const { - isShowSingleRun, - hideSingleRun, - runningStatus, - setRunInputData, - handleRun, - handleStop, - runResult, - } = useOneStepRun({ - id, - data: inputs, - defaultRunInputData: {}, - moreDataForCheckValid: { - toolInputsSchema: singleRunForms[0].inputs, - toolSettingSchema, - language, - }, - }) return { readOnly, @@ -190,6 +192,7 @@ const useConfig = (id: string, payload: ToolNodeType) => { isShowSingleRun, hideSingleRun, inputVarValues, + varInputs, setInputVarValues, singleRunForms, runningStatus,