index.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {
  2. memo,
  3. useCallback,
  4. useEffect,
  5. useState,
  6. } from 'react'
  7. import { RiCloseLine } from '@remixicon/react'
  8. import {
  9. useStore,
  10. useWorkflowStore,
  11. } from '../../store'
  12. import { useWorkflowRun } from '../../hooks'
  13. import UserInput from './user-input'
  14. import Chat from '@/app/components/base/chat/chat'
  15. import type { ChatItem, ChatItemInTree } from '@/app/components/base/chat/types'
  16. import { fetchConversationMessages } from '@/service/debug'
  17. import { useStore as useAppStore } from '@/app/components/app/store'
  18. import Loading from '@/app/components/base/loading'
  19. import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
  20. import type { IChatItem } from '@/app/components/base/chat/chat/type'
  21. import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
  22. function getFormattedChatList(messages: any[]) {
  23. const res: ChatItem[] = []
  24. messages.forEach((item: any) => {
  25. const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
  26. res.push({
  27. id: `question-${item.id}`,
  28. content: item.query,
  29. isAnswer: false,
  30. message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
  31. parentMessageId: item.parent_message_id || undefined,
  32. })
  33. const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
  34. res.push({
  35. id: item.id,
  36. content: item.answer,
  37. feedback: item.feedback,
  38. isAnswer: true,
  39. citation: item.metadata?.retriever_resources,
  40. message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
  41. workflow_run_id: item.workflow_run_id,
  42. parentMessageId: `question-${item.id}`,
  43. })
  44. })
  45. return res
  46. }
  47. const ChatRecord = () => {
  48. const [fetched, setFetched] = useState(false)
  49. const [chatItemTree, setChatItemTree] = useState<ChatItemInTree[]>([])
  50. const [threadChatItems, setThreadChatItems] = useState<IChatItem[]>([])
  51. const appDetail = useAppStore(s => s.appDetail)
  52. const workflowStore = useWorkflowStore()
  53. const { handleLoadBackupDraft } = useWorkflowRun()
  54. const historyWorkflowData = useStore(s => s.historyWorkflowData)
  55. const currentConversationID = historyWorkflowData?.conversation_id
  56. const handleFetchConversationMessages = useCallback(async () => {
  57. if (appDetail && currentConversationID) {
  58. try {
  59. setFetched(false)
  60. const res = await fetchConversationMessages(appDetail.id, currentConversationID)
  61. const newAllChatItems = getFormattedChatList((res as any).data)
  62. const tree = buildChatItemTree(newAllChatItems)
  63. setChatItemTree(tree)
  64. setThreadChatItems(getThreadMessages(tree, newAllChatItems.at(-1)?.id))
  65. }
  66. catch (e) {
  67. }
  68. finally {
  69. setFetched(true)
  70. }
  71. }
  72. }, [appDetail, currentConversationID])
  73. useEffect(() => {
  74. handleFetchConversationMessages()
  75. }, [currentConversationID, appDetail, handleFetchConversationMessages])
  76. const switchSibling = useCallback((siblingMessageId: string) => {
  77. setThreadChatItems(getThreadMessages(chatItemTree, siblingMessageId))
  78. }, [chatItemTree])
  79. return (
  80. <div
  81. className={`
  82. flex flex-col w-[420px] rounded-l-2xl h-full border border-black/2 shadow-xl
  83. `}
  84. style={{
  85. background: 'linear-gradient(156deg, rgba(242, 244, 247, 0.80) 0%, rgba(242, 244, 247, 0.00) 99.43%), var(--white, #FFF)',
  86. }}
  87. >
  88. {!fetched && (
  89. <div className='flex items-center justify-center h-full'>
  90. <Loading />
  91. </div>
  92. )}
  93. {fetched && (
  94. <>
  95. <div className='shrink-0 flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
  96. {`TEST CHAT#${historyWorkflowData?.sequence_number}`}
  97. <div
  98. className='flex justify-center items-center w-6 h-6 cursor-pointer'
  99. onClick={() => {
  100. handleLoadBackupDraft()
  101. workflowStore.setState({ historyWorkflowData: undefined })
  102. }}
  103. >
  104. <RiCloseLine className='w-4 h-4 text-gray-500' />
  105. </div>
  106. </div>
  107. <div className='grow h-0'>
  108. <Chat
  109. config={{
  110. supportCitationHitInfo: true,
  111. } as any}
  112. chatList={threadChatItems}
  113. chatContainerClassName='px-3'
  114. chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto'
  115. chatFooterClassName='px-4 rounded-b-2xl'
  116. chatFooterInnerClassName='pb-4 w-full max-w-full mx-auto'
  117. chatNode={<UserInput />}
  118. noChatInput
  119. allToolIcons={{}}
  120. showPromptLog
  121. switchSibling={switchSibling}
  122. noSpacing
  123. chatAnswerContainerInner='!pr-2'
  124. />
  125. </div>
  126. </>
  127. )}
  128. </div>
  129. )
  130. }
  131. export default memo(ChatRecord)