Spaces:
Paused
Paused
| 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) | |