Severian's picture
initial commit
a8b3f00
raw
history blame
5.27 kB
import {
forwardRef,
memo,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
} from 'react'
import { useNodes } from 'reactflow'
import { BlockEnum } from '../../types'
import {
useStore,
useWorkflowStore,
} from '../../store'
import type { StartNodeType } from '../../nodes/start/types'
import Empty from './empty'
import UserInput from './user-input'
import ConversationVariableModal from './conversation-variable-modal'
import { useChat } from './hooks'
import type { ChatWrapperRefType } from './index'
import Chat from '@/app/components/base/chat/chat'
import type { ChatItem, OnSend } from '@/app/components/base/chat/types'
import { useFeatures } from '@/app/components/base/features/hooks'
import {
fetchSuggestedQuestions,
stopChatMessageResponding,
} from '@/service/debug'
import { useStore as useAppStore } from '@/app/components/app/store'
import { getLastAnswer } from '@/app/components/base/chat/utils'
type ChatWrapperProps = {
showConversationVariableModal: boolean
onConversationModalHide: () => void
showInputsFieldsPanel: boolean
onHide: () => void
}
const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
showConversationVariableModal,
onConversationModalHide,
showInputsFieldsPanel,
onHide,
}, ref) => {
const nodes = useNodes<StartNodeType>()
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
const startVariables = startNode?.data.variables
const appDetail = useAppStore(s => s.appDetail)
const workflowStore = useWorkflowStore()
const inputs = useStore(s => s.inputs)
const features = useFeatures(s => s.features)
const config = useMemo(() => {
return {
opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '',
suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [],
suggested_questions_after_answer: features.suggested,
text_to_speech: features.text2speech,
speech_to_text: features.speech2text,
retriever_resource: features.citation,
sensitive_word_avoidance: features.moderation,
file_upload: features.file,
}
}, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
const {
conversationId,
chatList,
chatListRef,
handleUpdateChatList,
handleStop,
isResponding,
suggestedQuestions,
handleSend,
handleRestart,
} = useChat(
config,
{
inputs,
inputsForm: (startVariables || []) as any,
},
[],
taskId => stopChatMessageResponding(appDetail!.id, taskId),
)
const doSend = useCallback<OnSend>((query, files, last_answer) => {
handleSend(
{
query,
files,
inputs: workflowStore.getState().inputs,
conversation_id: conversationId,
parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
},
{
onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
},
)
}, [chatListRef, conversationId, handleSend, workflowStore, appDetail])
const doRegenerate = useCallback((chatItem: ChatItem) => {
const index = chatList.findIndex(item => item.id === chatItem.id)
if (index === -1)
return
const prevMessages = chatList.slice(0, index)
const question = prevMessages.pop()
const lastAnswer = getLastAnswer(prevMessages)
if (!question)
return
handleUpdateChatList(prevMessages)
doSend(question.content, question.message_files, lastAnswer)
}, [chatList, handleUpdateChatList, doSend])
useImperativeHandle(ref, () => {
return {
handleRestart,
}
}, [handleRestart])
useEffect(() => {
if (isResponding)
onHide()
}, [isResponding, onHide])
return (
<>
<Chat
config={{
...config,
supportCitationHitInfo: true,
} as any}
chatList={chatList}
isResponding={isResponding}
chatContainerClassName='px-3'
chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto'
chatFooterClassName='px-4 rounded-bl-2xl'
chatFooterInnerClassName='pb-0'
showFileUpload
showFeatureBar
onFeatureBarClick={setShowFeaturesPanel}
onSend={doSend}
inputs={inputs}
inputsForm={(startVariables || []) as any}
onRegenerate={doRegenerate}
onStopResponding={handleStop}
chatNode={(
<>
{showInputsFieldsPanel && <UserInput />}
{
!chatList.length && (
<Empty />
)
}
</>
)}
noSpacing
suggestedQuestions={suggestedQuestions}
showPromptLog
chatAnswerContainerInner='!pr-2'
/>
{showConversationVariableModal && (
<ConversationVariableModal
conversationID={conversationId}
onHide={onConversationModalHide}
/>
)}
</>
)
})
ChatWrapper.displayName = 'ChatWrapper'
export default memo(ChatWrapper)