-
-
+ ({ ...caseItem, id: caseItem.case_id }))}
+ setList={handleSortCase}
+ handle='.handle'
+ ghostClass='bg-components-panel-bg'
+ animation={150}
+ >
+ {
+ cases.map((item, index) => (
+
+
+
1 && 'group-hover:block',
+ )} />
+
+ {
+ index === 0 ? 'IF' : 'ELIF'
+ }
+ {
+ casesLength > 1 && (
+
CASE {index + 1}
+ )
+ }
+
+ {
+ !!item.conditions.length && (
+
+
+
+ )
+ }
+
+
+ {
+ ((index === 0 && casesLength > 1) || (index > 0)) && (
+
+ )
+ }
+
+
+
+
+ ))
+ }
+
+
+
+
+
+ {t(`${i18nPrefix}.elseDescription`)}
+
)
}
-export default React.memo(Panel)
+export default memo(Panel)
diff --git a/web/app/components/workflow/nodes/if-else/types.ts b/web/app/components/workflow/nodes/if-else/types.ts
index 45adf375e5..693dce1784 100644
--- a/web/app/components/workflow/nodes/if-else/types.ts
+++ b/web/app/components/workflow/nodes/if-else/types.ts
@@ -1,4 +1,10 @@
-import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
+import type { VarType as NumberVarType } from '../tool/types'
+import type {
+ CommonNodeType,
+ ValueSelector,
+ Var,
+ VarType,
+} from '@/app/components/workflow/types'
export enum LogicalOperator {
and = 'and',
@@ -26,12 +32,26 @@ export enum ComparisonOperator {
export type Condition = {
id: string
+ varType: VarType
variable_selector: ValueSelector
comparison_operator?: ComparisonOperator
value: string
+ numberVarType?: NumberVarType
}
-export type IfElseNodeType = CommonNodeType & {
+export type CaseItem = {
+ case_id: string
logical_operator: LogicalOperator
conditions: Condition[]
}
+
+export type IfElseNodeType = CommonNodeType & {
+ logical_operator?: LogicalOperator
+ conditions?: Condition[]
+ cases: CaseItem[]
+}
+
+export type HandleAddCondition = (caseId: string, valueSelector: ValueSelector, varItem: Var) => void
+export type HandleRemoveCondition = (caseId: string, conditionId: string) => void
+export type HandleUpdateCondition = (caseId: string, conditionId: string, newCondition: Condition) => void
+export type HandleUpdateConditionLogicalOperator = (caseId: string, value: LogicalOperator) => void
diff --git a/web/app/components/workflow/nodes/if-else/use-config.ts b/web/app/components/workflow/nodes/if-else/use-config.ts
index b20a5b56ea..d3e2785986 100644
--- a/web/app/components/workflow/nodes/if-else/use-config.ts
+++ b/web/app/components/workflow/nodes/if-else/use-config.ts
@@ -1,76 +1,177 @@
import { useCallback } from 'react'
import produce from 'immer'
-import type { Var } from '../../types'
+import { v4 as uuid4 } from 'uuid'
+import type {
+ Var,
+} from '../../types'
import { VarType } from '../../types'
-import { getVarType } from '../_base/components/variable/utils'
-import useNodeInfo from '../_base/hooks/use-node-info'
import { LogicalOperator } from './types'
-import type { Condition, IfElseNodeType } from './types'
+import type {
+ CaseItem,
+ HandleAddCondition,
+ HandleRemoveCondition,
+ HandleUpdateCondition,
+ HandleUpdateConditionLogicalOperator,
+ IfElseNodeType,
+} from './types'
+import {
+ branchNameCorrect,
+ getOperators,
+} from './utils'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import {
- useIsChatMode,
+ useEdgesInteractions,
useNodesReadOnly,
- useWorkflow,
} from '@/app/components/workflow/hooks'
+import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
const useConfig = (id: string, payload: IfElseNodeType) => {
const { nodesReadOnly: readOnly } = useNodesReadOnly()
- const { getBeforeNodesInSameBranch } = useWorkflow()
- const {
- parentNode,
- } = useNodeInfo(id)
- const isChatMode = useIsChatMode()
- const beforeNodes = getBeforeNodesInSameBranch(id)
-
+ const { handleEdgeDeleteByDeleteBranch } = useEdgesInteractions()
const { inputs, setInputs } = useNodeCrud
(id, payload)
- const handleConditionsChange = useCallback((newConditions: Condition[]) => {
- const newInputs = produce(inputs, (draft) => {
- draft.conditions = newConditions
- })
- setInputs(newInputs)
- }, [inputs, setInputs])
-
- const handleAddCondition = useCallback(() => {
- const newInputs = produce(inputs, (draft) => {
- draft.conditions.push({
- id: `${Date.now()}`,
- variable_selector: [],
- comparison_operator: undefined,
- value: '',
- })
- })
- setInputs(newInputs)
- }, [inputs, setInputs])
-
- const handleLogicalOperatorToggle = useCallback(() => {
- const newInputs = produce(inputs, (draft) => {
- draft.logical_operator = draft.logical_operator === LogicalOperator.and ? LogicalOperator.or : LogicalOperator.and
- })
- setInputs(newInputs)
- }, [inputs, setInputs])
-
const filterVar = useCallback((varPayload: Var) => {
return varPayload.type !== VarType.arrayFile
}, [])
- const varTypesList = (inputs.conditions || []).map((condition) => {
- return getVarType({
- parentNode,
- valueSelector: condition.variable_selector,
- availableNodes: beforeNodes,
- isChatMode,
- })
+ const {
+ availableVars,
+ availableNodesWithParent,
+ } = useAvailableVarList(id, {
+ onlyLeafNodeVar: false,
+ filterVar,
})
+ const filterNumberVar = useCallback((varPayload: Var) => {
+ return varPayload.type === VarType.number
+ }, [])
+
+ const {
+ availableVars: availableNumberVars,
+ availableNodesWithParent: availableNumberNodesWithParent,
+ } = useAvailableVarList(id, {
+ onlyLeafNodeVar: false,
+ filterVar: filterNumberVar,
+ })
+
+ const handleAddCase = useCallback(() => {
+ const newInputs = produce(inputs, () => {
+ if (inputs.cases) {
+ const case_id = uuid4()
+ inputs.cases.push({
+ case_id,
+ logical_operator: LogicalOperator.and,
+ conditions: [],
+ })
+ if (inputs._targetBranches) {
+ const elseCaseIndex = inputs._targetBranches.findIndex(branch => branch.id === 'false')
+ if (elseCaseIndex > -1) {
+ inputs._targetBranches = branchNameCorrect([
+ ...inputs._targetBranches.slice(0, elseCaseIndex),
+ {
+ id: case_id,
+ name: '',
+ },
+ ...inputs._targetBranches.slice(elseCaseIndex),
+ ])
+ }
+ }
+ }
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const handleRemoveCase = useCallback((caseId: string) => {
+ const newInputs = produce(inputs, (draft) => {
+ draft.cases = draft.cases?.filter(item => item.case_id !== caseId)
+
+ if (draft._targetBranches)
+ draft._targetBranches = branchNameCorrect(draft._targetBranches.filter(branch => branch.id !== caseId))
+
+ handleEdgeDeleteByDeleteBranch(id, caseId)
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs, id, handleEdgeDeleteByDeleteBranch])
+
+ const handleSortCase = useCallback((newCases: (CaseItem & { id: string })[]) => {
+ const newInputs = produce(inputs, (draft) => {
+ draft.cases = newCases.filter(Boolean).map(item => ({
+ id: item.id,
+ case_id: item.case_id,
+ logical_operator: item.logical_operator,
+ conditions: item.conditions,
+ }))
+
+ draft._targetBranches = branchNameCorrect([
+ ...newCases.filter(Boolean).map(item => ({ id: item.case_id, name: '' })),
+ { id: 'false', name: '' },
+ ])
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const handleAddCondition = useCallback((caseId, valueSelector, varItem) => {
+ const newInputs = produce(inputs, (draft) => {
+ const targetCase = draft.cases?.find(item => item.case_id === caseId)
+ if (targetCase) {
+ targetCase.conditions.push({
+ id: uuid4(),
+ varType: varItem.type,
+ variable_selector: valueSelector,
+ comparison_operator: getOperators(varItem.type)[0],
+ value: '',
+ })
+ }
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const handleRemoveCondition = useCallback((caseId, conditionId) => {
+ const newInputs = produce(inputs, (draft) => {
+ const targetCase = draft.cases?.find(item => item.case_id === caseId)
+ if (targetCase)
+ targetCase.conditions = targetCase.conditions.filter(item => item.id !== conditionId)
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const handleUpdateCondition = useCallback((caseId, conditionId, newCondition) => {
+ const newInputs = produce(inputs, (draft) => {
+ const targetCase = draft.cases?.find(item => item.case_id === caseId)
+ if (targetCase) {
+ const targetCondition = targetCase.conditions.find(item => item.id === conditionId)
+ if (targetCondition)
+ Object.assign(targetCondition, newCondition)
+ }
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const handleUpdateConditionLogicalOperator = useCallback((caseId, value) => {
+ const newInputs = produce(inputs, (draft) => {
+ const targetCase = draft.cases?.find(item => item.case_id === caseId)
+ if (targetCase)
+ targetCase.logical_operator = value
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
return {
readOnly,
inputs,
- handleConditionsChange,
- handleAddCondition,
- handleLogicalOperatorToggle,
- varTypesList,
filterVar,
+ filterNumberVar,
+ handleAddCase,
+ handleRemoveCase,
+ handleSortCase,
+ handleAddCondition,
+ handleRemoveCondition,
+ handleUpdateCondition,
+ handleUpdateConditionLogicalOperator,
+ nodesOutputVars: availableVars,
+ availableNodes: availableNodesWithParent,
+ nodesOutputNumberVars: availableNumberVars,
+ availableNumberNodes: availableNumberNodesWithParent,
}
}
diff --git a/web/app/components/workflow/nodes/if-else/utils.ts b/web/app/components/workflow/nodes/if-else/utils.ts
index 51858c64aa..ffb6758bba 100644
--- a/web/app/components/workflow/nodes/if-else/utils.ts
+++ b/web/app/components/workflow/nodes/if-else/utils.ts
@@ -1,4 +1,6 @@
import { ComparisonOperator } from './types'
+import { VarType } from '@/app/components/workflow/types'
+import type { Branch } from '@/app/components/workflow/types'
export const isEmptyRelatedOperator = (operator: ComparisonOperator) => {
return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull].includes(operator)
@@ -15,3 +17,80 @@ export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator)
return false
return !notTranslateKey.includes(operator)
}
+
+export const getOperators = (type?: VarType) => {
+ switch (type) {
+ case VarType.string:
+ return [
+ ComparisonOperator.contains,
+ ComparisonOperator.notContains,
+ ComparisonOperator.startWith,
+ ComparisonOperator.endWith,
+ ComparisonOperator.is,
+ ComparisonOperator.isNot,
+ ComparisonOperator.empty,
+ ComparisonOperator.notEmpty,
+ ]
+ case VarType.number:
+ return [
+ ComparisonOperator.equal,
+ ComparisonOperator.notEqual,
+ ComparisonOperator.largerThan,
+ ComparisonOperator.lessThan,
+ ComparisonOperator.largerThanOrEqual,
+ ComparisonOperator.lessThanOrEqual,
+ ComparisonOperator.empty,
+ ComparisonOperator.notEmpty,
+ ]
+ case VarType.arrayString:
+ case VarType.arrayNumber:
+ return [
+ ComparisonOperator.contains,
+ ComparisonOperator.notContains,
+ ComparisonOperator.empty,
+ ComparisonOperator.notEmpty,
+ ]
+ case VarType.array:
+ case VarType.arrayObject:
+ return [
+ ComparisonOperator.empty,
+ ComparisonOperator.notEmpty,
+ ]
+ default:
+ return [
+ ComparisonOperator.is,
+ ComparisonOperator.isNot,
+ ComparisonOperator.empty,
+ ComparisonOperator.notEmpty,
+ ]
+ }
+}
+
+export const comparisonOperatorNotRequireValue = (operator?: ComparisonOperator) => {
+ if (!operator)
+ return false
+
+ return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull].includes(operator)
+}
+
+export const branchNameCorrect = (branches: Branch[]) => {
+ const branchLength = branches.length
+ if (branchLength < 2)
+ throw new Error('if-else node branch number must than 2')
+
+ if (branchLength === 2) {
+ return branches.map((branch) => {
+ return {
+ ...branch,
+ name: branch.id === 'false' ? 'ELSE' : 'IF',
+ }
+ })
+ }
+
+ return branches.map((branch, index) => {
+ return {
+ ...branch,
+ name: branch.id === 'false' ? 'ELSE' : `CASE ${index + 1}`,
+ }
+ })
+}
diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts
index 4ad9c6591c..0d07b2e568 100644
--- a/web/app/components/workflow/utils.ts
+++ b/web/app/components/workflow/utils.ts
@@ -14,6 +14,7 @@ import type {
InputVar,
Node,
ToolWithProvider,
+ ValueSelector,
} from './types'
import { BlockEnum } from './types'
import {
@@ -23,6 +24,8 @@ import {
START_INITIAL_POSITION,
} from './constants'
import type { QuestionClassifierNodeType } from './nodes/question-classifier/types'
+import type { IfElseNodeType } from './nodes/if-else/types'
+import { branchNameCorrect } from './nodes/if-else/utils'
import type { ToolNodeType } from './nodes/tool/types'
import { CollectionType } from '@/app/components/tools/types'
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
@@ -114,16 +117,21 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => {
node.data._connectedTargetHandleIds = connectedEdges.filter(edge => edge.target === node.id).map(edge => edge.targetHandle || 'target')
if (node.data.type === BlockEnum.IfElse) {
- node.data._targetBranches = [
- {
- id: 'true',
- name: 'IS TRUE',
- },
- {
- id: 'false',
- name: 'IS FALSE',
- },
- ]
+ const nodeData = node.data as IfElseNodeType
+
+ if (!nodeData.cases && nodeData.logical_operator && nodeData.conditions) {
+ (node.data as IfElseNodeType).cases = [
+ {
+ case_id: 'true',
+ logical_operator: nodeData.logical_operator,
+ conditions: nodeData.conditions,
+ },
+ ]
+ }
+ node.data._targetBranches = branchNameCorrect([
+ ...(node.data as IfElseNodeType).cases.map(item => ({ id: item.case_id, name: '' })),
+ { id: 'false', name: '' },
+ ])
}
if (node.data.type === BlockEnum.QuestionClassifier) {
@@ -184,6 +192,7 @@ export const initialEdges = (originEdges: Edge[], originNodes: Node[]) => {
_connectedNodeIsSelected: edge.source === selectedNode.id || edge.target === selectedNode.id,
} as any
}
+
return edge
})
}
@@ -463,3 +472,10 @@ export const isEventTargetInputArea = (target: HTMLElement) => {
if (target.contentEditable === 'true')
return true
}
+
+export const variableTransformer = (v: ValueSelector | string) => {
+ if (typeof v === 'string')
+ return v.replace(/^{{#|#}}$/g, '').split('.')
+
+ return `{{#${v.join('.')}#}}`
+}
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts
index 4ac3e82a95..568823bb3a 100644
--- a/web/i18n/en-US/workflow.ts
+++ b/web/i18n/en-US/workflow.ts
@@ -364,6 +364,7 @@ const translation = {
enterValue: 'Enter value',
addCondition: 'Add Condition',
conditionNotSetup: 'Condition NOT setup',
+ selectVariable: 'Select variable...',
},
variableAssigner: {
title: 'Assign variables',
diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts
index a71b22c8e0..2b9af83f6c 100644
--- a/web/i18n/zh-Hans/workflow.ts
+++ b/web/i18n/zh-Hans/workflow.ts
@@ -364,6 +364,7 @@ const translation = {
enterValue: '输入值',
addCondition: '添加条件',
conditionNotSetup: '条件未设置',
+ selectVariable: '选择变量',
},
variableAssigner: {
title: '变量赋值',