mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-05-14 23:38:17 +08:00
206 lines
5.6 KiB
TypeScript
206 lines
5.6 KiB
TypeScript
'use client'
|
|
import type { FC } from 'react'
|
|
import React, { useEffect, useState } from 'react'
|
|
import { useBoolean, useGetState } from 'ahooks'
|
|
import { t } from 'i18next'
|
|
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, updateFeedback } from '@/service/share'
|
|
import type { Feedbacktype } from '@/app/components/app/chat'
|
|
import Loading from '@/app/components/base/loading'
|
|
import type { PromptConfig } from '@/models/debug'
|
|
import type { InstalledApp } from '@/models/explore'
|
|
export type IResultProps = {
|
|
isCallBatchAPI: boolean
|
|
isPC: boolean
|
|
isMobile: boolean
|
|
isInstalledApp: boolean
|
|
installedAppInfo?: InstalledApp
|
|
promptConfig: PromptConfig | null
|
|
moreLikeThisEnabled: boolean
|
|
inputs: Record<string, any>
|
|
query: string
|
|
controlSend?: number
|
|
controlStopResponding?: number
|
|
onShowRes: () => void
|
|
handleSaveMessage: (messageId: string) => void
|
|
taskId?: number
|
|
onCompleted: (completionRes: string, taskId?: number, success?: boolean) => void
|
|
}
|
|
|
|
const Result: FC<IResultProps> = ({
|
|
isCallBatchAPI,
|
|
isPC,
|
|
isMobile,
|
|
isInstalledApp,
|
|
installedAppInfo,
|
|
promptConfig,
|
|
moreLikeThisEnabled,
|
|
inputs,
|
|
query,
|
|
controlSend,
|
|
controlStopResponding,
|
|
onShowRes,
|
|
handleSaveMessage,
|
|
taskId,
|
|
onCompleted,
|
|
}) => {
|
|
const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
|
|
useEffect(() => {
|
|
if (controlStopResponding)
|
|
setResponsingFalse()
|
|
}, [controlStopResponding])
|
|
|
|
const [completionRes, setCompletionRes, getCompletionRes] = useGetState('')
|
|
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)
|
|
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
|
|
}
|
|
return !hasEmptyInput
|
|
}
|
|
|
|
const handleSend = async () => {
|
|
if (isResponsing) {
|
|
notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
|
|
return false
|
|
}
|
|
|
|
if (!checkCanSend())
|
|
return
|
|
|
|
if (!query) {
|
|
logError(t('appDebug.errorMessage.queryRequired'))
|
|
return false
|
|
}
|
|
|
|
const data = {
|
|
inputs,
|
|
query,
|
|
}
|
|
|
|
setMessageId(null)
|
|
setFeedback({
|
|
rating: null,
|
|
})
|
|
setCompletionRes('')
|
|
|
|
const res: string[] = []
|
|
let tempMessageId = ''
|
|
|
|
if (!isPC)
|
|
onShowRes()
|
|
|
|
setResponsingTrue()
|
|
sendCompletionMessage(data, {
|
|
onData: (data: string, _isFirstMessage: boolean, { messageId }: any) => {
|
|
tempMessageId = messageId
|
|
res.push(data)
|
|
setCompletionRes(res.join(''))
|
|
},
|
|
onCompleted: () => {
|
|
setResponsingFalse()
|
|
setMessageId(tempMessageId)
|
|
onCompleted(getCompletionRes(), taskId, true)
|
|
},
|
|
onError() {
|
|
setResponsingFalse()
|
|
onCompleted(getCompletionRes(), taskId, false)
|
|
},
|
|
}, isInstalledApp, installedAppInfo?.id)
|
|
}
|
|
|
|
const [controlClearMoreLikeThis, setControlClearMoreLikeThis] = useState(0)
|
|
useEffect(() => {
|
|
if (controlSend) {
|
|
handleSend()
|
|
setControlClearMoreLikeThis(Date.now())
|
|
}
|
|
}, [controlSend])
|
|
|
|
const renderTextGenerationRes = () => (
|
|
<TextGenerationRes
|
|
className='mt-3'
|
|
content={completionRes}
|
|
messageId={messageId}
|
|
isInWebApp
|
|
moreLikeThis={moreLikeThisEnabled}
|
|
onFeedback={handleFeedback}
|
|
feedback={feedback}
|
|
onSave={handleSaveMessage}
|
|
isMobile={isMobile}
|
|
isInstalledApp={isInstalledApp}
|
|
installedAppId={installedAppInfo?.id}
|
|
isLoading={isCallBatchAPI ? (!completionRes && isResponsing) : false}
|
|
taskId={isCallBatchAPI ? ((taskId as number) < 10 ? `0${taskId}` : `${taskId}`) : undefined}
|
|
controlClearMoreLikeThis={controlClearMoreLikeThis}
|
|
/>
|
|
)
|
|
|
|
return (
|
|
<div className={cn(isNoData && !isCallBatchAPI && 'h-full')}>
|
|
{!isCallBatchAPI && (
|
|
(isResponsing && !completionRes)
|
|
? (
|
|
<div className='flex h-full w-full justify-center items-center'>
|
|
<Loading type='area' />
|
|
</div>)
|
|
: (
|
|
<>
|
|
{isNoData
|
|
? <NoData />
|
|
: renderTextGenerationRes()
|
|
}
|
|
</>
|
|
)
|
|
)}
|
|
{isCallBatchAPI && (
|
|
<div className='mt-2'>
|
|
{renderTextGenerationRes()}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
export default React.memo(Result)
|