mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-10 11:26:52 +08:00
Feature/newnew workflow loop node (#14863)
Co-authored-by: arkunzz <4873204@qq.com>
This commit is contained in:
@@ -38,7 +38,7 @@ const Add = ({
|
||||
const [open, setOpen] = useState(false)
|
||||
const { handleNodeAdd } = useNodesInteractions()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
const { availableNextBlocks } = useAvailableBlocks(nodeData.type, nodeData.isInIteration)
|
||||
const { availableNextBlocks } = useAvailableBlocks(nodeData.type, nodeData.isInIteration, nodeData.isInLoop)
|
||||
const { checkParallelLimit } = useWorkflow()
|
||||
|
||||
const handleSelect = useCallback<OnSelectBlock>((type, toolDefaultValue) => {
|
||||
|
||||
@@ -36,7 +36,7 @@ const ChangeItem = ({
|
||||
const {
|
||||
availablePrevBlocks,
|
||||
availableNextBlocks,
|
||||
} = useAvailableBlocks(data.type, data.isInIteration)
|
||||
} = useAvailableBlocks(data.type, data.isInIteration, data.isInLoop)
|
||||
|
||||
const handleSelect = useCallback<OnSelectBlock>((type, toolDefaultValue) => {
|
||||
handleNodeChange(nodeId, type, sourceHandle, toolDefaultValue)
|
||||
|
||||
@@ -47,7 +47,7 @@ export const NodeTargetHandle = memo(({
|
||||
const { handleNodeAdd } = useNodesInteractions()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const connected = data._connectedTargetHandleIds?.includes(handleId)
|
||||
const { availablePrevBlocks } = useAvailableBlocks(data.type, data.isInIteration)
|
||||
const { availablePrevBlocks } = useAvailableBlocks(data.type, data.isInIteration, data.isInLoop)
|
||||
const isConnectable = !!availablePrevBlocks.length
|
||||
|
||||
const handleOpenChange = useCallback((v: boolean) => {
|
||||
@@ -129,7 +129,7 @@ export const NodeSourceHandle = memo(({
|
||||
const [open, setOpen] = useState(false)
|
||||
const { handleNodeAdd } = useNodesInteractions()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration)
|
||||
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration, data.isInLoop)
|
||||
const isConnectable = !!availableNextBlocks.length
|
||||
const isChatMode = useIsChatMode()
|
||||
const { checkParallelLimit } = useWorkflow()
|
||||
|
||||
@@ -30,7 +30,7 @@ const ChangeBlock = ({
|
||||
const {
|
||||
availablePrevBlocks,
|
||||
availableNextBlocks,
|
||||
} = useAvailableBlocks(nodeData.type, nodeData.isInIteration)
|
||||
} = useAvailableBlocks(nodeData.type, nodeData.isInIteration, nodeData.isInLoop)
|
||||
|
||||
const availableNodes = useMemo(() => {
|
||||
if (availablePrevBlocks.length && availableNextBlocks.length)
|
||||
|
||||
@@ -79,7 +79,7 @@ const PanelOperatorPopup = ({
|
||||
return customTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.description[language]
|
||||
}, [data, nodesExtraData, language, buildInTools, customTools, workflowTools])
|
||||
|
||||
const showChangeBlock = data.type !== BlockEnum.Start && !nodesReadOnly && data.type !== BlockEnum.Iteration
|
||||
const showChangeBlock = data.type !== BlockEnum.Start && !nodesReadOnly && data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop
|
||||
|
||||
const link = useNodeHelpLink(data.type)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { VarType as ToolVarType } from '../../../tool/types'
|
||||
import type { ToolNodeType } from '../../../tool/types'
|
||||
import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types'
|
||||
import type { IterationNodeType } from '../../../iteration/types'
|
||||
import type { LoopNodeType } from '../../../loop/types'
|
||||
import type { ListFilterNodeType } from '../../../list-operator/types'
|
||||
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants'
|
||||
import type { DocExtractorNodeType } from '../../../document-extractor/types'
|
||||
@@ -518,10 +519,61 @@ const getIterationItemType = ({
|
||||
}
|
||||
}
|
||||
|
||||
const getLoopItemType = ({
|
||||
valueSelector,
|
||||
beforeNodesOutputVars,
|
||||
}: {
|
||||
valueSelector: ValueSelector
|
||||
beforeNodesOutputVars: NodeOutPutVar[]
|
||||
}): VarType => {
|
||||
const outputVarNodeId = valueSelector[0]
|
||||
const isSystem = isSystemVar(valueSelector)
|
||||
|
||||
const targetVar = isSystem ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId)
|
||||
if (!targetVar)
|
||||
return VarType.string
|
||||
|
||||
let arrayType: VarType = VarType.string
|
||||
|
||||
let curr: any = targetVar.vars
|
||||
if (isSystem) {
|
||||
arrayType = curr.find((v: any) => v.variable === (valueSelector).join('.'))?.type
|
||||
}
|
||||
else {
|
||||
(valueSelector).slice(1).forEach((key, i) => {
|
||||
const isLast = i === valueSelector.length - 2
|
||||
curr = curr?.find((v: any) => v.variable === key)
|
||||
if (isLast) {
|
||||
arrayType = curr?.type
|
||||
}
|
||||
else {
|
||||
if (curr?.type === VarType.object || curr?.type === VarType.file)
|
||||
curr = curr.children
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
switch (arrayType as VarType) {
|
||||
case VarType.arrayString:
|
||||
return VarType.string
|
||||
case VarType.arrayNumber:
|
||||
return VarType.number
|
||||
case VarType.arrayObject:
|
||||
return VarType.object
|
||||
case VarType.array:
|
||||
return VarType.any
|
||||
case VarType.arrayFile:
|
||||
return VarType.file
|
||||
default:
|
||||
return VarType.string
|
||||
}
|
||||
}
|
||||
|
||||
export const getVarType = ({
|
||||
parentNode,
|
||||
valueSelector,
|
||||
isIterationItem,
|
||||
isLoopItem,
|
||||
availableNodes,
|
||||
isChatMode,
|
||||
isConstant,
|
||||
@@ -532,6 +584,7 @@ export const getVarType = ({
|
||||
valueSelector: ValueSelector
|
||||
parentNode?: Node | null
|
||||
isIterationItem?: boolean
|
||||
isLoopItem?: boolean
|
||||
availableNodes: any[]
|
||||
isChatMode: boolean
|
||||
isConstant?: boolean
|
||||
@@ -567,6 +620,26 @@ export const getVarType = ({
|
||||
if (valueSelector[1] === 'index')
|
||||
return VarType.number
|
||||
}
|
||||
|
||||
const isLoopInnerVar = parentNode?.data.type === BlockEnum.Loop
|
||||
if (isLoopItem) {
|
||||
return getLoopItemType({
|
||||
valueSelector,
|
||||
beforeNodesOutputVars,
|
||||
})
|
||||
}
|
||||
if (isLoopInnerVar) {
|
||||
if (valueSelector[1] === 'item') {
|
||||
const itemType = getLoopItemType({
|
||||
valueSelector: (parentNode?.data as any).iterator_selector || [],
|
||||
beforeNodesOutputVars,
|
||||
})
|
||||
return itemType
|
||||
}
|
||||
if (valueSelector[1] === 'index')
|
||||
return VarType.number
|
||||
}
|
||||
|
||||
const isSystem = isSystemVar(valueSelector)
|
||||
const isEnv = isENV(valueSelector)
|
||||
const isChatVar = isConversationVar(valueSelector)
|
||||
@@ -802,6 +875,14 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
||||
break
|
||||
}
|
||||
|
||||
case BlockEnum.Loop: {
|
||||
const payload = data as LoopNodeType
|
||||
res = payload.break_conditions?.map((c) => {
|
||||
return c.variable_selector || []
|
||||
}) || []
|
||||
break
|
||||
}
|
||||
|
||||
case BlockEnum.ListFilter: {
|
||||
res = [(data as ListFilterNodeType).variable]
|
||||
break
|
||||
@@ -1079,6 +1160,17 @@ export const updateNodeVars = (oldNode: Node, oldVarSelector: ValueSelector, new
|
||||
|
||||
break
|
||||
}
|
||||
case BlockEnum.Loop: {
|
||||
const payload = data as LoopNodeType
|
||||
if (payload.break_conditions) {
|
||||
payload.break_conditions = payload.break_conditions.map((c) => {
|
||||
if (c.variable_selector?.join('.') === oldVarSelector.join('.'))
|
||||
c.variable_selector = newVarSelector
|
||||
return c
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case BlockEnum.ListFilter: {
|
||||
const payload = data as ListFilterNodeType
|
||||
if (payload.variable.join('.') === oldVarSelector.join('.'))
|
||||
@@ -1200,6 +1292,11 @@ export const getNodeOutputVars = (node: Node, isChatMode: boolean): ValueSelecto
|
||||
break
|
||||
}
|
||||
|
||||
case BlockEnum.Loop: {
|
||||
res.push([id, 'output'])
|
||||
break
|
||||
}
|
||||
|
||||
case BlockEnum.DocExtractor: {
|
||||
res.push([id, 'text'])
|
||||
break
|
||||
|
||||
@@ -114,6 +114,9 @@ const VarReferencePicker: FC<Props> = ({
|
||||
const isInIteration = !!node?.data.isInIteration
|
||||
const iterationNode = isInIteration ? getNodes().find(n => n.id === node.parentId) : null
|
||||
|
||||
const isInLoop = !!node?.data.isInLoop
|
||||
const loopNode = isInLoop ? getNodes().find(n => n.id === node.parentId) : null
|
||||
|
||||
const triggerRef = useRef<HTMLDivElement>(null)
|
||||
const [triggerWidth, setTriggerWidth] = useState(TRIGGER_DEFAULT_WIDTH)
|
||||
useEffect(() => {
|
||||
@@ -142,6 +145,14 @@ const VarReferencePicker: FC<Props> = ({
|
||||
return false
|
||||
}, [isInIteration, value, node])
|
||||
|
||||
const isLoopVar = useMemo(() => {
|
||||
if (!isInLoop)
|
||||
return false
|
||||
if (value[0] === node?.parentId && ['item', 'index'].includes(value[1]))
|
||||
return true
|
||||
return false
|
||||
}, [isInLoop, value, node])
|
||||
|
||||
const outputVarNodeId = hasValue ? value[0] : ''
|
||||
const outputVarNode = useMemo(() => {
|
||||
if (!hasValue || isConstant)
|
||||
@@ -150,11 +161,14 @@ const VarReferencePicker: FC<Props> = ({
|
||||
if (isIterationVar)
|
||||
return iterationNode?.data
|
||||
|
||||
if (isLoopVar)
|
||||
return loopNode?.data
|
||||
|
||||
if (isSystemVar(value as ValueSelector))
|
||||
return startNode?.data
|
||||
|
||||
return getNodeInfoById(availableNodes, outputVarNodeId)?.data
|
||||
}, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode])
|
||||
}, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode, isLoopVar, loopNode])
|
||||
|
||||
const varName = useMemo(() => {
|
||||
if (hasValue) {
|
||||
@@ -220,7 +234,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
}, [onChange, varKindType])
|
||||
|
||||
const type = getCurrentVariableType({
|
||||
parentNode: iterationNode,
|
||||
parentNode: isInIteration ? iterationNode : loopNode,
|
||||
valueSelector: value as ValueSelector,
|
||||
availableNodes,
|
||||
isChatMode,
|
||||
|
||||
@@ -13,6 +13,7 @@ type Params = {
|
||||
passedInAvailableNodes?: Node[]
|
||||
}
|
||||
|
||||
// TODO: loop type?
|
||||
const useAvailableVarList = (nodeId: string, {
|
||||
onlyLeafNodeVar,
|
||||
filterVar,
|
||||
|
||||
@@ -27,6 +27,8 @@ export const useNodeHelpLink = (nodeType: BlockEnum) => {
|
||||
[BlockEnum.Assigner]: 'variable-assigner',
|
||||
[BlockEnum.Iteration]: 'iteration',
|
||||
[BlockEnum.IterationStart]: 'iteration',
|
||||
[BlockEnum.Loop]: 'loop',
|
||||
[BlockEnum.LoopStart]: 'loop',
|
||||
[BlockEnum.ParameterExtractor]: 'parameter-extractor',
|
||||
[BlockEnum.HttpRequest]: 'http-request',
|
||||
[BlockEnum.Tool]: 'tools',
|
||||
@@ -50,11 +52,14 @@ export const useNodeHelpLink = (nodeType: BlockEnum) => {
|
||||
[BlockEnum.Assigner]: 'variable-assigner',
|
||||
[BlockEnum.Iteration]: 'iteration',
|
||||
[BlockEnum.IterationStart]: 'iteration',
|
||||
[BlockEnum.Loop]: 'loop',
|
||||
[BlockEnum.LoopStart]: 'loop',
|
||||
[BlockEnum.ParameterExtractor]: 'parameter-extractor',
|
||||
[BlockEnum.HttpRequest]: 'http-request',
|
||||
[BlockEnum.Tool]: 'tools',
|
||||
[BlockEnum.DocExtractor]: 'doc-extractor',
|
||||
[BlockEnum.ListFilter]: 'list-operator',
|
||||
[BlockEnum.Agent]: 'agent',
|
||||
}
|
||||
}, [language])
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ const useNodeInfo = (nodeId: string) => {
|
||||
const allNodes = getNodes()
|
||||
const node = allNodes.find(n => n.id === nodeId)
|
||||
const isInIteration = !!node?.data.isInIteration
|
||||
const isInLoop = !!node?.data.isInLoop
|
||||
const parentNodeId = node?.parentId
|
||||
const parentNode = allNodes.find(n => n.id === parentNodeId)
|
||||
return {
|
||||
node,
|
||||
isInIteration,
|
||||
isInLoop,
|
||||
parentNode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { CommonNodeType, InputVar, ValueSelector, Var, Variable } from '@/a
|
||||
import { BlockEnum, InputVarType, NodeRunningStatus, VarType } from '@/app/components/workflow/types'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { getIterationSingleNodeRunUrl, singleNodeRun } from '@/service/workflow'
|
||||
import { getIterationSingleNodeRunUrl, getLoopSingleNodeRunUrl, singleNodeRun } from '@/service/workflow'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import LLMDefault from '@/app/components/workflow/nodes/llm/default'
|
||||
import KnowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default'
|
||||
@@ -28,6 +28,7 @@ import Assigner from '@/app/components/workflow/nodes/assigner/default'
|
||||
import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default'
|
||||
import IterationDefault from '@/app/components/workflow/nodes/iteration/default'
|
||||
import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default'
|
||||
import LoopDefault from '@/app/components/workflow/nodes/loop/default'
|
||||
import { ssePost } from '@/service/base'
|
||||
|
||||
import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants'
|
||||
@@ -45,6 +46,7 @@ const { checkValid: checkAssignerValid } = Assigner
|
||||
const { checkValid: checkParameterExtractorValid } = ParameterExtractorDefault
|
||||
const { checkValid: checkIterationValid } = IterationDefault
|
||||
const { checkValid: checkDocumentExtractorValid } = DocumentExtractorDefault
|
||||
const { checkValid: checkLoopValid } = LoopDefault
|
||||
|
||||
// eslint-disable-next-line ts/no-unsafe-function-type
|
||||
const checkValidFns: Record<BlockEnum, Function> = {
|
||||
@@ -61,6 +63,7 @@ const checkValidFns: Record<BlockEnum, Function> = {
|
||||
[BlockEnum.ParameterExtractor]: checkParameterExtractorValid,
|
||||
[BlockEnum.Iteration]: checkIterationValid,
|
||||
[BlockEnum.DocExtractor]: checkDocumentExtractorValid,
|
||||
[BlockEnum.Loop]: checkLoopValid,
|
||||
} as any
|
||||
|
||||
type Params<T> = {
|
||||
@@ -69,6 +72,7 @@ type Params<T> = {
|
||||
defaultRunInputData: Record<string, any>
|
||||
moreDataForCheckValid?: any
|
||||
iteratorInputKey?: string
|
||||
loopInputKey?: string
|
||||
}
|
||||
|
||||
const varTypeToInputVarType = (type: VarType, {
|
||||
@@ -100,12 +104,14 @@ const useOneStepRun = <T>({
|
||||
defaultRunInputData,
|
||||
moreDataForCheckValid,
|
||||
iteratorInputKey,
|
||||
loopInputKey,
|
||||
}: Params<T>) => {
|
||||
const { t } = useTranslation()
|
||||
const { getBeforeNodesInSameBranch, getBeforeNodesInSameBranchIncludeParent } = useWorkflow() as any
|
||||
const conversationVariables = useStore(s => s.conversationVariables)
|
||||
const isChatMode = useIsChatMode()
|
||||
const isIteration = data.type === BlockEnum.Iteration
|
||||
const isLoop = data.type === BlockEnum.Loop
|
||||
|
||||
const availableNodes = getBeforeNodesInSameBranch(id)
|
||||
const availableNodesIncludeParent = getBeforeNodesInSameBranchIncludeParent(id)
|
||||
@@ -145,12 +151,14 @@ const useOneStepRun = <T>({
|
||||
setRunInputData(data)
|
||||
}, [])
|
||||
const iterationTimes = iteratorInputKey ? runInputData[iteratorInputKey].length : 0
|
||||
const loopTimes = loopInputKey ? runInputData[loopInputKey].length : 0
|
||||
const [runResult, setRunResult] = useState<any>(null)
|
||||
|
||||
const { handleNodeDataUpdate }: { handleNodeDataUpdate: (data: any) => void } = useNodeDataUpdate()
|
||||
const [canShowSingleRun, setCanShowSingleRun] = useState(false)
|
||||
const isShowSingleRun = data._isSingleRun && canShowSingleRun
|
||||
const [iterationRunResult, setIterationRunResult] = useState<NodeTracing[]>([])
|
||||
const [loopRunResult, setLoopRunResult] = useState<NodeTracing[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkValid) {
|
||||
@@ -175,7 +183,7 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data._isSingleRun])
|
||||
|
||||
const workflowStore = useWorkflowStore()
|
||||
@@ -214,10 +222,10 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
let res: any
|
||||
try {
|
||||
if (!isIteration) {
|
||||
if (!isIteration && !isLoop) {
|
||||
res = await singleNodeRun(appId!, id, { inputs: submitData }) as any
|
||||
}
|
||||
else {
|
||||
else if (isIteration) {
|
||||
setIterationRunResult([])
|
||||
let _iterationResult: NodeTracing[] = []
|
||||
let _runResult: any = null
|
||||
@@ -315,11 +323,111 @@ const useOneStepRun = <T>({
|
||||
},
|
||||
)
|
||||
}
|
||||
if (res.error)
|
||||
else if (isLoop) {
|
||||
setLoopRunResult([])
|
||||
let _loopResult: NodeTracing[] = []
|
||||
let _runResult: any = null
|
||||
ssePost(
|
||||
getLoopSingleNodeRunUrl(isChatMode, appId!, id),
|
||||
{ body: { inputs: submitData } },
|
||||
{
|
||||
onWorkflowStarted: () => {
|
||||
},
|
||||
onWorkflowFinished: (params) => {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_singleRunningStatus: NodeRunningStatus.Succeeded,
|
||||
},
|
||||
})
|
||||
const { data: loopData } = params
|
||||
_runResult.created_by = loopData.created_by.name
|
||||
setRunResult(_runResult)
|
||||
},
|
||||
onLoopStart: (params) => {
|
||||
const newLoopRunResult = produce(_loopResult, (draft) => {
|
||||
draft.push({
|
||||
...params.data,
|
||||
status: NodeRunningStatus.Running,
|
||||
})
|
||||
})
|
||||
_loopResult = newLoopRunResult
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onLoopNext: () => {
|
||||
// loop next trigger time is triggered one more time than loopTimes
|
||||
if (_loopResult.length >= loopTimes!)
|
||||
return _loopResult.length >= loopTimes!
|
||||
},
|
||||
onLoopFinish: (params) => {
|
||||
_runResult = params.data
|
||||
setRunResult(_runResult)
|
||||
|
||||
const loopRunResult = _loopResult
|
||||
const currentIndex = loopRunResult.findIndex(trace => trace.id === params.data.id)
|
||||
const newLoopRunResult = produce(loopRunResult, (draft) => {
|
||||
if (currentIndex > -1) {
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...data,
|
||||
}
|
||||
}
|
||||
})
|
||||
_loopResult = newLoopRunResult
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onNodeStarted: (params) => {
|
||||
const newLoopRunResult = produce(_loopResult, (draft) => {
|
||||
draft.push({
|
||||
...params.data,
|
||||
status: NodeRunningStatus.Running,
|
||||
})
|
||||
})
|
||||
_loopResult = newLoopRunResult
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onNodeFinished: (params) => {
|
||||
const loopRunResult = _loopResult
|
||||
|
||||
const { data } = params
|
||||
const currentIndex = loopRunResult.findIndex(trace => trace.id === data.id)
|
||||
const newLoopRunResult = produce(loopRunResult, (draft) => {
|
||||
if (currentIndex > -1) {
|
||||
draft[currentIndex] = {
|
||||
...draft[currentIndex],
|
||||
...data,
|
||||
}
|
||||
}
|
||||
})
|
||||
_loopResult = newLoopRunResult
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onNodeRetry: (params) => {
|
||||
const newLoopRunResult = produce(_loopResult, (draft) => {
|
||||
draft.push(params.data)
|
||||
})
|
||||
_loopResult = newLoopRunResult
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onError: () => {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_singleRunningStatus: NodeRunningStatus.Failed,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
if (res && res.error)
|
||||
throw new Error(res.error)
|
||||
}
|
||||
catch (e: any) {
|
||||
if (!isIteration) {
|
||||
console.error(e)
|
||||
if (!isIteration && !isLoop) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
@@ -331,7 +439,7 @@ const useOneStepRun = <T>({
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (!isIteration) {
|
||||
if (!isIteration && !isLoop) {
|
||||
setRunResult({
|
||||
...res,
|
||||
total_tokens: res.execution_metadata?.total_tokens || 0,
|
||||
@@ -339,7 +447,7 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!isIteration) {
|
||||
if (!isIteration && !isLoop) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
@@ -430,6 +538,7 @@ const useOneStepRun = <T>({
|
||||
setRunInputData: handleSetRunInputData,
|
||||
runResult,
|
||||
iterationRunResult,
|
||||
loopRunResult,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
hasRetryNode,
|
||||
} from '../../utils'
|
||||
import { useNodeIterationInteractions } from '../iteration/use-interactions'
|
||||
import { useNodeLoopInteractions } from '../loop/use-interactions'
|
||||
import type { IterationNodeType } from '../iteration/types'
|
||||
import {
|
||||
NodeSourceHandle,
|
||||
@@ -57,6 +58,7 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
const nodeRef = useRef<HTMLDivElement>(null)
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions()
|
||||
const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions()
|
||||
const toolIcon = useToolIcon(data)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -73,6 +75,20 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
}
|
||||
}, [data.isInIteration, data.selected, id, handleNodeIterationChildSizeChange])
|
||||
|
||||
useEffect(() => {
|
||||
if (nodeRef.current && data.selected && data.isInLoop) {
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
handleNodeLoopChildSizeChange(id)
|
||||
})
|
||||
|
||||
resizeObserver.observe(nodeRef.current)
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}
|
||||
}, [data.isInLoop, data.selected, id, handleNodeLoopChildSizeChange])
|
||||
|
||||
const showSelectedBorder = data.selected || data._isBundled || data._isEntering
|
||||
const {
|
||||
showRunningBorder,
|
||||
@@ -98,16 +114,16 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
)}
|
||||
ref={nodeRef}
|
||||
style={{
|
||||
width: data.type === BlockEnum.Iteration ? data.width : 'auto',
|
||||
height: data.type === BlockEnum.Iteration ? data.height : 'auto',
|
||||
width: (data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) ? data.width : 'auto',
|
||||
height: (data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) ? data.height : 'auto',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'group relative pb-1 shadow-xs',
|
||||
'border border-transparent rounded-[15px]',
|
||||
data.type !== BlockEnum.Iteration && 'w-[240px] bg-workflow-block-bg',
|
||||
data.type === BlockEnum.Iteration && 'flex flex-col w-full h-full bg-workflow-block-bg-transparent border-workflow-block-border',
|
||||
(data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop) && 'w-[240px] bg-workflow-block-bg',
|
||||
(data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && 'flex flex-col w-full h-full bg-workflow-block-bg-transparent border-workflow-block-border',
|
||||
!data._runningStatus && 'hover:shadow-lg',
|
||||
showRunningBorder && '!border-state-accent-solid',
|
||||
showSuccessBorder && '!border-state-success-solid',
|
||||
@@ -139,6 +155,14 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
data.type === BlockEnum.Loop && (
|
||||
<NodeResizer
|
||||
nodeId={id}
|
||||
nodeData={data}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!data._isCandidate && (
|
||||
<NodeTargetHandle
|
||||
@@ -169,7 +193,7 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
}
|
||||
<div className={cn(
|
||||
'flex items-center px-3 pt-3 pb-2 rounded-t-2xl',
|
||||
data.type === BlockEnum.Iteration && 'bg-transparent',
|
||||
(data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && 'bg-transparent',
|
||||
)}>
|
||||
<BlockIcon
|
||||
className='shrink-0 mr-2'
|
||||
@@ -208,6 +232,13 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
data._loopLength && data._loopIndex && data._runningStatus === NodeRunningStatus.Running && (
|
||||
<div className='mr-1.5 text-xs font-medium text-primary-600'>
|
||||
{data._loopIndex > data._loopLength ? data._loopLength : data._loopIndex}/{data._loopLength}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
(data._runningStatus === NodeRunningStatus.Running || data._singleRunningStatus === NodeRunningStatus.Running) && (
|
||||
<RiLoader2Line className='w-3.5 h-3.5 text-text-accent animate-spin' />
|
||||
@@ -230,12 +261,12 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
}
|
||||
</div>
|
||||
{
|
||||
data.type !== BlockEnum.Iteration && (
|
||||
data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop && (
|
||||
cloneElement(children, { id, data })
|
||||
)
|
||||
}
|
||||
{
|
||||
data.type === BlockEnum.Iteration && (
|
||||
(data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && (
|
||||
<div className='grow pl-1 pr-1 pb-1'>
|
||||
{cloneElement(children, { id, data })}
|
||||
</div>
|
||||
@@ -258,7 +289,7 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
)
|
||||
}
|
||||
{
|
||||
data.desc && data.type !== BlockEnum.Iteration && (
|
||||
data.desc && data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop && (
|
||||
<div className='px-3 pt-1 pb-2 system-xs-regular text-text-tertiary whitespace-pre-line break-words'>
|
||||
{data.desc}
|
||||
</div>
|
||||
|
||||
@@ -61,14 +61,14 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
showMessageLogModal: state.showMessageLogModal,
|
||||
})))
|
||||
const showSingleRunPanel = useStore(s => s.showSingleRunPanel)
|
||||
const panelWidth = localStorage.getItem('workflow-node-panel-width') ? parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420
|
||||
const panelWidth = localStorage.getItem('workflow-node-panel-width') ? Number.parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420
|
||||
const {
|
||||
setPanelWidth,
|
||||
} = useWorkflow()
|
||||
const { handleNodeSelect } = useNodesInteractions()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration)
|
||||
const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration, data.isInLoop)
|
||||
const toolIcon = useToolIcon(data)
|
||||
|
||||
const handleResize = useCallback((width: number) => {
|
||||
|
||||
Reference in New Issue
Block a user