123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- import { useCallback } from 'react'
- import {
- getIncomers,
- useReactFlow,
- useStoreApi,
- } from 'reactflow'
- import produce from 'immer'
- import { v4 as uuidV4 } from 'uuid'
- import { usePathname } from 'next/navigation'
- import { useWorkflowStore } from '../store'
- import { useNodesSyncDraft } from '../hooks'
- import type { Node } from '../types'
- import {
- NodeRunningStatus,
- WorkflowRunningStatus,
- } from '../types'
- import { DEFAULT_ITER_TIMES } from '../constants'
- import { useWorkflowUpdate } from './use-workflow-interactions'
- import { useStore as useAppStore } from '@/app/components/app/store'
- import type { IOtherOptions } from '@/service/base'
- import { ssePost } from '@/service/base'
- import {
- fetchPublishedWorkflow,
- stopWorkflowRun,
- } from '@/service/workflow'
- import { useFeaturesStore } from '@/app/components/base/features/hooks'
- import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
- import {
- getProcessedFilesFromResponse,
- } from '@/app/components/base/file-uploader/utils'
- export const useWorkflowRun = () => {
- const store = useStoreApi()
- const workflowStore = useWorkflowStore()
- const reactflow = useReactFlow()
- const featuresStore = useFeaturesStore()
- const { doSyncWorkflowDraft } = useNodesSyncDraft()
- const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
- const pathname = usePathname()
- const handleBackupDraft = useCallback(() => {
- const {
- getNodes,
- edges,
- } = store.getState()
- const { getViewport } = reactflow
- const {
- backupDraft,
- setBackupDraft,
- environmentVariables,
- } = workflowStore.getState()
- const { features } = featuresStore!.getState()
- if (!backupDraft) {
- setBackupDraft({
- nodes: getNodes(),
- edges,
- viewport: getViewport(),
- features,
- environmentVariables,
- })
- doSyncWorkflowDraft()
- }
- }, [reactflow, workflowStore, store, featuresStore, doSyncWorkflowDraft])
- const handleLoadBackupDraft = useCallback(() => {
- const {
- backupDraft,
- setBackupDraft,
- setEnvironmentVariables,
- } = workflowStore.getState()
- if (backupDraft) {
- const {
- nodes,
- edges,
- viewport,
- features,
- environmentVariables,
- } = backupDraft
- handleUpdateWorkflowCanvas({
- nodes,
- edges,
- viewport,
- })
- setEnvironmentVariables(environmentVariables)
- featuresStore!.setState({ features })
- setBackupDraft(undefined)
- }
- }, [handleUpdateWorkflowCanvas, workflowStore, featuresStore])
- const handleRun = useCallback(async (
- params: any,
- callback?: IOtherOptions,
- ) => {
- const {
- getNodes,
- setNodes,
- } = store.getState()
- const newNodes = produce(getNodes(), (draft) => {
- draft.forEach((node) => {
- node.data.selected = false
- node.data._runningStatus = undefined
- })
- })
- setNodes(newNodes)
- await doSyncWorkflowDraft()
- const {
- onWorkflowStarted,
- onWorkflowFinished,
- onNodeStarted,
- onNodeFinished,
- onIterationStart,
- onIterationNext,
- onIterationFinish,
- onError,
- ...restCallback
- } = callback || {}
- workflowStore.setState({ historyWorkflowData: undefined })
- const appDetail = useAppStore.getState().appDetail
- const workflowContainer = document.getElementById('workflow-container')
- const {
- clientWidth,
- clientHeight,
- } = workflowContainer!
- let url = ''
- if (appDetail?.mode === 'advanced-chat')
- url = `/apps/${appDetail.id}/advanced-chat/workflows/draft/run`
- if (appDetail?.mode === 'workflow')
- url = `/apps/${appDetail.id}/workflows/draft/run`
- let prevNodeId = ''
- const {
- setWorkflowRunningData,
- } = workflowStore.getState()
- setWorkflowRunningData({
- result: {
- status: WorkflowRunningStatus.Running,
- },
- tracing: [],
- resultText: '',
- })
- let ttsUrl = ''
- let ttsIsPublic = false
- if (params.token) {
- ttsUrl = '/text-to-audio'
- ttsIsPublic = true
- }
- else if (params.appId) {
- if (pathname.search('explore/installed') > -1)
- ttsUrl = `/installed-apps/${params.appId}/text-to-audio`
- else
- ttsUrl = `/apps/${params.appId}/text-to-audio`
- }
- const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', (_: any): any => {})
- ssePost(
- url,
- {
- body: params,
- },
- {
- onWorkflowStarted: (params) => {
- const { task_id, data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- setIterParallelLogMap,
- } = workflowStore.getState()
- const {
- edges,
- setEdges,
- } = store.getState()
- setIterParallelLogMap(new Map())
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.task_id = task_id
- draft.result = {
- ...draft?.result,
- ...data,
- status: WorkflowRunningStatus.Running,
- }
- }))
- const newEdges = produce(edges, (draft) => {
- draft.forEach((edge) => {
- edge.data = {
- ...edge.data,
- _run: false,
- }
- })
- })
- setEdges(newEdges)
- if (onWorkflowStarted)
- onWorkflowStarted(params)
- },
- onWorkflowFinished: (params) => {
- const { data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- } = workflowStore.getState()
- const isStringOutput = data.outputs && Object.keys(data.outputs).length === 1 && typeof data.outputs[Object.keys(data.outputs)[0]] === 'string'
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.result = {
- ...draft.result,
- ...data,
- files: getProcessedFilesFromResponse(data.files || []),
- } as any
- if (isStringOutput) {
- draft.resultTabActive = true
- draft.resultText = data.outputs[Object.keys(data.outputs)[0]]
- }
- }))
- prevNodeId = ''
- if (onWorkflowFinished)
- onWorkflowFinished(params)
- },
- onError: (params) => {
- const {
- workflowRunningData,
- setWorkflowRunningData,
- } = workflowStore.getState()
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.result = {
- ...draft.result,
- status: WorkflowRunningStatus.Failed,
- }
- }))
- if (onError)
- onError(params)
- },
- onNodeStarted: (params) => {
- const { data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- iterParallelLogMap,
- setIterParallelLogMap,
- } = workflowStore.getState()
- const {
- getNodes,
- setNodes,
- edges,
- setEdges,
- transform,
- } = store.getState()
- const nodes = getNodes()
- const node = nodes.find(node => node.id === data.node_id)
- if (node?.parentId) {
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const tracing = draft.tracing!
- const iterations = tracing.find(trace => trace.node_id === node?.parentId)
- const currIteration = iterations?.details![node.data.iteration_index] || iterations?.details![iterations.details!.length - 1]
- if (!data.parallel_run_id) {
- currIteration?.push({
- ...data,
- status: NodeRunningStatus.Running,
- } as any)
- }
- else {
- const nodeId = iterations?.node_id as string
- if (!iterParallelLogMap.has(nodeId as string))
- iterParallelLogMap.set(iterations?.node_id as string, new Map())
- const currentIterLogMap = iterParallelLogMap.get(nodeId)!
- if (!currentIterLogMap.has(data.parallel_run_id))
- currentIterLogMap.set(data.parallel_run_id, [{ ...data, status: NodeRunningStatus.Running } as any])
- else
- currentIterLogMap.get(data.parallel_run_id)!.push({ ...data, status: NodeRunningStatus.Running } as any)
- setIterParallelLogMap(iterParallelLogMap)
- if (iterations)
- iterations.details = Array.from(currentIterLogMap.values())
- }
- }))
- }
- else {
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.tracing!.push({
- ...data,
- status: NodeRunningStatus.Running,
- } as any)
- }))
- const {
- setViewport,
- } = reactflow
- const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id)
- const currentNode = nodes[currentNodeIndex]
- const position = currentNode.position
- const zoom = transform[2]
- if (!currentNode.parentId) {
- setViewport({
- x: (clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom,
- y: (clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom,
- zoom: transform[2],
- })
- }
- const newNodes = produce(nodes, (draft) => {
- draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running
- })
- setNodes(newNodes)
- const incomeNodesId = getIncomers({ id: data.node_id } as Node, newNodes, edges).filter(node => node.data._runningStatus === NodeRunningStatus.Succeeded).map(node => node.id)
- const newEdges = produce(edges, (draft) => {
- draft.forEach((edge) => {
- if (edge.target === data.node_id && incomeNodesId.includes(edge.source))
- edge.data = { ...edge.data, _run: true } as any
- })
- })
- setEdges(newEdges)
- }
- if (onNodeStarted)
- onNodeStarted(params)
- },
- onNodeFinished: (params) => {
- const { data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- iterParallelLogMap,
- setIterParallelLogMap,
- } = workflowStore.getState()
- const {
- getNodes,
- setNodes,
- } = store.getState()
- const nodes = getNodes()
- const nodeParentId = nodes.find(node => node.id === data.node_id)!.parentId
- if (nodeParentId) {
- if (!data.execution_metadata.parallel_mode_run_id) {
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const tracing = draft.tracing!
- const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node
- if (iterations && iterations.details) {
- const iterationIndex = data.execution_metadata?.iteration_index || 0
- if (!iterations.details[iterationIndex])
- iterations.details[iterationIndex] = []
- const currIteration = iterations.details[iterationIndex]
- const nodeIndex = currIteration.findIndex(node =>
- node.node_id === data.node_id && (
- node.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || node.parallel_id === data.execution_metadata?.parallel_id),
- )
- if (nodeIndex !== -1) {
- currIteration[nodeIndex] = {
- ...currIteration[nodeIndex],
- ...data,
- } as any
- }
- else {
- currIteration.push({
- ...data,
- } as any)
- }
- }
- }))
- }
- else {
- // open parallel mode
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const tracing = draft.tracing!
- const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node
- if (iterations && iterations.details) {
- const iterRunID = data.execution_metadata?.parallel_mode_run_id
- const currIteration = iterParallelLogMap.get(iterations.node_id)?.get(iterRunID)
- const nodeIndex = currIteration?.findIndex(node =>
- node.node_id === data.node_id && (
- node?.parallel_run_id === data.execution_metadata?.parallel_mode_run_id),
- )
- if (currIteration) {
- if (nodeIndex !== undefined && nodeIndex !== -1) {
- currIteration[nodeIndex] = {
- ...currIteration[nodeIndex],
- ...data,
- } as any
- }
- else {
- currIteration.push({
- ...data,
- } as any)
- }
- }
- setIterParallelLogMap(iterParallelLogMap)
- const iterLogMap = iterParallelLogMap.get(iterations.node_id)
- if (iterLogMap)
- iterations.details = Array.from(iterLogMap.values())
- }
- }))
- }
- }
- else {
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const currentIndex = draft.tracing!.findIndex((trace) => {
- if (!trace.execution_metadata?.parallel_id)
- return trace.node_id === data.node_id
- return trace.node_id === data.node_id && trace.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id
- })
- if (currentIndex > -1 && draft.tracing) {
- draft.tracing[currentIndex] = {
- ...(draft.tracing[currentIndex].extras
- ? { extras: draft.tracing[currentIndex].extras }
- : {}),
- ...data,
- } as any
- }
- }))
- const newNodes = produce(nodes, (draft) => {
- const currentNode = draft.find(node => node.id === data.node_id)!
- currentNode.data._runningStatus = data.status as any
- })
- setNodes(newNodes)
- prevNodeId = data.node_id
- }
- if (onNodeFinished)
- onNodeFinished(params)
- },
- onIterationStart: (params) => {
- const { data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- setIterTimes,
- } = workflowStore.getState()
- const {
- getNodes,
- setNodes,
- edges,
- setEdges,
- transform,
- } = store.getState()
- const nodes = getNodes()
- setIterTimes(DEFAULT_ITER_TIMES)
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.tracing!.push({
- ...data,
- status: NodeRunningStatus.Running,
- details: [],
- iterDurationMap: {},
- } as any)
- }))
- const {
- setViewport,
- } = reactflow
- const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id)
- const currentNode = nodes[currentNodeIndex]
- const position = currentNode.position
- const zoom = transform[2]
- if (!currentNode.parentId) {
- setViewport({
- x: (clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom,
- y: (clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom,
- zoom: transform[2],
- })
- }
- const newNodes = produce(nodes, (draft) => {
- draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running
- draft[currentNodeIndex].data._iterationLength = data.metadata.iterator_length
- })
- setNodes(newNodes)
- const newEdges = produce(edges, (draft) => {
- const edge = draft.find(edge => edge.target === data.node_id && edge.source === prevNodeId)
- if (edge)
- edge.data = { ...edge.data, _run: true } as any
- })
- setEdges(newEdges)
- if (onIterationStart)
- onIterationStart(params)
- },
- onIterationNext: (params) => {
- const {
- workflowRunningData,
- setWorkflowRunningData,
- iterTimes,
- setIterTimes,
- } = workflowStore.getState()
- const { data } = params
- const {
- getNodes,
- setNodes,
- } = store.getState()
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const iteration = draft.tracing!.find(trace => trace.node_id === data.node_id)
- if (iteration) {
- if (iteration.iterDurationMap && data.duration)
- iteration.iterDurationMap[data.parallel_mode_run_id ?? `${data.index - 1}`] = data.duration
- if (iteration.details!.length >= iteration.metadata.iterator_length!)
- return
- }
- if (!data.parallel_mode_run_id)
- iteration?.details!.push([])
- }))
- const nodes = getNodes()
- const newNodes = produce(nodes, (draft) => {
- const currentNode = draft.find(node => node.id === data.node_id)!
- currentNode.data._iterationIndex = iterTimes
- setIterTimes(iterTimes + 1)
- })
- setNodes(newNodes)
- if (onIterationNext)
- onIterationNext(params)
- },
- onIterationFinish: (params) => {
- const { data } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- setIterTimes,
- } = workflowStore.getState()
- const {
- getNodes,
- setNodes,
- } = store.getState()
- const nodes = getNodes()
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- const tracing = draft.tracing!
- const currIterationNode = tracing.find(trace => trace.node_id === data.node_id)
- if (currIterationNode) {
- Object.assign(currIterationNode, {
- ...data,
- status: NodeRunningStatus.Succeeded,
- })
- }
- }))
- setIterTimes(DEFAULT_ITER_TIMES)
- const newNodes = produce(nodes, (draft) => {
- const currentNode = draft.find(node => node.id === data.node_id)!
- currentNode.data._runningStatus = data.status
- })
- setNodes(newNodes)
- prevNodeId = data.node_id
- if (onIterationFinish)
- onIterationFinish(params)
- },
- onParallelBranchStarted: (params) => {
- // console.log(params, 'parallel start')
- },
- onParallelBranchFinished: (params) => {
- // console.log(params, 'finished')
- },
- onTextChunk: (params) => {
- const { data: { text } } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- } = workflowStore.getState()
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.resultTabActive = true
- draft.resultText += text
- }))
- },
- onTextReplace: (params) => {
- const { data: { text } } = params
- const {
- workflowRunningData,
- setWorkflowRunningData,
- } = workflowStore.getState()
- setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
- draft.resultText = text
- }))
- },
- onTTSChunk: (messageId: string, audio: string, audioType?: string) => {
- if (!audio || audio === '')
- return
- player.playAudioWithAudio(audio, true)
- AudioPlayerManager.getInstance().resetMsgId(messageId)
- },
- onTTSEnd: (messageId: string, audio: string, audioType?: string) => {
- player.playAudioWithAudio(audio, false)
- },
- ...restCallback,
- },
- )
- }, [store, reactflow, workflowStore, doSyncWorkflowDraft])
- const handleStopRun = useCallback((taskId: string) => {
- const appId = useAppStore.getState().appDetail?.id
- stopWorkflowRun(`/apps/${appId}/workflow-runs/tasks/${taskId}/stop`)
- }, [])
- const handleRestoreFromPublishedWorkflow = useCallback(async () => {
- const appDetail = useAppStore.getState().appDetail
- const publishedWorkflow = await fetchPublishedWorkflow(`/apps/${appDetail?.id}/workflows/publish`)
- if (publishedWorkflow) {
- const nodes = publishedWorkflow.graph.nodes
- const edges = publishedWorkflow.graph.edges
- const viewport = publishedWorkflow.graph.viewport!
- handleUpdateWorkflowCanvas({
- nodes,
- edges,
- viewport,
- })
- featuresStore?.setState({ features: publishedWorkflow.features })
- workflowStore.getState().setPublishedAt(publishedWorkflow.created_at)
- workflowStore.getState().setEnvironmentVariables(publishedWorkflow.environment_variables || [])
- }
- }, [featuresStore, handleUpdateWorkflowCanvas, workflowStore])
- return {
- handleBackupDraft,
- handleLoadBackupDraft,
- handleRun,
- handleStopRun,
- handleRestoreFromPublishedWorkflow,
- }
- }
|