Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

use-workflow-run.ts 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import { useCallback } from 'react'
  2. import {
  3. useReactFlow,
  4. useStoreApi,
  5. } from 'reactflow'
  6. import produce from 'immer'
  7. import { v4 as uuidV4 } from 'uuid'
  8. import { usePathname } from 'next/navigation'
  9. import { useWorkflowStore } from '@/app/components/workflow/store'
  10. import { WorkflowRunningStatus } from '@/app/components/workflow/types'
  11. import { useWorkflowUpdate } from '@/app/components/workflow/hooks/use-workflow-interactions'
  12. import { useWorkflowRunEvent } from '@/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event'
  13. import { useStore as useAppStore } from '@/app/components/app/store'
  14. import type { IOtherOptions } from '@/service/base'
  15. import { ssePost } from '@/service/base'
  16. import { stopWorkflowRun } from '@/service/workflow'
  17. import { useFeaturesStore } from '@/app/components/base/features/hooks'
  18. import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
  19. import type { VersionHistory } from '@/types/workflow'
  20. import { noop } from 'lodash-es'
  21. import { useNodesSyncDraft } from './use-nodes-sync-draft'
  22. import { useInvalidAllLastRun } from '@/service/use-workflow'
  23. import useSetWorkflowVarsWithValue from './use-fetch-workflow-inspect-vars'
  24. export const useWorkflowRun = () => {
  25. const store = useStoreApi()
  26. const workflowStore = useWorkflowStore()
  27. const reactflow = useReactFlow()
  28. const featuresStore = useFeaturesStore()
  29. const { doSyncWorkflowDraft } = useNodesSyncDraft()
  30. const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
  31. const pathname = usePathname()
  32. const appId = useAppStore.getState().appDetail?.id
  33. const invalidAllLastRun = useInvalidAllLastRun(appId as string)
  34. const { fetchInspectVars } = useSetWorkflowVarsWithValue()
  35. const {
  36. handleWorkflowStarted,
  37. handleWorkflowFinished,
  38. handleWorkflowFailed,
  39. handleWorkflowNodeStarted,
  40. handleWorkflowNodeFinished,
  41. handleWorkflowNodeIterationStarted,
  42. handleWorkflowNodeIterationNext,
  43. handleWorkflowNodeIterationFinished,
  44. handleWorkflowNodeLoopStarted,
  45. handleWorkflowNodeLoopNext,
  46. handleWorkflowNodeLoopFinished,
  47. handleWorkflowNodeRetry,
  48. handleWorkflowAgentLog,
  49. handleWorkflowTextChunk,
  50. handleWorkflowTextReplace,
  51. } = useWorkflowRunEvent()
  52. const handleBackupDraft = useCallback(() => {
  53. const {
  54. getNodes,
  55. edges,
  56. } = store.getState()
  57. const { getViewport } = reactflow
  58. const {
  59. backupDraft,
  60. setBackupDraft,
  61. environmentVariables,
  62. } = workflowStore.getState()
  63. const { features } = featuresStore!.getState()
  64. if (!backupDraft) {
  65. setBackupDraft({
  66. nodes: getNodes(),
  67. edges,
  68. viewport: getViewport(),
  69. features,
  70. environmentVariables,
  71. })
  72. doSyncWorkflowDraft()
  73. }
  74. }, [reactflow, workflowStore, store, featuresStore, doSyncWorkflowDraft])
  75. const handleLoadBackupDraft = useCallback(() => {
  76. const {
  77. backupDraft,
  78. setBackupDraft,
  79. setEnvironmentVariables,
  80. } = workflowStore.getState()
  81. if (backupDraft) {
  82. const {
  83. nodes,
  84. edges,
  85. viewport,
  86. features,
  87. environmentVariables,
  88. } = backupDraft
  89. handleUpdateWorkflowCanvas({
  90. nodes,
  91. edges,
  92. viewport,
  93. })
  94. setEnvironmentVariables(environmentVariables)
  95. featuresStore!.setState({ features })
  96. setBackupDraft(undefined)
  97. }
  98. }, [handleUpdateWorkflowCanvas, workflowStore, featuresStore])
  99. const handleRun = useCallback(async (
  100. params: any,
  101. callback?: IOtherOptions,
  102. ) => {
  103. const {
  104. getNodes,
  105. setNodes,
  106. } = store.getState()
  107. const newNodes = produce(getNodes(), (draft) => {
  108. draft.forEach((node) => {
  109. node.data.selected = false
  110. node.data._runningStatus = undefined
  111. })
  112. })
  113. setNodes(newNodes)
  114. await doSyncWorkflowDraft()
  115. const {
  116. onWorkflowStarted,
  117. onWorkflowFinished,
  118. onNodeStarted,
  119. onNodeFinished,
  120. onIterationStart,
  121. onIterationNext,
  122. onIterationFinish,
  123. onLoopStart,
  124. onLoopNext,
  125. onLoopFinish,
  126. onNodeRetry,
  127. onAgentLog,
  128. onError,
  129. ...restCallback
  130. } = callback || {}
  131. workflowStore.setState({ historyWorkflowData: undefined })
  132. const appDetail = useAppStore.getState().appDetail
  133. const workflowContainer = document.getElementById('workflow-container')
  134. const {
  135. clientWidth,
  136. clientHeight,
  137. } = workflowContainer!
  138. const isInWorkflowDebug = appDetail?.mode === 'workflow'
  139. let url = ''
  140. if (appDetail?.mode === 'advanced-chat')
  141. url = `/apps/${appDetail.id}/advanced-chat/workflows/draft/run`
  142. if (isInWorkflowDebug)
  143. url = `/apps/${appDetail.id}/workflows/draft/run`
  144. const {
  145. setWorkflowRunningData,
  146. } = workflowStore.getState()
  147. setWorkflowRunningData({
  148. result: {
  149. status: WorkflowRunningStatus.Running,
  150. },
  151. tracing: [],
  152. resultText: '',
  153. })
  154. let ttsUrl = ''
  155. let ttsIsPublic = false
  156. if (params.token) {
  157. ttsUrl = '/text-to-audio'
  158. ttsIsPublic = true
  159. }
  160. else if (params.appId) {
  161. if (pathname.search('explore/installed') > -1)
  162. ttsUrl = `/installed-apps/${params.appId}/text-to-audio`
  163. else
  164. ttsUrl = `/apps/${params.appId}/text-to-audio`
  165. }
  166. const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', noop)
  167. ssePost(
  168. url,
  169. {
  170. body: params,
  171. },
  172. {
  173. onWorkflowStarted: (params) => {
  174. handleWorkflowStarted(params)
  175. if (onWorkflowStarted)
  176. onWorkflowStarted(params)
  177. },
  178. onWorkflowFinished: (params) => {
  179. handleWorkflowFinished(params)
  180. if (onWorkflowFinished)
  181. onWorkflowFinished(params)
  182. if (isInWorkflowDebug) {
  183. fetchInspectVars()
  184. invalidAllLastRun()
  185. }
  186. },
  187. onError: (params) => {
  188. handleWorkflowFailed()
  189. if (onError)
  190. onError(params)
  191. },
  192. onNodeStarted: (params) => {
  193. handleWorkflowNodeStarted(
  194. params,
  195. {
  196. clientWidth,
  197. clientHeight,
  198. },
  199. )
  200. if (onNodeStarted)
  201. onNodeStarted(params)
  202. },
  203. onNodeFinished: (params) => {
  204. handleWorkflowNodeFinished(params)
  205. if (onNodeFinished)
  206. onNodeFinished(params)
  207. },
  208. onIterationStart: (params) => {
  209. handleWorkflowNodeIterationStarted(
  210. params,
  211. {
  212. clientWidth,
  213. clientHeight,
  214. },
  215. )
  216. if (onIterationStart)
  217. onIterationStart(params)
  218. },
  219. onIterationNext: (params) => {
  220. handleWorkflowNodeIterationNext(params)
  221. if (onIterationNext)
  222. onIterationNext(params)
  223. },
  224. onIterationFinish: (params) => {
  225. handleWorkflowNodeIterationFinished(params)
  226. if (onIterationFinish)
  227. onIterationFinish(params)
  228. },
  229. onLoopStart: (params) => {
  230. handleWorkflowNodeLoopStarted(
  231. params,
  232. {
  233. clientWidth,
  234. clientHeight,
  235. },
  236. )
  237. if (onLoopStart)
  238. onLoopStart(params)
  239. },
  240. onLoopNext: (params) => {
  241. handleWorkflowNodeLoopNext(params)
  242. if (onLoopNext)
  243. onLoopNext(params)
  244. },
  245. onLoopFinish: (params) => {
  246. handleWorkflowNodeLoopFinished(params)
  247. if (onLoopFinish)
  248. onLoopFinish(params)
  249. },
  250. onNodeRetry: (params) => {
  251. handleWorkflowNodeRetry(params)
  252. if (onNodeRetry)
  253. onNodeRetry(params)
  254. },
  255. onAgentLog: (params) => {
  256. handleWorkflowAgentLog(params)
  257. if (onAgentLog)
  258. onAgentLog(params)
  259. },
  260. onTextChunk: (params) => {
  261. handleWorkflowTextChunk(params)
  262. },
  263. onTextReplace: (params) => {
  264. handleWorkflowTextReplace(params)
  265. },
  266. onTTSChunk: (messageId: string, audio: string) => {
  267. if (!audio || audio === '')
  268. return
  269. player.playAudioWithAudio(audio, true)
  270. AudioPlayerManager.getInstance().resetMsgId(messageId)
  271. },
  272. onTTSEnd: (messageId: string, audio: string) => {
  273. player.playAudioWithAudio(audio, false)
  274. },
  275. ...restCallback,
  276. },
  277. )
  278. }, [store, doSyncWorkflowDraft, workflowStore, pathname, handleWorkflowStarted, handleWorkflowFinished, fetchInspectVars, invalidAllLastRun, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeLoopStarted, handleWorkflowNodeLoopNext, handleWorkflowNodeLoopFinished, handleWorkflowNodeRetry, handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace],
  279. )
  280. const handleStopRun = useCallback((taskId: string) => {
  281. const appId = useAppStore.getState().appDetail?.id
  282. stopWorkflowRun(`/apps/${appId}/workflow-runs/tasks/${taskId}/stop`)
  283. }, [])
  284. const handleRestoreFromPublishedWorkflow = useCallback((publishedWorkflow: VersionHistory) => {
  285. const nodes = publishedWorkflow.graph.nodes.map(node => ({ ...node, selected: false, data: { ...node.data, selected: false } }))
  286. const edges = publishedWorkflow.graph.edges
  287. const viewport = publishedWorkflow.graph.viewport!
  288. handleUpdateWorkflowCanvas({
  289. nodes,
  290. edges,
  291. viewport,
  292. })
  293. const mappedFeatures = {
  294. opening: {
  295. enabled: !!publishedWorkflow.features.opening_statement || !!publishedWorkflow.features.suggested_questions.length,
  296. opening_statement: publishedWorkflow.features.opening_statement,
  297. suggested_questions: publishedWorkflow.features.suggested_questions,
  298. },
  299. suggested: publishedWorkflow.features.suggested_questions_after_answer,
  300. text2speech: publishedWorkflow.features.text_to_speech,
  301. speech2text: publishedWorkflow.features.speech_to_text,
  302. citation: publishedWorkflow.features.retriever_resource,
  303. moderation: publishedWorkflow.features.sensitive_word_avoidance,
  304. file: publishedWorkflow.features.file_upload,
  305. }
  306. featuresStore?.setState({ features: mappedFeatures })
  307. workflowStore.getState().setEnvironmentVariables(publishedWorkflow.environment_variables || [])
  308. }, [featuresStore, handleUpdateWorkflowCanvas, workflowStore])
  309. return {
  310. handleBackupDraft,
  311. handleLoadBackupDraft,
  312. handleRun,
  313. handleStopRun,
  314. handleRestoreFromPublishedWorkflow,
  315. }
  316. }