use-workflow-run.ts 9.2 KB

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