chat-wrapper.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { useCallback, useEffect, useMemo } from 'react'
  2. import Chat from '../chat'
  3. import type {
  4. ChatConfig,
  5. OnSend,
  6. } from '../types'
  7. import { useChat } from '../chat/hooks'
  8. import { useEmbeddedChatbotContext } from './context'
  9. import ConfigPanel from './config-panel'
  10. import { isDify } from './utils'
  11. import cn from '@/utils/classnames'
  12. import {
  13. fetchSuggestedQuestions,
  14. getUrl,
  15. stopChatMessageResponding,
  16. } from '@/service/share'
  17. import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar'
  18. const ChatWrapper = () => {
  19. const {
  20. appData,
  21. appParams,
  22. appPrevChatList,
  23. currentConversationId,
  24. currentConversationItem,
  25. inputsForms,
  26. newConversationInputs,
  27. handleNewConversationCompleted,
  28. isMobile,
  29. isInstalledApp,
  30. appId,
  31. appMeta,
  32. handleFeedback,
  33. currentChatInstanceRef,
  34. themeBuilder,
  35. } = useEmbeddedChatbotContext()
  36. const appConfig = useMemo(() => {
  37. const config = appParams || {}
  38. return {
  39. ...config,
  40. supportFeedback: true,
  41. opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
  42. } as ChatConfig
  43. }, [appParams, currentConversationItem?.introduction, currentConversationId])
  44. const {
  45. chatList,
  46. handleSend,
  47. handleStop,
  48. isResponding,
  49. suggestedQuestions,
  50. } = useChat(
  51. appConfig,
  52. {
  53. inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
  54. promptVariables: inputsForms,
  55. },
  56. appPrevChatList,
  57. taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
  58. )
  59. useEffect(() => {
  60. if (currentChatInstanceRef.current)
  61. currentChatInstanceRef.current.handleStop = handleStop
  62. }, [])
  63. const doSend: OnSend = useCallback((message, files) => {
  64. const data: any = {
  65. query: message,
  66. inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
  67. conversation_id: currentConversationId,
  68. }
  69. if (appConfig?.file_upload?.image.enabled && files?.length)
  70. data.files = files
  71. handleSend(
  72. getUrl('chat-messages', isInstalledApp, appId || ''),
  73. data,
  74. {
  75. onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
  76. onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
  77. isPublicAPI: !isInstalledApp,
  78. },
  79. )
  80. }, [
  81. appConfig,
  82. currentConversationId,
  83. currentConversationItem,
  84. handleSend,
  85. newConversationInputs,
  86. handleNewConversationCompleted,
  87. isInstalledApp,
  88. appId,
  89. ])
  90. const chatNode = useMemo(() => {
  91. if (inputsForms.length) {
  92. return (
  93. <>
  94. {!currentConversationId && (
  95. <div className={cn('mx-auto w-full max-w-[720px] tablet:px-4', isMobile && 'px-4')}>
  96. <div className='mb-6' />
  97. <ConfigPanel />
  98. <div
  99. className='my-6 h-[1px]'
  100. style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}
  101. />
  102. </div>
  103. )}
  104. </>
  105. )
  106. }
  107. return null
  108. }, [currentConversationId, inputsForms, isMobile])
  109. return (
  110. <Chat
  111. appData={appData}
  112. config={appConfig}
  113. chatList={chatList}
  114. isResponding={isResponding}
  115. chatContainerInnerClassName={cn('mx-auto w-full max-w-[720px] tablet:px-4', isMobile && 'px-4')}
  116. chatFooterClassName='pb-4'
  117. chatFooterInnerClassName={cn('mx-auto w-full max-w-[720px] tablet:px-4', isMobile && 'px-4')}
  118. onSend={doSend}
  119. onStopResponding={handleStop}
  120. chatNode={chatNode}
  121. allToolIcons={appMeta?.tool_icons || {}}
  122. onFeedback={handleFeedback}
  123. suggestedQuestions={suggestedQuestions}
  124. answerIcon={isDify() ? <LogoAvatar className='relative shrink-0' /> : null}
  125. hideProcessDetail
  126. themeBuilder={themeBuilder}
  127. />
  128. )
  129. }
  130. export default ChatWrapper