chat-wrapper.tsx 3.9 KB

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