Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

chat-wrapper.tsx 5.8KB

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