| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 | 
							- import { addFileInfos, sortAgentSorts } from '../../tools/utils'
 
- import { UUID_NIL } from './constants'
 
- import type { IChatItem } from './chat/type'
 
- import type { ChatItem, ChatItemInTree } from './types'
 
- import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
 
- async function decodeBase64AndDecompress(base64String: string) {
 
-   const binaryString = atob(base64String)
 
-   const compressedUint8Array = Uint8Array.from(binaryString, char => char.charCodeAt(0))
 
-   const decompressedStream = new Response(compressedUint8Array).body?.pipeThrough(new DecompressionStream('gzip'))
 
-   const decompressedArrayBuffer = await new Response(decompressedStream).arrayBuffer()
 
-   return new TextDecoder().decode(decompressedArrayBuffer)
 
- }
 
- function getProcessedInputsFromUrlParams(): Record<string, any> {
 
-   const urlParams = new URLSearchParams(window.location.search)
 
-   const inputs: Record<string, any> = {}
 
-   urlParams.forEach(async (value, key) => {
 
-     inputs[key] = await decodeBase64AndDecompress(decodeURIComponent(value))
 
-   })
 
-   return inputs
 
- }
 
- function getLastAnswer(chatList: ChatItem[]) {
 
-   for (let i = chatList.length - 1; i >= 0; i--) {
 
-     const item = chatList[i]
 
-     if (item.isAnswer && !item.id.startsWith('answer-placeholder-') && !item.isOpeningStatement)
 
-       return item
 
-   }
 
-   return null
 
- }
 
- function appendQAToChatList(chatList: ChatItem[], item: any) {
 
-   // we append answer first and then question since will reverse the whole chatList later
 
-   const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
 
-   chatList.push({
 
-     id: item.id,
 
-     content: item.answer,
 
-     agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files),
 
-     feedback: item.feedback,
 
-     isAnswer: true,
 
-     citation: item.retriever_resources,
 
-     message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
 
-   })
 
-   const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
 
-   chatList.push({
 
-     id: `question-${item.id}`,
 
-     content: item.query,
 
-     isAnswer: false,
 
-     message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
 
-   })
 
- }
 
- /**
 
-  * Computes the latest thread messages from all messages of the conversation.
 
-  * Same logic as backend codebase `api/core/prompt/utils/extract_thread_messages.py`
 
-  *
 
-  * @param fetchedMessages - The history chat list data from the backend, sorted by created_at in descending order. This includes all flattened history messages of the conversation.
 
-  * @returns An array of ChatItems representing the latest thread.
 
-  */
 
- function getPrevChatList(fetchedMessages: any[]) {
 
-   const ret: ChatItem[] = []
 
-   let nextMessageId = null
 
-   for (const item of fetchedMessages) {
 
-     if (!item.parent_message_id) {
 
-       appendQAToChatList(ret, item)
 
-       break
 
-     }
 
-     if (!nextMessageId) {
 
-       appendQAToChatList(ret, item)
 
-       nextMessageId = item.parent_message_id
 
-     }
 
-     else {
 
-       if (item.id === nextMessageId || nextMessageId === UUID_NIL) {
 
-         appendQAToChatList(ret, item)
 
-         nextMessageId = item.parent_message_id
 
-       }
 
-     }
 
-   }
 
-   return ret.reverse()
 
- }
 
- function buildChatItemTree(allMessages: IChatItem[]): ChatItemInTree[] {
 
-   const map: Record<string, ChatItemInTree> = {}
 
-   const rootNodes: ChatItemInTree[] = []
 
-   const childrenCount: Record<string, number> = {}
 
-   let lastAppendedLegacyAnswer: ChatItemInTree | null = null
 
-   for (let i = 0; i < allMessages.length; i += 2) {
 
-     const question = allMessages[i]!
 
-     const answer = allMessages[i + 1]!
 
-     const isLegacy = question.parentMessageId === UUID_NIL
 
-     const parentMessageId = isLegacy
 
-       ? (lastAppendedLegacyAnswer?.id || '')
 
-       : (question.parentMessageId || '')
 
-     // Process question
 
-     childrenCount[parentMessageId] = (childrenCount[parentMessageId] || 0) + 1
 
-     const questionNode: ChatItemInTree = {
 
-       ...question,
 
-       children: [],
 
-     }
 
-     map[question.id] = questionNode
 
-     // Process answer
 
-     childrenCount[question.id] = 1
 
-     const answerNode: ChatItemInTree = {
 
-       ...answer,
 
-       children: [],
 
-       siblingIndex: isLegacy ? 0 : childrenCount[parentMessageId] - 1,
 
-     }
 
-     map[answer.id] = answerNode
 
-     // Connect question and answer
 
-     questionNode.children!.push(answerNode)
 
-     // Append to parent or add to root
 
-     if (isLegacy) {
 
-       if (!lastAppendedLegacyAnswer)
 
-         rootNodes.push(questionNode)
 
-       else
 
-         lastAppendedLegacyAnswer.children!.push(questionNode)
 
-       lastAppendedLegacyAnswer = answerNode
 
-     }
 
-     else {
 
-       if (!parentMessageId)
 
-         rootNodes.push(questionNode)
 
-       else
 
-         map[parentMessageId]?.children!.push(questionNode)
 
-     }
 
-   }
 
-   // If no messages have parentMessageId=null (indicating a root node),
 
-   // then we likely have a partial chat history. In this case,
 
-   // use the first available message as the root node.
 
-   if (rootNodes.length === 0 && allMessages.length > 0)
 
-     rootNodes.push(map[allMessages[0]!.id]!)
 
-   return rootNodes
 
- }
 
- function getThreadMessages(tree: ChatItemInTree[], targetMessageId?: string): ChatItemInTree[] {
 
-   let ret: ChatItemInTree[] = []
 
-   let targetNode: ChatItemInTree | undefined
 
-   // find path to the target message
 
-   const stack = tree.toReversed().map(rootNode => ({
 
-     node: rootNode,
 
-     path: [rootNode],
 
-   }))
 
-   while (stack.length > 0) {
 
-     const { node, path } = stack.pop()!
 
-     if (
 
-       node.id === targetMessageId
 
-       || (!targetMessageId && !node.children?.length && !stack.length) // if targetMessageId is not provided, we use the last message in the tree as the target
 
-     ) {
 
-       targetNode = node
 
-       ret = path.map((item, index) => {
 
-         if (!item.isAnswer)
 
-           return item
 
-         const parentAnswer = path[index - 2]
 
-         const siblingCount = !parentAnswer ? tree.length : parentAnswer.children!.length
 
-         const prevSibling = !parentAnswer ? tree[item.siblingIndex! - 1]?.children?.[0]?.id : parentAnswer.children![item.siblingIndex! - 1]?.children?.[0].id
 
-         const nextSibling = !parentAnswer ? tree[item.siblingIndex! + 1]?.children?.[0]?.id : parentAnswer.children![item.siblingIndex! + 1]?.children?.[0].id
 
-         return { ...item, siblingCount, prevSibling, nextSibling }
 
-       })
 
-       break
 
-     }
 
-     if (node.children) {
 
-       for (let i = node.children.length - 1; i >= 0; i--) {
 
-         stack.push({
 
-           node: node.children[i],
 
-           path: [...path, node.children[i]],
 
-         })
 
-       }
 
-     }
 
-   }
 
-   // append all descendant messages to the path
 
-   if (targetNode) {
 
-     const stack = [targetNode]
 
-     while (stack.length > 0) {
 
-       const node = stack.pop()!
 
-       if (node !== targetNode)
 
-         ret.push(node)
 
-       if (node.children?.length) {
 
-         const lastChild = node.children.at(-1)!
 
-         if (!lastChild.isAnswer) {
 
-           stack.push(lastChild)
 
-           continue
 
-         }
 
-         const parentAnswer = ret.at(-2)
 
-         const siblingCount = parentAnswer?.children?.length
 
-         const prevSibling = parentAnswer?.children?.at(-2)?.children?.[0]?.id
 
-         stack.push({ ...lastChild, siblingCount, prevSibling })
 
-       }
 
-     }
 
-   }
 
-   return ret
 
- }
 
- export {
 
-   getProcessedInputsFromUrlParams,
 
-   getPrevChatList,
 
-   getLastAnswer,
 
-   buildChatItemTree,
 
-   getThreadMessages,
 
- }
 
 
  |