chat-wrapper.tsx 4.6 KB

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