chat-wrapper.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import {
  2. forwardRef,
  3. memo,
  4. useCallback,
  5. useEffect,
  6. useImperativeHandle,
  7. useMemo,
  8. } from 'react'
  9. import { useNodes } from 'reactflow'
  10. import { BlockEnum } from '../../types'
  11. import {
  12. useStore,
  13. useWorkflowStore,
  14. } from '../../store'
  15. import type { StartNodeType } from '../../nodes/start/types'
  16. import Empty from './empty'
  17. import UserInput from './user-input'
  18. import ConversationVariableModal from './conversation-variable-modal'
  19. import { useChat } from './hooks'
  20. import type { ChatWrapperRefType } from './index'
  21. import Chat from '@/app/components/base/chat/chat'
  22. import type { ChatItem, OnSend } from '@/app/components/base/chat/types'
  23. import { useFeatures } from '@/app/components/base/features/hooks'
  24. import {
  25. fetchSuggestedQuestions,
  26. stopChatMessageResponding,
  27. } from '@/service/debug'
  28. import { useStore as useAppStore } from '@/app/components/app/store'
  29. import { getLastAnswer } from '@/app/components/base/chat/utils'
  30. type ChatWrapperProps = {
  31. showConversationVariableModal: boolean
  32. onConversationModalHide: () => void
  33. showInputsFieldsPanel: boolean
  34. onHide: () => void
  35. }
  36. const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
  37. showConversationVariableModal,
  38. onConversationModalHide,
  39. showInputsFieldsPanel,
  40. onHide,
  41. }, ref) => {
  42. const nodes = useNodes<StartNodeType>()
  43. const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
  44. const startVariables = startNode?.data.variables
  45. const appDetail = useAppStore(s => s.appDetail)
  46. const workflowStore = useWorkflowStore()
  47. const inputs = useStore(s => s.inputs)
  48. const features = useFeatures(s => s.features)
  49. const config = useMemo(() => {
  50. return {
  51. opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '',
  52. suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [],
  53. suggested_questions_after_answer: features.suggested,
  54. text_to_speech: features.text2speech,
  55. speech_to_text: features.speech2text,
  56. retriever_resource: features.citation,
  57. sensitive_word_avoidance: features.moderation,
  58. file_upload: features.file,
  59. }
  60. }, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
  61. const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
  62. const {
  63. conversationId,
  64. chatList,
  65. chatListRef,
  66. handleUpdateChatList,
  67. handleStop,
  68. isResponding,
  69. suggestedQuestions,
  70. handleSend,
  71. handleRestart,
  72. } = useChat(
  73. config,
  74. {
  75. inputs,
  76. inputsForm: (startVariables || []) as any,
  77. },
  78. [],
  79. taskId => stopChatMessageResponding(appDetail!.id, taskId),
  80. )
  81. const doSend = useCallback<OnSend>((query, files, last_answer) => {
  82. handleSend(
  83. {
  84. query,
  85. files,
  86. inputs: workflowStore.getState().inputs,
  87. conversation_id: conversationId,
  88. parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
  89. },
  90. {
  91. onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
  92. },
  93. )
  94. }, [chatListRef, conversationId, handleSend, workflowStore, appDetail])
  95. const doRegenerate = useCallback((chatItem: ChatItem) => {
  96. const index = chatList.findIndex(item => item.id === chatItem.id)
  97. if (index === -1)
  98. return
  99. const prevMessages = chatList.slice(0, index)
  100. const question = prevMessages.pop()
  101. const lastAnswer = getLastAnswer(prevMessages)
  102. if (!question)
  103. return
  104. handleUpdateChatList(prevMessages)
  105. doSend(question.content, question.message_files, lastAnswer)
  106. }, [chatList, handleUpdateChatList, doSend])
  107. useImperativeHandle(ref, () => {
  108. return {
  109. handleRestart,
  110. }
  111. }, [handleRestart])
  112. useEffect(() => {
  113. if (isResponding)
  114. onHide()
  115. }, [isResponding, onHide])
  116. return (
  117. <>
  118. <Chat
  119. config={{
  120. ...config,
  121. supportCitationHitInfo: true,
  122. } as any}
  123. chatList={chatList}
  124. isResponding={isResponding}
  125. chatContainerClassName='px-3'
  126. chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto'
  127. chatFooterClassName='px-4 rounded-bl-2xl'
  128. chatFooterInnerClassName='pb-0'
  129. showFileUpload
  130. showFeatureBar
  131. onFeatureBarClick={setShowFeaturesPanel}
  132. onSend={doSend}
  133. inputs={inputs}
  134. inputsForm={(startVariables || []) as any}
  135. onRegenerate={doRegenerate}
  136. onStopResponding={handleStop}
  137. chatNode={(
  138. <>
  139. {showInputsFieldsPanel && <UserInput />}
  140. {
  141. !chatList.length && (
  142. <Empty />
  143. )
  144. }
  145. </>
  146. )}
  147. noSpacing
  148. suggestedQuestions={suggestedQuestions}
  149. showPromptLog
  150. chatAnswerContainerInner='!pr-2'
  151. />
  152. {showConversationVariableModal && (
  153. <ConversationVariableModal
  154. conversationID={conversationId}
  155. onHide={onConversationModalHide}
  156. />
  157. )}
  158. </>
  159. )
  160. })
  161. ChatWrapper.displayName = 'ChatWrapper'
  162. export default memo(ChatWrapper)