Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { UUID_NIL } from './constants'
  2. import type { IChatItem } from './chat/type'
  3. import type { ChatItem, ChatItemInTree } from './types'
  4. async function decodeBase64AndDecompress(base64String: string) {
  5. try {
  6. const binaryString = atob(base64String)
  7. const compressedUint8Array = Uint8Array.from(binaryString, char => char.charCodeAt(0))
  8. const decompressedStream = new Response(compressedUint8Array).body?.pipeThrough(new DecompressionStream('gzip'))
  9. const decompressedArrayBuffer = await new Response(decompressedStream).arrayBuffer()
  10. return new TextDecoder().decode(decompressedArrayBuffer)
  11. }
  12. catch {
  13. return undefined
  14. }
  15. }
  16. async function getProcessedInputsFromUrlParams(): Promise<Record<string, any>> {
  17. const urlParams = new URLSearchParams(window.location.search)
  18. const inputs: Record<string, any> = {}
  19. const entriesArray = Array.from(urlParams.entries())
  20. await Promise.all(
  21. entriesArray.map(async ([key, value]) => {
  22. if (!key.startsWith('sys.'))
  23. inputs[key] = await decodeBase64AndDecompress(decodeURIComponent(value))
  24. }),
  25. )
  26. return inputs
  27. }
  28. async function getProcessedSystemVariablesFromUrlParams(): Promise<Record<string, any>> {
  29. const urlParams = new URLSearchParams(window.location.search)
  30. const systemVariables: Record<string, any> = {}
  31. const entriesArray = Array.from(urlParams.entries())
  32. await Promise.all(
  33. entriesArray.map(async ([key, value]) => {
  34. if (key.startsWith('sys.'))
  35. systemVariables[key.slice(4)] = await decodeBase64AndDecompress(decodeURIComponent(value))
  36. }),
  37. )
  38. return systemVariables
  39. }
  40. function isValidGeneratedAnswer(item?: ChatItem | ChatItemInTree): boolean {
  41. return !!item && item.isAnswer && !item.id.startsWith('answer-placeholder-') && !item.isOpeningStatement
  42. }
  43. function getLastAnswer<T extends ChatItem | ChatItemInTree>(chatList: T[]): T | null {
  44. for (let i = chatList.length - 1; i >= 0; i--) {
  45. const item = chatList[i]
  46. if (isValidGeneratedAnswer(item))
  47. return item
  48. }
  49. return null
  50. }
  51. /**
  52. * Build a chat item tree from a chat list
  53. * @param allMessages - The chat list, sorted from oldest to newest
  54. * @returns The chat item tree
  55. */
  56. function buildChatItemTree(allMessages: IChatItem[]): ChatItemInTree[] {
  57. const map: Record<string, ChatItemInTree> = {}
  58. const rootNodes: ChatItemInTree[] = []
  59. const childrenCount: Record<string, number> = {}
  60. let lastAppendedLegacyAnswer: ChatItemInTree | null = null
  61. for (let i = 0; i < allMessages.length; i += 2) {
  62. const question = allMessages[i]!
  63. const answer = allMessages[i + 1]!
  64. const isLegacy = question.parentMessageId === UUID_NIL
  65. const parentMessageId = isLegacy
  66. ? (lastAppendedLegacyAnswer?.id || '')
  67. : (question.parentMessageId || '')
  68. // Process question
  69. childrenCount[parentMessageId] = (childrenCount[parentMessageId] || 0) + 1
  70. const questionNode: ChatItemInTree = {
  71. ...question,
  72. children: [],
  73. }
  74. map[question.id] = questionNode
  75. // Process answer
  76. childrenCount[question.id] = 1
  77. const answerNode: ChatItemInTree = {
  78. ...answer,
  79. children: [],
  80. siblingIndex: isLegacy ? 0 : childrenCount[parentMessageId] - 1,
  81. }
  82. map[answer.id] = answerNode
  83. // Connect question and answer
  84. questionNode.children!.push(answerNode)
  85. // Append to parent or add to root
  86. if (isLegacy) {
  87. if (!lastAppendedLegacyAnswer)
  88. rootNodes.push(questionNode)
  89. else
  90. lastAppendedLegacyAnswer.children!.push(questionNode)
  91. lastAppendedLegacyAnswer = answerNode
  92. }
  93. else {
  94. if (
  95. !parentMessageId
  96. || !allMessages.some(item => item.id === parentMessageId) // parent message might not be fetched yet, in this case we will append the question to the root nodes
  97. )
  98. rootNodes.push(questionNode)
  99. else
  100. map[parentMessageId]?.children!.push(questionNode)
  101. }
  102. }
  103. return rootNodes
  104. }
  105. function getThreadMessages(tree: ChatItemInTree[], targetMessageId?: string): ChatItemInTree[] {
  106. let ret: ChatItemInTree[] = []
  107. let targetNode: ChatItemInTree | undefined
  108. // find path to the target message
  109. const stack = tree.slice().reverse().map(rootNode => ({
  110. node: rootNode,
  111. path: [rootNode],
  112. }))
  113. while (stack.length > 0) {
  114. const { node, path } = stack.pop()!
  115. if (
  116. node.id === targetMessageId
  117. || (!targetMessageId && !node.children?.length && !stack.length) // if targetMessageId is not provided, we use the last message in the tree as the target
  118. ) {
  119. targetNode = node
  120. ret = path.map((item, index) => {
  121. if (!item.isAnswer)
  122. return item
  123. const parentAnswer = path[index - 2]
  124. const siblingCount = !parentAnswer ? tree.length : parentAnswer.children!.length
  125. const prevSibling = !parentAnswer ? tree[item.siblingIndex! - 1]?.children?.[0]?.id : parentAnswer.children![item.siblingIndex! - 1]?.children?.[0].id
  126. const nextSibling = !parentAnswer ? tree[item.siblingIndex! + 1]?.children?.[0]?.id : parentAnswer.children![item.siblingIndex! + 1]?.children?.[0].id
  127. return { ...item, siblingCount, prevSibling, nextSibling }
  128. })
  129. break
  130. }
  131. if (node.children) {
  132. for (let i = node.children.length - 1; i >= 0; i--) {
  133. stack.push({
  134. node: node.children[i],
  135. path: [...path, node.children[i]],
  136. })
  137. }
  138. }
  139. }
  140. // append all descendant messages to the path
  141. if (targetNode) {
  142. const stack = [targetNode]
  143. while (stack.length > 0) {
  144. const node = stack.pop()!
  145. if (node !== targetNode)
  146. ret.push(node)
  147. if (node.children?.length) {
  148. const lastChild = node.children.at(-1)!
  149. if (!lastChild.isAnswer) {
  150. stack.push(lastChild)
  151. continue
  152. }
  153. const parentAnswer = ret.at(-2)
  154. const siblingCount = parentAnswer?.children?.length
  155. const prevSibling = parentAnswer?.children?.at(-2)?.children?.[0]?.id
  156. stack.push({ ...lastChild, siblingCount, prevSibling })
  157. }
  158. }
  159. }
  160. return ret
  161. }
  162. export {
  163. getProcessedInputsFromUrlParams,
  164. getProcessedSystemVariablesFromUrlParams,
  165. isValidGeneratedAnswer,
  166. getLastAnswer,
  167. buildChatItemTree,
  168. getThreadMessages,
  169. }