| | 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) { |
| | |
| | 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 }))), |
| | }) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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 || '') |
| |
|
| | |
| | childrenCount[parentMessageId] = (childrenCount[parentMessageId] || 0) + 1 |
| | const questionNode: ChatItemInTree = { |
| | ...question, |
| | children: [], |
| | } |
| | map[question.id] = questionNode |
| |
|
| | |
| | childrenCount[question.id] = 1 |
| | const answerNode: ChatItemInTree = { |
| | ...answer, |
| | children: [], |
| | siblingIndex: isLegacy ? 0 : childrenCount[parentMessageId] - 1, |
| | } |
| | map[answer.id] = answerNode |
| |
|
| | |
| | questionNode.children!.push(answerNode) |
| |
|
| | |
| | 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 (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 |
| |
|
| | |
| | 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) |
| | ) { |
| | 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]], |
| | }) |
| | } |
| | } |
| | } |
| |
|
| | |
| | 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, |
| | } |
| |
|