chat-wrapper.tsx 4.7 KB

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