| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 | 
							- 'use client'
 
- import type { FC } from 'react'
 
- import React, { useEffect, useRef, useState } from 'react'
 
- import { useBoolean } from 'ahooks'
 
- import { t } from 'i18next'
 
- import produce from 'immer'
 
- import cn from 'classnames'
 
- import TextGenerationRes from '@/app/components/app/text-generate/item'
 
- import NoData from '@/app/components/share/text-generation/no-data'
 
- import Toast from '@/app/components/base/toast'
 
- import { sendCompletionMessage, sendWorkflowMessage, updateFeedback } from '@/service/share'
 
- import type { Feedbacktype } from '@/app/components/app/chat/type'
 
- import Loading from '@/app/components/base/loading'
 
- import type { PromptConfig } from '@/models/debug'
 
- import type { InstalledApp } from '@/models/explore'
 
- import type { ModerationService } from '@/models/common'
 
- import { TransferMethod, type VisionFile, type VisionSettings } from '@/types/app'
 
- import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types'
 
- import type { WorkflowProcess } from '@/app/components/base/chat/types'
 
- import { sleep } from '@/utils'
 
- export type IResultProps = {
 
-   isWorkflow: boolean
 
-   isCallBatchAPI: boolean
 
-   isPC: boolean
 
-   isMobile: boolean
 
-   isInstalledApp: boolean
 
-   installedAppInfo?: InstalledApp
 
-   isError: boolean
 
-   isShowTextToSpeech: boolean
 
-   promptConfig: PromptConfig | null
 
-   moreLikeThisEnabled: boolean
 
-   inputs: Record<string, any>
 
-   controlSend?: number
 
-   controlRetry?: number
 
-   controlStopResponding?: number
 
-   onShowRes: () => void
 
-   handleSaveMessage: (messageId: string) => void
 
-   taskId?: number
 
-   onCompleted: (completionRes: string, taskId?: number, success?: boolean) => void
 
-   enableModeration?: boolean
 
-   moderationService?: (text: string) => ReturnType<ModerationService>
 
-   visionConfig: VisionSettings
 
-   completionFiles: VisionFile[]
 
- }
 
- const Result: FC<IResultProps> = ({
 
-   isWorkflow,
 
-   isCallBatchAPI,
 
-   isPC,
 
-   isMobile,
 
-   isInstalledApp,
 
-   installedAppInfo,
 
-   isError,
 
-   isShowTextToSpeech,
 
-   promptConfig,
 
-   moreLikeThisEnabled,
 
-   inputs,
 
-   controlSend,
 
-   controlRetry,
 
-   controlStopResponding,
 
-   onShowRes,
 
-   handleSaveMessage,
 
-   taskId,
 
-   onCompleted,
 
-   visionConfig,
 
-   completionFiles,
 
- }) => {
 
-   const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
 
-   useEffect(() => {
 
-     if (controlStopResponding)
 
-       setRespondingFalse()
 
-   }, [controlStopResponding])
 
-   const [completionRes, doSetCompletionRes] = useState<any>('')
 
-   const completionResRef = useRef<any>()
 
-   const setCompletionRes = (res: any) => {
 
-     completionResRef.current = res
 
-     doSetCompletionRes(res)
 
-   }
 
-   const getCompletionRes = () => completionResRef.current
 
-   const [workflowProcessData, doSetWorkflowProccessData] = useState<WorkflowProcess>()
 
-   const workflowProcessDataRef = useRef<WorkflowProcess>()
 
-   const setWorkflowProccessData = (data: WorkflowProcess) => {
 
-     workflowProcessDataRef.current = data
 
-     doSetWorkflowProccessData(data)
 
-   }
 
-   const getWorkflowProccessData = () => workflowProcessDataRef.current
 
-   const { notify } = Toast
 
-   const isNoData = !completionRes
 
-   const [messageId, setMessageId] = useState<string | null>(null)
 
-   const [feedback, setFeedback] = useState<Feedbacktype>({
 
-     rating: null,
 
-   })
 
-   const handleFeedback = async (feedback: Feedbacktype) => {
 
-     await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id)
 
-     setFeedback(feedback)
 
-   }
 
-   const logError = (message: string) => {
 
-     notify({ type: 'error', message })
 
-   }
 
-   const checkCanSend = () => {
 
-     // batch will check outer
 
-     if (isCallBatchAPI)
 
-       return true
 
-     const prompt_variables = promptConfig?.prompt_variables
 
-     if (!prompt_variables || prompt_variables?.length === 0) {
 
-       if (completionFiles.find(item => item.transfer_method === TransferMethod.local_file && !item.upload_file_id)) {
 
-         notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
 
-         return false
 
-       }
 
-       return true
 
-     }
 
-     let hasEmptyInput = ''
 
-     const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
 
-       const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
 
-       return res
 
-     }) || [] // compatible with old version
 
-     requiredVars.forEach(({ key, name }) => {
 
-       if (hasEmptyInput)
 
-         return
 
-       if (!inputs[key])
 
-         hasEmptyInput = name
 
-     })
 
-     if (hasEmptyInput) {
 
-       logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
 
-       return false
 
-     }
 
-     if (completionFiles.find(item => item.transfer_method === TransferMethod.local_file && !item.upload_file_id)) {
 
-       notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
 
-       return false
 
-     }
 
-     return !hasEmptyInput
 
-   }
 
-   const handleSend = async () => {
 
-     if (isResponding) {
 
-       notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
 
-       return false
 
-     }
 
-     if (!checkCanSend())
 
-       return
 
-     const data: Record<string, any> = {
 
-       inputs,
 
-     }
 
-     if (visionConfig.enabled && completionFiles && completionFiles?.length > 0) {
 
-       data.files = completionFiles.map((item) => {
 
-         if (item.transfer_method === TransferMethod.local_file) {
 
-           return {
 
-             ...item,
 
-             url: '',
 
-           }
 
-         }
 
-         return item
 
-       })
 
-     }
 
-     setMessageId(null)
 
-     setFeedback({
 
-       rating: null,
 
-     })
 
-     setCompletionRes('')
 
-     let res: string[] = []
 
-     let tempMessageId = ''
 
-     if (!isPC)
 
-       onShowRes()
 
-     setRespondingTrue()
 
-     let isEnd = false
 
-     let isTimeout = false;
 
-     (async () => {
 
-       await sleep(1000 * 60) // 1min timeout
 
-       if (!isEnd) {
 
-         setRespondingFalse()
 
-         onCompleted(getCompletionRes(), taskId, false)
 
-         isTimeout = true
 
-       }
 
-     })()
 
-     if (isWorkflow) {
 
-       let isInIteration = false
 
-       sendWorkflowMessage(
 
-         data,
 
-         {
 
-           onWorkflowStarted: ({ workflow_run_id }) => {
 
-             tempMessageId = workflow_run_id
 
-             setWorkflowProccessData({
 
-               status: WorkflowRunningStatus.Running,
 
-               tracing: [],
 
-               expand: false,
 
-               resultText: '',
 
-             })
 
-           },
 
-           onIterationStart: ({ data }) => {
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.expand = true
 
-               draft.tracing!.push({
 
-                 ...data,
 
-                 status: NodeRunningStatus.Running,
 
-                 expand: true,
 
-               } as any)
 
-             }))
 
-             isInIteration = true
 
-           },
 
-           onIterationNext: () => {
 
-           },
 
-           onIterationFinish: ({ data }) => {
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.expand = true
 
-               // const iteration = draft.tracing![draft.tracing!.length - 1]
 
-               draft.tracing![draft.tracing!.length - 1] = {
 
-                 ...data,
 
-                 expand: !!data.error,
 
-               } as any
 
-             }))
 
-             isInIteration = false
 
-           },
 
-           onNodeStarted: ({ data }) => {
 
-             if (isInIteration)
 
-               return
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.expand = true
 
-               draft.tracing!.push({
 
-                 ...data,
 
-                 status: NodeRunningStatus.Running,
 
-                 expand: true,
 
-               } as any)
 
-             }))
 
-           },
 
-           onNodeFinished: ({ data }) => {
 
-             if (isInIteration)
 
-               return
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               const currentIndex = draft.tracing!.findIndex(trace => trace.node_id === data.node_id)
 
-               if (currentIndex > -1 && draft.tracing) {
 
-                 draft.tracing[currentIndex] = {
 
-                   ...(draft.tracing[currentIndex].extras
 
-                     ? { extras: draft.tracing[currentIndex].extras }
 
-                     : {}),
 
-                   ...data,
 
-                   expand: !!data.error,
 
-                 } as any
 
-               }
 
-             }))
 
-           },
 
-           onWorkflowFinished: ({ data }) => {
 
-             if (isTimeout)
 
-               return
 
-             if (data.error) {
 
-               notify({ type: 'error', message: data.error })
 
-               setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-                 draft.status = WorkflowRunningStatus.Failed
 
-               }))
 
-               setRespondingFalse()
 
-               onCompleted(getCompletionRes(), taskId, false)
 
-               isEnd = true
 
-               return
 
-             }
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.status = WorkflowRunningStatus.Succeeded
 
-             }))
 
-             if (!data.outputs) {
 
-               setCompletionRes('')
 
-             }
 
-             else {
 
-               setCompletionRes(data.outputs)
 
-               const isStringOutput = Object.keys(data.outputs).length === 1 && typeof data.outputs[Object.keys(data.outputs)[0]] === 'string'
 
-               if (isStringOutput) {
 
-                 setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-                   draft.resultText = data.outputs[Object.keys(data.outputs)[0]]
 
-                 }))
 
-               }
 
-             }
 
-             setRespondingFalse()
 
-             setMessageId(tempMessageId)
 
-             onCompleted(getCompletionRes(), taskId, true)
 
-             isEnd = true
 
-           },
 
-           onTextChunk: (params) => {
 
-             const { data: { text } } = params
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.resultText += text
 
-             }))
 
-           },
 
-           onTextReplace: (params) => {
 
-             const { data: { text } } = params
 
-             setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => {
 
-               draft.resultText = text
 
-             }))
 
-           },
 
-         },
 
-         isInstalledApp,
 
-         installedAppInfo?.id,
 
-       )
 
-     }
 
-     else {
 
-       sendCompletionMessage(data, {
 
-         onData: (data: string, _isFirstMessage: boolean, { messageId }) => {
 
-           tempMessageId = messageId
 
-           res.push(data)
 
-           setCompletionRes(res.join(''))
 
-         },
 
-         onCompleted: () => {
 
-           if (isTimeout)
 
-             return
 
-           setRespondingFalse()
 
-           setMessageId(tempMessageId)
 
-           onCompleted(getCompletionRes(), taskId, true)
 
-           isEnd = true
 
-         },
 
-         onMessageReplace: (messageReplace) => {
 
-           res = [messageReplace.answer]
 
-           setCompletionRes(res.join(''))
 
-         },
 
-         onError() {
 
-           if (isTimeout)
 
-             return
 
-           setRespondingFalse()
 
-           onCompleted(getCompletionRes(), taskId, false)
 
-           isEnd = true
 
-         },
 
-       }, isInstalledApp, installedAppInfo?.id)
 
-     }
 
-   }
 
-   const [controlClearMoreLikeThis, setControlClearMoreLikeThis] = useState(0)
 
-   useEffect(() => {
 
-     if (controlSend) {
 
-       handleSend()
 
-       setControlClearMoreLikeThis(Date.now())
 
-     }
 
-   }, [controlSend])
 
-   useEffect(() => {
 
-     if (controlRetry)
 
-       handleSend()
 
-   }, [controlRetry])
 
-   const renderTextGenerationRes = () => (
 
-     <TextGenerationRes
 
-       isWorkflow={isWorkflow}
 
-       workflowProcessData={workflowProcessData}
 
-       className='mt-3'
 
-       isError={isError}
 
-       onRetry={handleSend}
 
-       content={completionRes}
 
-       messageId={messageId}
 
-       isInWebApp
 
-       moreLikeThis={moreLikeThisEnabled}
 
-       onFeedback={handleFeedback}
 
-       feedback={feedback}
 
-       onSave={handleSaveMessage}
 
-       isMobile={isMobile}
 
-       isInstalledApp={isInstalledApp}
 
-       installedAppId={installedAppInfo?.id}
 
-       isLoading={isCallBatchAPI ? (!completionRes && isResponding) : false}
 
-       taskId={isCallBatchAPI ? ((taskId as number) < 10 ? `0${taskId}` : `${taskId}`) : undefined}
 
-       controlClearMoreLikeThis={controlClearMoreLikeThis}
 
-       isShowTextToSpeech={isShowTextToSpeech}
 
-       hideProcessDetail
 
-     />
 
-   )
 
-   return (
 
-     <div className={cn(isNoData && !isCallBatchAPI && 'h-full')}>
 
-       {!isCallBatchAPI && !isWorkflow && (
 
-         (isResponding && !completionRes)
 
-           ? (
 
-             <div className='flex h-full w-full justify-center items-center'>
 
-               <Loading type='area' />
 
-             </div>)
 
-           : (
 
-             <>
 
-               {(isNoData)
 
-                 ? <NoData />
 
-                 : renderTextGenerationRes()
 
-               }
 
-             </>
 
-           )
 
-       )}
 
-       {
 
-         !isCallBatchAPI && isWorkflow && (
 
-           (isResponding && !workflowProcessData)
 
-             ? (
 
-               <div className='flex h-full w-full justify-center items-center'>
 
-                 <Loading type='area' />
 
-               </div>
 
-             )
 
-             : !workflowProcessData
 
-               ? <NoData />
 
-               : renderTextGenerationRes()
 
-         )
 
-       }
 
-       {isCallBatchAPI && (
 
-         <div className='mt-2'>
 
-           {renderTextGenerationRes()}
 
-         </div>
 
-       )}
 
-     </div>
 
-   )
 
- }
 
- export default React.memo(Result)
 
 
  |