chat-wrapper.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { useCallback, useEffect, useMemo } from 'react'
  2. import Chat from '../chat'
  3. import type {
  4. ChatConfig,
  5. ChatItem,
  6. ChatItemInTree,
  7. OnSend,
  8. } from '../types'
  9. import { useChat } from '../chat/hooks'
  10. import { getLastAnswer, isValidGeneratedAnswer } from '../utils'
  11. import { useChatWithHistoryContext } from './context'
  12. import Header from './header'
  13. import ConfigPanel from './config-panel'
  14. import {
  15. fetchSuggestedQuestions,
  16. getUrl,
  17. stopChatMessageResponding,
  18. } from '@/service/share'
  19. import AnswerIcon from '@/app/components/base/answer-icon'
  20. const ChatWrapper = () => {
  21. const {
  22. appParams,
  23. appPrevChatTree,
  24. currentConversationId,
  25. currentConversationItem,
  26. inputsForms,
  27. newConversationInputs,
  28. handleNewConversationCompleted,
  29. isMobile,
  30. isInstalledApp,
  31. appId,
  32. appMeta,
  33. handleFeedback,
  34. currentChatInstanceRef,
  35. appData,
  36. themeBuilder,
  37. } = useChatWithHistoryContext()
  38. const appConfig = useMemo(() => {
  39. const config = appParams || {}
  40. return {
  41. ...config,
  42. file_upload: {
  43. ...(config as any).file_upload,
  44. fileUploadConfig: (config as any).system_parameters,
  45. },
  46. supportFeedback: true,
  47. opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
  48. } as ChatConfig
  49. }, [appParams, currentConversationItem?.introduction, currentConversationId])
  50. const {
  51. chatList,
  52. setTargetMessageId,
  53. handleSend,
  54. handleStop,
  55. isResponding,
  56. suggestedQuestions,
  57. } = useChat(
  58. appConfig,
  59. {
  60. inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
  61. inputsForm: inputsForms,
  62. },
  63. appPrevChatTree,
  64. taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
  65. )
  66. useEffect(() => {
  67. if (currentChatInstanceRef.current)
  68. currentChatInstanceRef.current.handleStop = handleStop
  69. // eslint-disable-next-line react-hooks/exhaustive-deps
  70. }, [])
  71. const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => {
  72. const data: any = {
  73. query: message,
  74. files,
  75. inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
  76. conversation_id: currentConversationId,
  77. parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null,
  78. }
  79. handleSend(
  80. getUrl('chat-messages', isInstalledApp, appId || ''),
  81. data,
  82. {
  83. onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
  84. onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
  85. isPublicAPI: !isInstalledApp,
  86. },
  87. )
  88. }, [
  89. chatList,
  90. handleNewConversationCompleted,
  91. handleSend,
  92. currentConversationId,
  93. currentConversationItem,
  94. newConversationInputs,
  95. isInstalledApp,
  96. appId,
  97. ])
  98. const doRegenerate = useCallback((chatItem: ChatItemInTree) => {
  99. const question = chatList.find(item => item.id === chatItem.parentMessageId)!
  100. const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
  101. doSend(question.content, question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null)
  102. }, [chatList, doSend])
  103. const chatNode = useMemo(() => {
  104. if (inputsForms.length) {
  105. return (
  106. <>
  107. <Header
  108. isMobile={isMobile}
  109. title={currentConversationItem?.name || ''}
  110. />
  111. {
  112. !currentConversationId && (
  113. <div className={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}>
  114. <div className='mb-6' />
  115. <ConfigPanel />
  116. <div
  117. className='my-6 h-[1px]'
  118. style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}
  119. />
  120. </div>
  121. )
  122. }
  123. </>
  124. )
  125. }
  126. return (
  127. <Header
  128. isMobile={isMobile}
  129. title={currentConversationItem?.name || ''}
  130. />
  131. )
  132. }, [
  133. currentConversationId,
  134. inputsForms,
  135. currentConversationItem,
  136. isMobile,
  137. ])
  138. const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
  139. ? <AnswerIcon
  140. iconType={appData.site.icon_type}
  141. icon={appData.site.icon}
  142. background={appData.site.icon_background}
  143. imageUrl={appData.site.icon_url}
  144. />
  145. : null
  146. return (
  147. <div
  148. className='h-full bg-chatbot-bg overflow-hidden'
  149. >
  150. <Chat
  151. appData={appData}
  152. config={appConfig}
  153. chatList={chatList}
  154. isResponding={isResponding}
  155. chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
  156. chatFooterClassName='pb-4'
  157. chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
  158. onSend={doSend}
  159. inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
  160. inputsForm={inputsForms}
  161. onRegenerate={doRegenerate}
  162. onStopResponding={handleStop}
  163. chatNode={chatNode}
  164. allToolIcons={appMeta?.tool_icons || {}}
  165. onFeedback={handleFeedback}
  166. suggestedQuestions={suggestedQuestions}
  167. answerIcon={answerIcon}
  168. hideProcessDetail
  169. themeBuilder={themeBuilder}
  170. switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)}
  171. />
  172. </div>
  173. )
  174. }
  175. export default ChatWrapper