選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

chat-wrapper.tsx 5.4KB

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