feat: tool new struct

This commit is contained in:
Joel 2024-04-02 11:14:01 +08:00
parent 00728c2b1d
commit 6e0f13f269
5 changed files with 168 additions and 125 deletions

View File

@ -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: {

View File

@ -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<Props> = ({
}) => {
const language = useLanguage()
const keyValues = (() => {
const res: Record<string, ToolVarInput> = {}
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<Props> = ({
required,
tooltip,
}, index) => {
const varInput = keyValues[variable]
const varInput = value[variable]
const isString = type !== FormTypeEnum.textNumber
return (
<div key={variable} className='space-y-1'>
<div className='flex items-center h-[18px] space-x-2'>
<span className='text-[13px] font-medium text-gray-900'>{label[language] || label.en_US}</span>
<span className='text-xs font-normal text-gray-500'>{type === FormTypeEnum.textNumber ? 'Number' : 'String'}</span>
<span className='text-xs font-normal text-gray-500'>{!isString ? 'Number' : 'String'}</span>
{required && <span className='leading-[18px] text-xs font-normal text-[#EC4A0A]'>Required</span>}
</div>
<VarReferencePicker
readonly={readOnly}
isShowNodeName
nodeId={nodeId}
value={varInput?.variable_type === VarKindType.static ? (varInput?.value || '') : (varInput?.value_selector || [])}
onChange={handleChange(variable)}
onOpen={handleOpen(index)}
isSupportConstantValue={isSupportConstantValue}
defaultVarKindType={varInput?.variable_type}
filterVar={filterVar}
/>
{isString
? (<Input
className={cn(isFocus ? 'shadow-xs bg-gray-50 border-gray-300' : 'bg-gray-100 border-gray-100', 'rounded-lg px-3 py-[6px] border')}
value={varInput?.value as string || ''}
onChange={handleMixedTypeChange(variable)}
readOnly={readOnly}
nodesOutputVars={availableVarList}
onFocusChange={setIsFocus}
placeholder={t('workflow.nodes.http.insertVarPlaceholder')!}
placeholderClassName='!leading-[21px]'
/>)
: (
<VarReferencePicker
readonly={readOnly}
isShowNodeName
nodeId={nodeId}
value={varInput?.type === VarKindType.constant ? (varInput?.value || '') : (varInput?.value || [])}
onChange={handleNotMixedTypeChange(variable)}
onOpen={handleOpen(index)}
isSupportConstantValue={isSupportConstantValue}
defaultVarKindType={varInput?.type}
filterVar={filterVar}
/>
)}
{tooltip && <div className='leading-[18px] text-xs font-normal text-gray-600'>{tooltip[language] || tooltip.en_US}</div>}
</div>
)

View File

@ -8,7 +8,7 @@ const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault<ToolNodeType> = {
defaultValue: {
tool_parameters: [],
tool_parameters: {},
tool_configurations: {},
},
getAvailablePrevNodes(isChatMode: boolean) {
@ -32,15 +32,15 @@ const nodeDefault: NodeDefault<ToolNodeType> = {
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 })
}

View File

@ -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<string, {
type: VarType
value?: string | ValueSelector
}>
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<string, any>
}

View File

@ -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<ToolNodeType>({
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<ToolNodeType>({
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,