mirror of https://github.com/langgenius/dify.git
feat: var name rename struct
This commit is contained in:
parent
267d9568c6
commit
02a059bdc6
|
|
@ -16,12 +16,23 @@ import { type InputVar, InputVarType } from '@/app/components/workflow/types'
|
|||
import Modal from '@/app/components/base/modal'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
export enum ChangeType {
|
||||
changeVarName = 'changeVarName',
|
||||
}
|
||||
|
||||
export type MoreInfo = {
|
||||
type: ChangeType
|
||||
payload?: {
|
||||
beforeKey: string
|
||||
afterKey: string
|
||||
}
|
||||
}
|
||||
export type IConfigModalProps = {
|
||||
isCreate?: boolean
|
||||
payload?: InputVar
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
onConfirm: (newValue: InputVar) => void
|
||||
onConfirm: (newValue: InputVar, moreInfo?: MoreInfo) => void
|
||||
}
|
||||
|
||||
const inputClassName = 'w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
|
||||
|
|
@ -39,7 +50,6 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
const { type, label, variable, options, max_length } = tempPayload
|
||||
|
||||
const isStringInput = type === InputVarType.textInput || type === InputVarType.paragraph
|
||||
|
||||
const handlePayloadChange = useCallback((key: string) => {
|
||||
return (value: any) => {
|
||||
setTempPayload((prev) => {
|
||||
|
|
@ -52,6 +62,12 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
}, [])
|
||||
|
||||
const handleConfirm = () => {
|
||||
const moreInfo = tempPayload.variable === payload?.variable
|
||||
? undefined
|
||||
: {
|
||||
type: ChangeType.changeVarName,
|
||||
payload: { beforeKey: payload?.variable || '', afterKey: tempPayload.variable },
|
||||
}
|
||||
if (!tempPayload.variable) {
|
||||
Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.varNameRequired') })
|
||||
return
|
||||
|
|
@ -61,7 +77,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
return
|
||||
}
|
||||
if (isStringInput || type === InputVarType.number) {
|
||||
onConfirm(tempPayload)
|
||||
onConfirm(tempPayload, moreInfo)
|
||||
}
|
||||
else {
|
||||
if (options?.length === 0) {
|
||||
|
|
@ -81,7 +97,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
|||
Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.optionRepeat') })
|
||||
return
|
||||
}
|
||||
onConfirm(tempPayload)
|
||||
onConfirm(tempPayload, moreInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
import type {
|
||||
Edge,
|
||||
Node,
|
||||
ValueSelector,
|
||||
} from '../types'
|
||||
import {
|
||||
BlockEnum,
|
||||
|
|
@ -41,6 +42,7 @@ import {
|
|||
START_INITIAL_POSITION,
|
||||
SUPPORT_OUTPUT_VARS_NODE,
|
||||
} from '../constants'
|
||||
import { findUsedVarNodes, updateNodeVars } from '../nodes/_base/components/variable/utils'
|
||||
import {
|
||||
useNodesExtraData,
|
||||
useNodesInitialData,
|
||||
|
|
@ -55,7 +57,6 @@ import {
|
|||
} from '@/service/workflow'
|
||||
import { fetchCollectionList } from '@/service/tools'
|
||||
import I18n from '@/context/i18n'
|
||||
|
||||
export const useIsChatMode = () => {
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
|
||||
|
|
@ -211,6 +212,25 @@ export const useWorkflow = () => {
|
|||
return uniqBy(list, 'id')
|
||||
}, [store])
|
||||
|
||||
const handleOutVarRenameChange = useCallback((nodeId: string, oldValeSelector: ValueSelector, newVarSelector: ValueSelector) => {
|
||||
const { getNodes, setNodes } = store.getState()
|
||||
const afterNodes = getAfterNodesInSameBranch(nodeId)
|
||||
const effectNodes = findUsedVarNodes(oldValeSelector, afterNodes)
|
||||
console.log(effectNodes)
|
||||
if (effectNodes.length > 0) {
|
||||
// debugger
|
||||
const newNodes = getNodes().map((node) => {
|
||||
if (effectNodes.find(n => n.id === node.id))
|
||||
return updateNodeVars(node, oldValeSelector, newVarSelector)
|
||||
|
||||
return node
|
||||
})
|
||||
setNodes(newNodes)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [store])
|
||||
|
||||
const isValidConnection = useCallback(({ source, target }: Connection) => {
|
||||
const { getNodes } = store.getState()
|
||||
const nodes = getNodes()
|
||||
|
|
@ -294,6 +314,7 @@ export const useWorkflow = () => {
|
|||
getTreeLeafNodes,
|
||||
getBeforeNodesInSameBranch,
|
||||
getAfterNodesInSameBranch,
|
||||
handleOutVarRenameChange,
|
||||
isValidConnection,
|
||||
formatTimeFromNow,
|
||||
getValidTreeNodes,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,18 @@
|
|||
import produce from 'immer'
|
||||
import type { CodeNodeType } from '../../../code/types'
|
||||
import type { EndNodeType } from '../../../end/types'
|
||||
import type { AnswerNodeType } from '../../../answer/types'
|
||||
import type { LLMNodeType } from '../../../llm/types'
|
||||
import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/types'
|
||||
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 { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types'
|
||||
import {
|
||||
CHAT_QUESTION_CLASSIFIER_OUTPUT_STRUCT,
|
||||
|
|
@ -199,3 +210,116 @@ export const getVarType = (value: ValueSelector, availableNodes: any[], isChatMo
|
|||
return type
|
||||
}
|
||||
}
|
||||
|
||||
const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
||||
const { data } = node
|
||||
const { type } = data
|
||||
let res: ValueSelector[] = []
|
||||
switch (type) {
|
||||
case BlockEnum.End: {
|
||||
res = (data as EndNodeType).outputs?.map((output) => {
|
||||
return output.value_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.Answer: {
|
||||
res = (data as AnswerNodeType).variables?.map((v) => {
|
||||
return v.value_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.LLM: {
|
||||
const inputVars = (data as LLMNodeType)?.variables.map((v) => {
|
||||
return v.value_selector
|
||||
})
|
||||
|
||||
const contextVar = (data as LLMNodeType).context?.variable_selector ? [(data as LLMNodeType).context?.variable_selector] : []
|
||||
res = [...inputVars, ...contextVar]
|
||||
break
|
||||
}
|
||||
case BlockEnum.KnowledgeRetrieval: {
|
||||
res = [(data as KnowledgeRetrievalNodeType).query_variable_selector]
|
||||
break
|
||||
}
|
||||
case BlockEnum.IfElse: {
|
||||
res = (data as IfElseNodeType).conditions?.map((c) => {
|
||||
return c.variable_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.Code: {
|
||||
res = (data as CodeNodeType).variables?.map((v) => {
|
||||
return v.value_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.TemplateTransform: {
|
||||
res = (data as TemplateTransformNodeType).variables?.map((v: any) => {
|
||||
return v.value_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.QuestionClassifier: {
|
||||
res = [(data as QuestionClassifierNodeType).query_variable_selector]
|
||||
break
|
||||
}
|
||||
case BlockEnum.HttpRequest: {
|
||||
res = (data as HttpNodeType).variables?.map((v) => {
|
||||
return v.value_selector
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.Tool: {
|
||||
res = (data as ToolNodeType).tool_parameters?.filter((v) => {
|
||||
return v.variable_type === VarKindType.static
|
||||
}).map((v) => {
|
||||
return v.value_selector || []
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case BlockEnum.VariableAssigner: {
|
||||
res = (data as VariableAssignerNodeType)?.variables
|
||||
}
|
||||
}
|
||||
return res || []
|
||||
}
|
||||
|
||||
export const findUsedVarNodes = (varSelector: ValueSelector, availableNodes: Node[]): Node[] => {
|
||||
const res: Node[] = []
|
||||
availableNodes.forEach((node) => {
|
||||
const vars = getNodeUsedVars(node)
|
||||
if (vars.find(v => v.join('.') === varSelector.join('.')))
|
||||
res.push(node)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
export const updateNodeVars = (oldNode: Node, oldVarSelector: ValueSelector, newVarSelector: ValueSelector): Node => {
|
||||
const newNode = produce(oldNode, (draft: any) => {
|
||||
const { data } = draft
|
||||
const { type } = data
|
||||
switch (type) {
|
||||
case BlockEnum.End: {
|
||||
if ((data as EndNodeType).outputs) {
|
||||
(data as EndNodeType).outputs = (data as EndNodeType).outputs.map((output) => {
|
||||
if (output.value_selector.join('.') === oldVarSelector.join('.'))
|
||||
output.value_selector = newVarSelector
|
||||
return output
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case BlockEnum.Answer: {
|
||||
if ((data as AnswerNodeType).variables) {
|
||||
(data as AnswerNodeType).variables = (data as AnswerNodeType).variables.map((v) => {
|
||||
if (v.value_selector.join('.') === oldVarSelector.join('.'))
|
||||
v.value_selector = newVarSelector
|
||||
return v
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return newNode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,10 +187,10 @@ const VarReferencePicker: FC<Props> = ({
|
|||
// 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 5 buff
|
||||
const availableWidth = triggerWidth - 47
|
||||
const [maxNodeNameWidth, maxVarNameWidth, maxTypeWidth] = (() => {
|
||||
const totalTextLength = ((outputVarNode?.title) + (varName) + type).length
|
||||
const maxNodeNameWidth = Math.ceil(outputVarNode?.title.length / totalTextLength * availableWidth)
|
||||
const maxVarNameWidth = Math.ceil(varName.length / totalTextLength * availableWidth)
|
||||
const maxTypeWidth = Math.ceil(type.length / totalTextLength * availableWidth)
|
||||
const totalTextLength = ((outputVarNode?.title || '') + (varName || '') + (type || '')).length
|
||||
const maxNodeNameWidth = Math.floor((outputVarNode?.title?.length || 0) / totalTextLength * availableWidth)
|
||||
const maxVarNameWidth = Math.floor((varName?.length || 0) / totalTextLength * availableWidth)
|
||||
const maxTypeWidth = Math.floor((type?.length || 0) / totalTextLength * availableWidth)
|
||||
return [maxNodeNameWidth, maxVarNameWidth, maxTypeWidth]
|
||||
})()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ import type { InputVar } from '@/app/components/workflow/types'
|
|||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import type { MoreInfo } from '@/app/components/app/configuration/config-var/config-modal'
|
||||
import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal'
|
||||
|
||||
type Props = {
|
||||
readonly: boolean
|
||||
payload: InputVar
|
||||
onChange: (item: InputVar) => void
|
||||
onChange: (item: InputVar, moreInfo?: MoreInfo) => void
|
||||
onRemove: () => void
|
||||
}
|
||||
|
||||
|
|
@ -32,8 +33,8 @@ const VarItem: FC<Props> = ({
|
|||
setFalse: hideEditVarModal,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const handlePayloadChange = useCallback((payload: InputVar) => {
|
||||
onChange(payload)
|
||||
const handlePayloadChange = useCallback((payload: InputVar, moreInfo?: MoreInfo) => {
|
||||
onChange(payload, moreInfo)
|
||||
hideEditVarModal()
|
||||
}, [onChange, hideEditVarModal])
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import produce from 'immer'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import VarItem from './var-item'
|
||||
import type { InputVar } from '@/app/components/workflow/types'
|
||||
|
||||
import type { MoreInfo } from '@/app/components/app/configuration/config-var/config-modal'
|
||||
type Props = {
|
||||
readonly: boolean
|
||||
list: InputVar[]
|
||||
onChange: (list: InputVar[]) => void
|
||||
onChange: (list: InputVar[], moreInfo?: { index: number; payload: MoreInfo }) => void
|
||||
}
|
||||
|
||||
const VarList: FC<Props> = ({
|
||||
|
|
@ -19,12 +19,12 @@ const VarList: FC<Props> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleVarNameChange = useCallback((index: number) => {
|
||||
return (payload: InputVar) => {
|
||||
const handleVarChange = useCallback((index: number) => {
|
||||
return (payload: InputVar, moreInfo?: MoreInfo) => {
|
||||
const newList = produce(list, (draft) => {
|
||||
draft[index] = payload
|
||||
})
|
||||
onChange(newList)
|
||||
onChange(newList, moreInfo ? { index, payload: moreInfo } : undefined)
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ const VarList: FC<Props> = ({
|
|||
key={index}
|
||||
readonly={readonly}
|
||||
payload={item}
|
||||
onChange={handleVarNameChange(index)}
|
||||
onChange={handleVarChange(index)}
|
||||
onRemove={handleVarRemove(index)}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-cr
|
|||
import {
|
||||
useIsChatMode,
|
||||
useNodesReadOnly,
|
||||
useWorkflow,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
import type { MoreInfo } from '@/app/components/app/configuration/config-var/config-modal'
|
||||
|
||||
const useConfig = (id: string, payload: StartNodeType) => {
|
||||
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||
const { handleOutVarRenameChange } = useWorkflow()
|
||||
const isChatMode = useIsChatMode()
|
||||
|
||||
const { inputs, setInputs } = useNodeCrud<StartNodeType>(id, payload)
|
||||
|
|
@ -20,11 +23,15 @@ const useConfig = (id: string, payload: StartNodeType) => {
|
|||
setFalse: hideAddVarModal,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const handleVarListChange = useCallback((newList: InputVar[]) => {
|
||||
const handleVarListChange = useCallback((newList: InputVar[], moreInfo?: { index: number; payload: MoreInfo }) => {
|
||||
const newInputs = produce(inputs, (draft: any) => {
|
||||
draft.variables = newList
|
||||
})
|
||||
setInputs(newInputs)
|
||||
if (moreInfo) {
|
||||
const changedVar = newList[moreInfo.index]
|
||||
handleOutVarRenameChange(id, [id, inputs.variables[moreInfo.index].variable], [id, changedVar.variable])
|
||||
}
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const handleAddVariable = useCallback((payload: InputVar) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue