Feature/newnew workflow loop node (#14863)

Co-authored-by: arkunzz <4873204@qq.com>
This commit is contained in:
Wood
2025-03-05 17:41:15 +08:00
committed by GitHub
parent da91217bc9
commit 2c17bb2c36
131 changed files with 6031 additions and 159 deletions

View File

@@ -13,6 +13,7 @@ type Params = {
passedInAvailableNodes?: Node[]
}
// TODO: loop type?
const useAvailableVarList = (nodeId: string, {
onlyLeafNodeVar,
filterVar,

View File

@@ -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])

View File

@@ -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,
}
}

View File

@@ -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,
}
}