123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 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)
|