Selaa lähdekoodia

feat: regenerate history switch navigation (#8749)

Hash Brown 6 kuukautta sitten
vanhempi
commit
57ec12eb6b

+ 2 - 0
api/controllers/console/app/message.py

@@ -105,6 +105,8 @@ class ChatMessageListApi(Resource):
             if rest_count > 0:
                 has_more = True
 
+        history_messages = list(reversed(history_messages))
+
         return InfiniteScrollPagination(data=history_messages, limit=args["limit"], has_more=has_more)
 
 

+ 0 - 0
web/__mocks__/mime.js


+ 45 - 24
web/app/components/app/log/list.tsx

@@ -17,6 +17,7 @@ import { createContext, useContext } from 'use-context-selector'
 import { useShallow } from 'zustand/react/shallow'
 import { useTranslation } from 'react-i18next'
 import { UUID_NIL } from '../../base/chat/constants'
+import type { ChatItemInTree } from '../../base/chat/types'
 import VarPanel from './var-panel'
 import cn from '@/utils/classnames'
 import type { FeedbackFunc, FeedbackType, IChatItem, SubmitAnnotationFunc } from '@/app/components/base/chat/chat/type'
@@ -41,6 +42,7 @@ import { useAppContext } from '@/context/app-context'
 import useTimestamp from '@/hooks/use-timestamp'
 import Tooltip from '@/app/components/base/tooltip'
 import { CopyIcon } from '@/app/components/base/copy-icon'
+import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
 import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
 
 dayjs.extend(utc)
@@ -139,6 +141,7 @@ function appendQAToChatList(newChatList: IChatItem[], item: any, conversationId:
     })(),
     parentMessageId: `question-${item.id}`,
   })
+
   const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
   newChatList.push({
     id: `question-${item.id}`,
@@ -193,50 +196,66 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
     currentLogModalActiveTab: state.currentLogModalActiveTab,
   })))
   const { t } = useTranslation()
-  const [items, setItems] = React.useState<IChatItem[]>([])
-  const fetchedMessages = useRef<ChatMessage[]>([])
   const [hasMore, setHasMore] = useState(true)
   const [varValues, setVarValues] = useState<Record<string, string>>({})
-  const fetchData = async () => {
+
+  const [allChatItems, setAllChatItems] = useState<IChatItem[]>([])
+  const [chatItemTree, setChatItemTree] = useState<ChatItemInTree[]>([])
+  const [threadChatItems, setThreadChatItems] = useState<IChatItem[]>([])
+
+  const fetchData = useCallback(async () => {
     try {
       if (!hasMore)
         return
+
       const params: ChatMessagesRequest = {
         conversation_id: detail.id,
         limit: 10,
       }
-      if (items?.[0]?.id)
-        params.first_id = items?.[0]?.id.replace('question-', '')
-
+      if (allChatItems.at(-1)?.id)
+        params.first_id = allChatItems.at(-1)?.id.replace('question-', '')
       const messageRes = await fetchChatMessages({
         url: `/apps/${appDetail?.id}/chat-messages`,
         params,
       })
       if (messageRes.data.length > 0) {
-        const varValues = messageRes.data[0].inputs
+        const varValues = messageRes.data.at(-1)!.inputs
         setVarValues(varValues)
       }
-      fetchedMessages.current = [...fetchedMessages.current, ...messageRes.data]
-      const newItems = getFormattedChatList(fetchedMessages.current, detail.id, timezone!, t('appLog.dateTimeFormat') as string)
+      setHasMore(messageRes.has_more)
+
+      const newAllChatItems = [
+        ...getFormattedChatList(messageRes.data, detail.id, timezone!, t('appLog.dateTimeFormat') as string),
+        ...allChatItems,
+      ]
+      setAllChatItems(newAllChatItems)
+
+      let tree = buildChatItemTree(newAllChatItems)
       if (messageRes.has_more === false && detail?.model_config?.configs?.introduction) {
-        newItems.unshift({
+        tree = [{
           id: 'introduction',
           isAnswer: true,
           isOpeningStatement: true,
           content: detail?.model_config?.configs?.introduction ?? 'hello',
           feedbackDisabled: true,
-        })
+          children: tree,
+        }]
       }
-      setItems(newItems)
-      setHasMore(messageRes.has_more)
+      setChatItemTree(tree)
+
+      setThreadChatItems(getThreadMessages(tree, newAllChatItems.at(-1)?.id))
     }
     catch (err) {
       console.error(err)
     }
-  }
+  }, [allChatItems, detail.id, hasMore, timezone, t, appDetail, detail?.model_config?.configs?.introduction])
+
+  const switchSibling = useCallback((siblingMessageId: string) => {
+    setThreadChatItems(getThreadMessages(chatItemTree, siblingMessageId))
+  }, [chatItemTree])
 
   const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
-    setItems(items.map((item, i) => {
+    setAllChatItems(allChatItems.map((item, i) => {
       if (i === index - 1) {
         return {
           ...item,
@@ -257,9 +276,9 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
       }
       return item
     }))
-  }, [items])
+  }, [allChatItems])
   const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => {
-    setItems(items.map((item, i) => {
+    setAllChatItems(allChatItems.map((item, i) => {
       if (i === index - 1) {
         return {
           ...item,
@@ -287,9 +306,9 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
       }
       return item
     }))
-  }, [items])
+  }, [allChatItems])
   const handleAnnotationRemoved = useCallback((index: number) => {
-    setItems(items.map((item, i) => {
+    setAllChatItems(allChatItems.map((item, i) => {
       if (i === index) {
         return {
           ...item,
@@ -299,7 +318,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
       }
       return item
     }))
-  }, [items])
+  }, [allChatItems])
 
   const fetchInitiated = useRef(false)
 
@@ -464,7 +483,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
             siteInfo={null}
           />
         </div>
-        : (items.length < 8 && !hasMore)
+        : threadChatItems.length < 8
           ? <div className="pt-4 mb-4">
             <Chat
               config={{
@@ -478,7 +497,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
                 },
                 supportFeedback: true,
               } as any}
-              chatList={items}
+              chatList={threadChatItems}
               onAnnotationAdded={handleAnnotationAdded}
               onAnnotationEdited={handleAnnotationEdited}
               onAnnotationRemoved={handleAnnotationRemoved}
@@ -487,6 +506,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
               showPromptLog
               hideProcessDetail
               chatContainerInnerClassName='px-6'
+              switchSibling={switchSibling}
             />
           </div>
           : <div
@@ -501,7 +521,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
             {/* Put the scroll bar always on the bottom */}
             <InfiniteScroll
               scrollableTarget="scrollableDiv"
-              dataLength={items.length}
+              dataLength={threadChatItems.length}
               next={fetchData}
               hasMore={hasMore}
               loader={<div className='text-center text-gray-400 text-xs'>{t('appLog.detail.loading')}...</div>}
@@ -532,7 +552,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
                   },
                   supportFeedback: true,
                 } as any}
-                chatList={items}
+                chatList={threadChatItems}
                 onAnnotationAdded={handleAnnotationAdded}
                 onAnnotationEdited={handleAnnotationEdited}
                 onAnnotationRemoved={handleAnnotationRemoved}
@@ -541,6 +561,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
                 showPromptLog
                 hideProcessDetail
                 chatContainerInnerClassName='px-6'
+                switchSibling={switchSibling}
               />
             </InfiniteScroll>
           </div>

+ 2281 - 0
web/app/components/base/chat/__tests__/__snapshots__/utils.spec.ts.snap

@@ -0,0 +1,2281 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`build chat item tree and get thread messages should get thread messages from tree6, using specified message as target 1`] = `
+[
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105791,
+            "files": [],
+            "id": "f9d7ff7c-3a3b-4d9a-a289-657817f4caff",
+            "message_id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+            "observation": "",
+            "position": 1,
+            "thought": "Sure, I'll play! My number is 57. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726105795,
+                    "files": [],
+                    "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+                    "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "I choose 83. What's your next number?",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [
+                  {
+                    "children": [
+                      {
+                        "agent_thoughts": [
+                          {
+                            "chain_id": null,
+                            "created_at": 1726105799,
+                            "files": [],
+                            "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+                            "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+                            "observation": "",
+                            "position": 1,
+                            "thought": "I'll go with 112. Your turn!",
+                            "tool": "",
+                            "tool_input": "",
+                            "tool_labels": {},
+                          },
+                        ],
+                        "children": [],
+                        "content": "I'll go with 112. Your turn!",
+                        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                        "feedbackDisabled": false,
+                        "id": "4c5d0841-1206-463e-95d8-71f812877658",
+                        "input": {
+                          "inputs": {},
+                          "query": "99",
+                        },
+                        "isAnswer": true,
+                        "log": [
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "Sure, I'll play! My number is 57. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "58",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "I choose 83. What's your next number?",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "99",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "I'll go with 112. Your turn!",
+                          },
+                        ],
+                        "message_files": [],
+                        "more": {
+                          "latency": "1.49",
+                          "time": "09/11/2024 09:50 PM",
+                          "tokens": 86,
+                        },
+                        "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                        "siblingIndex": 0,
+                        "workflow_run_id": null,
+                      },
+                    ],
+                    "content": "99",
+                    "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                    "isAnswer": false,
+                    "message_files": [],
+                    "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+                  },
+                ],
+                "content": "I choose 83. What's your next number?",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+                "input": {
+                  "inputs": {},
+                  "query": "58",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure, I'll play! My number is 57. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "58",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "I choose 83. What's your next number?",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.33",
+                  "time": "09/11/2024 09:49 PM",
+                  "tokens": 68,
+                },
+                "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "58",
+            "id": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+          },
+        ],
+        "content": "Sure, I'll play! My number is 57. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "input": {
+          "inputs": {},
+          "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure, I'll play! My number is 57. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.56",
+          "time": "09/11/2024 09:49 PM",
+          "tokens": 49,
+        },
+        "parentMessageId": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "siblingIndex": 0,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet random-looking number. I'll start first, 38",
+    "id": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "isAnswer": false,
+    "message_files": [],
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726105791,
+        "files": [],
+        "id": "f9d7ff7c-3a3b-4d9a-a289-657817f4caff",
+        "message_id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "observation": "",
+        "position": 1,
+        "thought": "Sure, I'll play! My number is 57. Your turn!",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [
+      {
+        "children": [
+          {
+            "agent_thoughts": [
+              {
+                "chain_id": null,
+                "created_at": 1726105795,
+                "files": [],
+                "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+                "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+                "observation": "",
+                "position": 1,
+                "thought": "I choose 83. What's your next number?",
+                "tool": "",
+                "tool_input": "",
+                "tool_labels": {},
+              },
+            ],
+            "children": [
+              {
+                "children": [
+                  {
+                    "agent_thoughts": [
+                      {
+                        "chain_id": null,
+                        "created_at": 1726105799,
+                        "files": [],
+                        "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+                        "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+                        "observation": "",
+                        "position": 1,
+                        "thought": "I'll go with 112. Your turn!",
+                        "tool": "",
+                        "tool_input": "",
+                        "tool_labels": {},
+                      },
+                    ],
+                    "children": [],
+                    "content": "I'll go with 112. Your turn!",
+                    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                    "feedbackDisabled": false,
+                    "id": "4c5d0841-1206-463e-95d8-71f812877658",
+                    "input": {
+                      "inputs": {},
+                      "query": "99",
+                    },
+                    "isAnswer": true,
+                    "log": [
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "Sure, I'll play! My number is 57. Your turn!",
+                      },
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "58",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "I choose 83. What's your next number?",
+                      },
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "99",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "I'll go with 112. Your turn!",
+                      },
+                    ],
+                    "message_files": [],
+                    "more": {
+                      "latency": "1.49",
+                      "time": "09/11/2024 09:50 PM",
+                      "tokens": 86,
+                    },
+                    "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                    "siblingIndex": 0,
+                    "workflow_run_id": null,
+                  },
+                ],
+                "content": "99",
+                "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                "isAnswer": false,
+                "message_files": [],
+                "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+              },
+            ],
+            "content": "I choose 83. What's your next number?",
+            "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+            "feedbackDisabled": false,
+            "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+            "input": {
+              "inputs": {},
+              "query": "58",
+            },
+            "isAnswer": true,
+            "log": [
+              {
+                "files": [],
+                "role": "user",
+                "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "Sure, I'll play! My number is 57. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "58",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "I choose 83. What's your next number?",
+              },
+            ],
+            "message_files": [],
+            "more": {
+              "latency": "1.33",
+              "time": "09/11/2024 09:49 PM",
+              "tokens": 68,
+            },
+            "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+            "siblingIndex": 0,
+            "workflow_run_id": null,
+          },
+        ],
+        "content": "58",
+        "id": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+        "isAnswer": false,
+        "message_files": [],
+        "parentMessageId": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+      },
+    ],
+    "content": "Sure, I'll play! My number is 57. Your turn!",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "input": {
+      "inputs": {},
+      "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.56",
+      "time": "09/11/2024 09:49 PM",
+      "tokens": 49,
+    },
+    "nextSibling": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "parentMessageId": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "prevSibling": undefined,
+    "siblingCount": 2,
+    "siblingIndex": 0,
+    "workflow_run_id": null,
+  },
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105795,
+            "files": [],
+            "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+            "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+            "observation": "",
+            "position": 1,
+            "thought": "I choose 83. What's your next number?",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726105799,
+                    "files": [],
+                    "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+                    "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "I'll go with 112. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [],
+                "content": "I'll go with 112. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "4c5d0841-1206-463e-95d8-71f812877658",
+                "input": {
+                  "inputs": {},
+                  "query": "99",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure, I'll play! My number is 57. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "58",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "I choose 83. What's your next number?",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "99",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "I'll go with 112. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.49",
+                  "time": "09/11/2024 09:50 PM",
+                  "tokens": 86,
+                },
+                "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "99",
+            "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+          },
+        ],
+        "content": "I choose 83. What's your next number?",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+        "input": {
+          "inputs": {},
+          "query": "58",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure, I'll play! My number is 57. Your turn!",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "58",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "I choose 83. What's your next number?",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.33",
+          "time": "09/11/2024 09:49 PM",
+          "tokens": 68,
+        },
+        "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+        "siblingIndex": 0,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "58",
+    "id": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726105795,
+        "files": [],
+        "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+        "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+        "observation": "",
+        "position": 1,
+        "thought": "I choose 83. What's your next number?",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [
+      {
+        "children": [
+          {
+            "agent_thoughts": [
+              {
+                "chain_id": null,
+                "created_at": 1726105799,
+                "files": [],
+                "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+                "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+                "observation": "",
+                "position": 1,
+                "thought": "I'll go with 112. Your turn!",
+                "tool": "",
+                "tool_input": "",
+                "tool_labels": {},
+              },
+            ],
+            "children": [],
+            "content": "I'll go with 112. Your turn!",
+            "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+            "feedbackDisabled": false,
+            "id": "4c5d0841-1206-463e-95d8-71f812877658",
+            "input": {
+              "inputs": {},
+              "query": "99",
+            },
+            "isAnswer": true,
+            "log": [
+              {
+                "files": [],
+                "role": "user",
+                "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "Sure, I'll play! My number is 57. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "58",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "I choose 83. What's your next number?",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "99",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "I'll go with 112. Your turn!",
+              },
+            ],
+            "message_files": [],
+            "more": {
+              "latency": "1.49",
+              "time": "09/11/2024 09:50 PM",
+              "tokens": 86,
+            },
+            "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+            "siblingIndex": 0,
+            "workflow_run_id": null,
+          },
+        ],
+        "content": "99",
+        "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+        "isAnswer": false,
+        "message_files": [],
+        "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+      },
+    ],
+    "content": "I choose 83. What's your next number?",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+    "input": {
+      "inputs": {},
+      "query": "58",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "58",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "I choose 83. What's your next number?",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.33",
+      "time": "09/11/2024 09:49 PM",
+      "tokens": 68,
+    },
+    "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+    "prevSibling": undefined,
+    "siblingCount": 1,
+    "siblingIndex": 0,
+    "workflow_run_id": null,
+  },
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105799,
+            "files": [],
+            "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+            "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+            "observation": "",
+            "position": 1,
+            "thought": "I'll go with 112. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [],
+        "content": "I'll go with 112. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "4c5d0841-1206-463e-95d8-71f812877658",
+        "input": {
+          "inputs": {},
+          "query": "99",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure, I'll play! My number is 57. Your turn!",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "58",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "I choose 83. What's your next number?",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "99",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "I'll go with 112. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.49",
+          "time": "09/11/2024 09:50 PM",
+          "tokens": 86,
+        },
+        "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+        "siblingIndex": 0,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "99",
+    "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726105799,
+        "files": [],
+        "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+        "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+        "observation": "",
+        "position": 1,
+        "thought": "I'll go with 112. Your turn!",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [],
+    "content": "I'll go with 112. Your turn!",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "4c5d0841-1206-463e-95d8-71f812877658",
+    "input": {
+      "inputs": {},
+      "query": "99",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "58",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "I choose 83. What's your next number?",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "99",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "I'll go with 112. Your turn!",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.49",
+      "time": "09/11/2024 09:50 PM",
+      "tokens": 86,
+    },
+    "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+    "prevSibling": undefined,
+    "siblingCount": 1,
+    "siblingIndex": 0,
+    "workflow_run_id": null,
+  },
+]
+`;
+
+exports[`build chat item tree and get thread messages should get thread messages from tree6, using the last message as target 1`] = `
+[
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105809,
+            "files": [],
+            "id": "1019cd79-d141-4f9f-880a-fc1441cfd802",
+            "message_id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+            "observation": "",
+            "position": 1,
+            "thought": "Sure! My number is 54. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726105822,
+                    "files": [],
+                    "id": "0773bec7-b992-4a53-92b2-20ebaeae8798",
+                    "message_id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "My number is 4729. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [],
+                "content": "My number is 4729. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+                "input": {
+                  "inputs": {},
+                  "query": "3306",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure! My number is 54. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "3306",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 4729. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.30",
+                  "time": "09/11/2024 09:50 PM",
+                  "tokens": 66,
+                },
+                "parentMessageId": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "3306",
+            "id": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+          },
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726107812,
+                    "files": [],
+                    "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+                    "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "My number is 4821. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [
+                  {
+                    "children": [
+                      {
+                        "agent_thoughts": [
+                          {
+                            "chain_id": null,
+                            "created_at": 1726111024,
+                            "files": [],
+                            "id": "095cacab-afad-4387-a41d-1662578b8b13",
+                            "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                            "observation": "",
+                            "position": 1,
+                            "thought": "My number is 1456. Your turn!",
+                            "tool": "",
+                            "tool_input": "",
+                            "tool_labels": {},
+                          },
+                        ],
+                        "children": [],
+                        "content": "My number is 1456. Your turn!",
+                        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                        "feedbackDisabled": false,
+                        "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                        "input": {
+                          "inputs": {},
+                          "query": "1003",
+                        },
+                        "isAnswer": true,
+                        "log": [
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "Sure! My number is 54. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "3306",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "My number is 4821. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "1003",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "My number is 1456. Your turn!",
+                          },
+                        ],
+                        "message_files": [],
+                        "more": {
+                          "latency": "1.38",
+                          "time": "09/11/2024 11:17 PM",
+                          "tokens": 86,
+                        },
+                        "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                        "siblingIndex": 0,
+                        "workflow_run_id": null,
+                      },
+                    ],
+                    "content": "1003",
+                    "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                    "isAnswer": false,
+                    "message_files": [],
+                    "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                  },
+                ],
+                "content": "My number is 4821. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                "input": {
+                  "inputs": {},
+                  "query": "3306",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure! My number is 54. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "3306",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 4821. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.48",
+                  "time": "09/11/2024 10:23 PM",
+                  "tokens": 66,
+                },
+                "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+                "siblingIndex": 1,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "3306",
+            "id": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+          },
+        ],
+        "content": "Sure! My number is 54. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "input": {
+          "inputs": {},
+          "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure! My number is 54. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.52",
+          "time": "09/11/2024 09:50 PM",
+          "tokens": 46,
+        },
+        "parentMessageId": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "siblingIndex": 1,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+    "id": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "isAnswer": false,
+    "message_files": [],
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726105809,
+        "files": [],
+        "id": "1019cd79-d141-4f9f-880a-fc1441cfd802",
+        "message_id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "observation": "",
+        "position": 1,
+        "thought": "Sure! My number is 54. Your turn!",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [
+      {
+        "children": [
+          {
+            "agent_thoughts": [
+              {
+                "chain_id": null,
+                "created_at": 1726105822,
+                "files": [],
+                "id": "0773bec7-b992-4a53-92b2-20ebaeae8798",
+                "message_id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+                "observation": "",
+                "position": 1,
+                "thought": "My number is 4729. Your turn!",
+                "tool": "",
+                "tool_input": "",
+                "tool_labels": {},
+              },
+            ],
+            "children": [],
+            "content": "My number is 4729. Your turn!",
+            "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+            "feedbackDisabled": false,
+            "id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+            "input": {
+              "inputs": {},
+              "query": "3306",
+            },
+            "isAnswer": true,
+            "log": [
+              {
+                "files": [],
+                "role": "user",
+                "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "Sure! My number is 54. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "3306",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "My number is 4729. Your turn!",
+              },
+            ],
+            "message_files": [],
+            "more": {
+              "latency": "1.30",
+              "time": "09/11/2024 09:50 PM",
+              "tokens": 66,
+            },
+            "parentMessageId": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+            "siblingIndex": 0,
+            "workflow_run_id": null,
+          },
+        ],
+        "content": "3306",
+        "id": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+        "isAnswer": false,
+        "message_files": [],
+        "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+      },
+      {
+        "children": [
+          {
+            "agent_thoughts": [
+              {
+                "chain_id": null,
+                "created_at": 1726107812,
+                "files": [],
+                "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+                "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                "observation": "",
+                "position": 1,
+                "thought": "My number is 4821. Your turn!",
+                "tool": "",
+                "tool_input": "",
+                "tool_labels": {},
+              },
+            ],
+            "children": [
+              {
+                "children": [
+                  {
+                    "agent_thoughts": [
+                      {
+                        "chain_id": null,
+                        "created_at": 1726111024,
+                        "files": [],
+                        "id": "095cacab-afad-4387-a41d-1662578b8b13",
+                        "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                        "observation": "",
+                        "position": 1,
+                        "thought": "My number is 1456. Your turn!",
+                        "tool": "",
+                        "tool_input": "",
+                        "tool_labels": {},
+                      },
+                    ],
+                    "children": [],
+                    "content": "My number is 1456. Your turn!",
+                    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                    "feedbackDisabled": false,
+                    "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                    "input": {
+                      "inputs": {},
+                      "query": "1003",
+                    },
+                    "isAnswer": true,
+                    "log": [
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "Sure! My number is 54. Your turn!",
+                      },
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "3306",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "My number is 4821. Your turn!",
+                      },
+                      {
+                        "files": [],
+                        "role": "user",
+                        "text": "1003",
+                      },
+                      {
+                        "files": [],
+                        "role": "assistant",
+                        "text": "My number is 1456. Your turn!",
+                      },
+                    ],
+                    "message_files": [],
+                    "more": {
+                      "latency": "1.38",
+                      "time": "09/11/2024 11:17 PM",
+                      "tokens": 86,
+                    },
+                    "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                    "siblingIndex": 0,
+                    "workflow_run_id": null,
+                  },
+                ],
+                "content": "1003",
+                "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                "isAnswer": false,
+                "message_files": [],
+                "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+              },
+            ],
+            "content": "My number is 4821. Your turn!",
+            "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+            "feedbackDisabled": false,
+            "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+            "input": {
+              "inputs": {},
+              "query": "3306",
+            },
+            "isAnswer": true,
+            "log": [
+              {
+                "files": [],
+                "role": "user",
+                "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "Sure! My number is 54. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "3306",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "My number is 4821. Your turn!",
+              },
+            ],
+            "message_files": [],
+            "more": {
+              "latency": "1.48",
+              "time": "09/11/2024 10:23 PM",
+              "tokens": 66,
+            },
+            "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+            "siblingIndex": 1,
+            "workflow_run_id": null,
+          },
+        ],
+        "content": "3306",
+        "id": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+        "isAnswer": false,
+        "message_files": [],
+        "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+      },
+    ],
+    "content": "Sure! My number is 54. Your turn!",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "input": {
+      "inputs": {},
+      "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.52",
+      "time": "09/11/2024 09:50 PM",
+      "tokens": 46,
+    },
+    "nextSibling": undefined,
+    "parentMessageId": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "prevSibling": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "siblingCount": 2,
+    "siblingIndex": 1,
+    "workflow_run_id": null,
+  },
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726107812,
+            "files": [],
+            "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+            "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+            "observation": "",
+            "position": 1,
+            "thought": "My number is 4821. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726111024,
+                    "files": [],
+                    "id": "095cacab-afad-4387-a41d-1662578b8b13",
+                    "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "My number is 1456. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [],
+                "content": "My number is 1456. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                "input": {
+                  "inputs": {},
+                  "query": "1003",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure! My number is 54. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "3306",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 4821. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "1003",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 1456. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.38",
+                  "time": "09/11/2024 11:17 PM",
+                  "tokens": 86,
+                },
+                "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "1003",
+            "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+          },
+        ],
+        "content": "My number is 4821. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+        "input": {
+          "inputs": {},
+          "query": "3306",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure! My number is 54. Your turn!",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "3306",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "My number is 4821. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.48",
+          "time": "09/11/2024 10:23 PM",
+          "tokens": 66,
+        },
+        "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+        "siblingIndex": 1,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "3306",
+    "id": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726107812,
+        "files": [],
+        "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+        "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+        "observation": "",
+        "position": 1,
+        "thought": "My number is 4821. Your turn!",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [
+      {
+        "children": [
+          {
+            "agent_thoughts": [
+              {
+                "chain_id": null,
+                "created_at": 1726111024,
+                "files": [],
+                "id": "095cacab-afad-4387-a41d-1662578b8b13",
+                "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                "observation": "",
+                "position": 1,
+                "thought": "My number is 1456. Your turn!",
+                "tool": "",
+                "tool_input": "",
+                "tool_labels": {},
+              },
+            ],
+            "children": [],
+            "content": "My number is 1456. Your turn!",
+            "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+            "feedbackDisabled": false,
+            "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+            "input": {
+              "inputs": {},
+              "query": "1003",
+            },
+            "isAnswer": true,
+            "log": [
+              {
+                "files": [],
+                "role": "user",
+                "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "Sure! My number is 54. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "3306",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "My number is 4821. Your turn!",
+              },
+              {
+                "files": [],
+                "role": "user",
+                "text": "1003",
+              },
+              {
+                "files": [],
+                "role": "assistant",
+                "text": "My number is 1456. Your turn!",
+              },
+            ],
+            "message_files": [],
+            "more": {
+              "latency": "1.38",
+              "time": "09/11/2024 11:17 PM",
+              "tokens": 86,
+            },
+            "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+            "siblingIndex": 0,
+            "workflow_run_id": null,
+          },
+        ],
+        "content": "1003",
+        "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+        "isAnswer": false,
+        "message_files": [],
+        "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+      },
+    ],
+    "content": "My number is 4821. Your turn!",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+    "input": {
+      "inputs": {},
+      "query": "3306",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "3306",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "My number is 4821. Your turn!",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.48",
+      "time": "09/11/2024 10:23 PM",
+      "tokens": 66,
+    },
+    "nextSibling": undefined,
+    "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+    "prevSibling": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+    "siblingCount": 2,
+    "siblingIndex": 1,
+    "workflow_run_id": null,
+  },
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726111024,
+            "files": [],
+            "id": "095cacab-afad-4387-a41d-1662578b8b13",
+            "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+            "observation": "",
+            "position": 1,
+            "thought": "My number is 1456. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [],
+        "content": "My number is 1456. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+        "input": {
+          "inputs": {},
+          "query": "1003",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure! My number is 54. Your turn!",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "3306",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "My number is 4821. Your turn!",
+          },
+          {
+            "files": [],
+            "role": "user",
+            "text": "1003",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "My number is 1456. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.38",
+          "time": "09/11/2024 11:17 PM",
+          "tokens": 86,
+        },
+        "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+        "siblingIndex": 0,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "1003",
+    "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+  },
+  {
+    "agent_thoughts": [
+      {
+        "chain_id": null,
+        "created_at": 1726111024,
+        "files": [],
+        "id": "095cacab-afad-4387-a41d-1662578b8b13",
+        "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+        "observation": "",
+        "position": 1,
+        "thought": "My number is 1456. Your turn!",
+        "tool": "",
+        "tool_input": "",
+        "tool_labels": {},
+      },
+    ],
+    "children": [],
+    "content": "My number is 1456. Your turn!",
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "feedbackDisabled": false,
+    "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+    "input": {
+      "inputs": {},
+      "query": "1003",
+    },
+    "isAnswer": true,
+    "log": [
+      {
+        "files": [],
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "3306",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "My number is 4821. Your turn!",
+      },
+      {
+        "files": [],
+        "role": "user",
+        "text": "1003",
+      },
+      {
+        "files": [],
+        "role": "assistant",
+        "text": "My number is 1456. Your turn!",
+      },
+    ],
+    "message_files": [],
+    "more": {
+      "latency": "1.38",
+      "time": "09/11/2024 11:17 PM",
+      "tokens": 86,
+    },
+    "nextSibling": undefined,
+    "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+    "prevSibling": undefined,
+    "siblingCount": 1,
+    "siblingIndex": 0,
+    "workflow_run_id": null,
+  },
+]
+`;
+
+exports[`build chat item tree and get thread messages should work with real world messages 1`] = `
+[
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105791,
+            "files": [],
+            "id": "f9d7ff7c-3a3b-4d9a-a289-657817f4caff",
+            "message_id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+            "observation": "",
+            "position": 1,
+            "thought": "Sure, I'll play! My number is 57. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726105795,
+                    "files": [],
+                    "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+                    "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "I choose 83. What's your next number?",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [
+                  {
+                    "children": [
+                      {
+                        "agent_thoughts": [
+                          {
+                            "chain_id": null,
+                            "created_at": 1726105799,
+                            "files": [],
+                            "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+                            "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+                            "observation": "",
+                            "position": 1,
+                            "thought": "I'll go with 112. Your turn!",
+                            "tool": "",
+                            "tool_input": "",
+                            "tool_labels": {},
+                          },
+                        ],
+                        "children": [],
+                        "content": "I'll go with 112. Your turn!",
+                        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                        "feedbackDisabled": false,
+                        "id": "4c5d0841-1206-463e-95d8-71f812877658",
+                        "input": {
+                          "inputs": {},
+                          "query": "99",
+                        },
+                        "isAnswer": true,
+                        "log": [
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "Sure, I'll play! My number is 57. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "58",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "I choose 83. What's your next number?",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "99",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "I'll go with 112. Your turn!",
+                          },
+                        ],
+                        "message_files": [],
+                        "more": {
+                          "latency": "1.49",
+                          "time": "09/11/2024 09:50 PM",
+                          "tokens": 86,
+                        },
+                        "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                        "siblingIndex": 0,
+                        "workflow_run_id": null,
+                      },
+                    ],
+                    "content": "99",
+                    "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+                    "isAnswer": false,
+                    "message_files": [],
+                    "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
+                  },
+                ],
+                "content": "I choose 83. What's your next number?",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+                "input": {
+                  "inputs": {},
+                  "query": "58",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure, I'll play! My number is 57. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "58",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "I choose 83. What's your next number?",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.33",
+                  "time": "09/11/2024 09:49 PM",
+                  "tokens": 68,
+                },
+                "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "58",
+            "id": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+          },
+        ],
+        "content": "Sure, I'll play! My number is 57. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "input": {
+          "inputs": {},
+          "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure, I'll play! My number is 57. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.56",
+          "time": "09/11/2024 09:49 PM",
+          "tokens": 49,
+        },
+        "parentMessageId": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "siblingIndex": 0,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet random-looking number. I'll start first, 38",
+    "id": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "isAnswer": false,
+    "message_files": [],
+  },
+  {
+    "children": [
+      {
+        "agent_thoughts": [
+          {
+            "chain_id": null,
+            "created_at": 1726105809,
+            "files": [],
+            "id": "1019cd79-d141-4f9f-880a-fc1441cfd802",
+            "message_id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+            "observation": "",
+            "position": 1,
+            "thought": "Sure! My number is 54. Your turn!",
+            "tool": "",
+            "tool_input": "",
+            "tool_labels": {},
+          },
+        ],
+        "children": [
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726105822,
+                    "files": [],
+                    "id": "0773bec7-b992-4a53-92b2-20ebaeae8798",
+                    "message_id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "My number is 4729. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [],
+                "content": "My number is 4729. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+                "input": {
+                  "inputs": {},
+                  "query": "3306",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure! My number is 54. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "3306",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 4729. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.30",
+                  "time": "09/11/2024 09:50 PM",
+                  "tokens": 66,
+                },
+                "parentMessageId": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+                "siblingIndex": 0,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "3306",
+            "id": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+          },
+          {
+            "children": [
+              {
+                "agent_thoughts": [
+                  {
+                    "chain_id": null,
+                    "created_at": 1726107812,
+                    "files": [],
+                    "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+                    "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                    "observation": "",
+                    "position": 1,
+                    "thought": "My number is 4821. Your turn!",
+                    "tool": "",
+                    "tool_input": "",
+                    "tool_labels": {},
+                  },
+                ],
+                "children": [
+                  {
+                    "children": [
+                      {
+                        "agent_thoughts": [
+                          {
+                            "chain_id": null,
+                            "created_at": 1726111024,
+                            "files": [],
+                            "id": "095cacab-afad-4387-a41d-1662578b8b13",
+                            "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                            "observation": "",
+                            "position": 1,
+                            "thought": "My number is 1456. Your turn!",
+                            "tool": "",
+                            "tool_input": "",
+                            "tool_labels": {},
+                          },
+                        ],
+                        "children": [],
+                        "content": "My number is 1456. Your turn!",
+                        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                        "feedbackDisabled": false,
+                        "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                        "input": {
+                          "inputs": {},
+                          "query": "1003",
+                        },
+                        "isAnswer": true,
+                        "log": [
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "Sure! My number is 54. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "3306",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "My number is 4821. Your turn!",
+                          },
+                          {
+                            "files": [],
+                            "role": "user",
+                            "text": "1003",
+                          },
+                          {
+                            "files": [],
+                            "role": "assistant",
+                            "text": "My number is 1456. Your turn!",
+                          },
+                        ],
+                        "message_files": [],
+                        "more": {
+                          "latency": "1.38",
+                          "time": "09/11/2024 11:17 PM",
+                          "tokens": 86,
+                        },
+                        "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                        "siblingIndex": 0,
+                        "workflow_run_id": null,
+                      },
+                    ],
+                    "content": "1003",
+                    "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+                    "isAnswer": false,
+                    "message_files": [],
+                    "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                  },
+                ],
+                "content": "My number is 4821. Your turn!",
+                "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+                "feedbackDisabled": false,
+                "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+                "input": {
+                  "inputs": {},
+                  "query": "3306",
+                },
+                "isAnswer": true,
+                "log": [
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "Sure! My number is 54. Your turn!",
+                  },
+                  {
+                    "files": [],
+                    "role": "user",
+                    "text": "3306",
+                  },
+                  {
+                    "files": [],
+                    "role": "assistant",
+                    "text": "My number is 4821. Your turn!",
+                  },
+                ],
+                "message_files": [],
+                "more": {
+                  "latency": "1.48",
+                  "time": "09/11/2024 10:23 PM",
+                  "tokens": 66,
+                },
+                "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+                "siblingIndex": 1,
+                "workflow_run_id": null,
+              },
+            ],
+            "content": "3306",
+            "id": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+            "isAnswer": false,
+            "message_files": [],
+            "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+          },
+        ],
+        "content": "Sure! My number is 54. Your turn!",
+        "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+        "feedbackDisabled": false,
+        "id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "input": {
+          "inputs": {},
+          "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        },
+        "isAnswer": true,
+        "log": [
+          {
+            "files": [],
+            "role": "user",
+            "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+          },
+          {
+            "files": [],
+            "role": "assistant",
+            "text": "Sure! My number is 54. Your turn!",
+          },
+        ],
+        "message_files": [],
+        "more": {
+          "latency": "1.52",
+          "time": "09/11/2024 09:50 PM",
+          "tokens": 46,
+        },
+        "parentMessageId": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "siblingIndex": 1,
+        "workflow_run_id": null,
+      },
+    ],
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+    "id": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "isAnswer": false,
+    "message_files": [],
+  },
+]
+`;

+ 42 - 0
web/app/components/base/chat/__tests__/branchedTestMessages.json

@@ -0,0 +1,42 @@
+[
+  {
+    "id": "question-1",
+    "isAnswer": false,
+    "parentMessageId": null
+  },
+  {
+    "id": "1",
+    "isAnswer": true,
+    "parentMessageId": "question-1"
+  },
+  {
+    "id": "question-2",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "2",
+    "isAnswer": true,
+    "parentMessageId": "question-2"
+  },
+  {
+    "id": "question-3",
+    "isAnswer": false,
+    "parentMessageId": "2"
+  },
+  {
+    "id": "3",
+    "isAnswer": true,
+    "parentMessageId": "question-3"
+  },
+  {
+    "id": "question-4",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "4",
+    "isAnswer": true,
+    "parentMessageId": "question-4"
+  }
+]

+ 42 - 0
web/app/components/base/chat/__tests__/legacyTestMessages.json

@@ -0,0 +1,42 @@
+[
+  {
+    "id": "question-1",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "1",
+    "isAnswer": true,
+    "parentMessageId": "question-1"
+  },
+  {
+    "id": "question-2",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "2",
+    "isAnswer": true,
+    "parentMessageId": "question-2"
+  },
+  {
+    "id": "question-3",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "3",
+    "isAnswer": true,
+    "parentMessageId": "question-3"
+  },
+  {
+    "id": "question-4",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "4",
+    "isAnswer": true,
+    "parentMessageId": "question-4"
+  }
+]

+ 42 - 0
web/app/components/base/chat/__tests__/mixedTestMessages.json

@@ -0,0 +1,42 @@
+[
+  {
+    "id": "question-1",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "1",
+    "isAnswer": true,
+    "parentMessageId": "question-1"
+  },
+  {
+    "id": "question-2",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "2",
+    "isAnswer": true,
+    "parentMessageId": "question-2"
+  },
+  {
+    "id": "question-3",
+    "isAnswer": false,
+    "parentMessageId": "2"
+  },
+  {
+    "id": "3",
+    "isAnswer": true,
+    "parentMessageId": "question-3"
+  },
+  {
+    "id": "question-4",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "4",
+    "isAnswer": true,
+    "parentMessageId": "question-4"
+  }
+]

+ 52 - 0
web/app/components/base/chat/__tests__/multiRootNodesMessages.json

@@ -0,0 +1,52 @@
+[
+  {
+    "id": "question-1",
+    "isAnswer": false,
+    "parentMessageId": null
+  },
+  {
+    "id": "1",
+    "isAnswer": true,
+    "parentMessageId": "question-1"
+  },
+  {
+    "id": "question-2",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "2",
+    "isAnswer": true,
+    "parentMessageId": "question-2"
+  },
+  {
+    "id": "question-3",
+    "isAnswer": false,
+    "parentMessageId": "2"
+  },
+  {
+    "id": "3",
+    "isAnswer": true,
+    "parentMessageId": "question-3"
+  },
+  {
+    "id": "question-4",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "4",
+    "isAnswer": true,
+    "parentMessageId": "question-4"
+  },
+  {
+    "id": "question-5",
+    "isAnswer": false,
+    "parentMessageId": null
+  },
+  {
+    "id": "5",
+    "isAnswer": true,
+    "parentMessageId": "question-5"
+  }
+]

+ 52 - 0
web/app/components/base/chat/__tests__/multiRootNodesWithLegacyTestMessages.json

@@ -0,0 +1,52 @@
+[
+  {
+    "id": "question-1",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "1",
+    "isAnswer": true,
+    "parentMessageId": "question-1"
+  },
+  {
+    "id": "question-2",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "2",
+    "isAnswer": true,
+    "parentMessageId": "question-2"
+  },
+  {
+    "id": "question-3",
+    "isAnswer": false,
+    "parentMessageId": "00000000-0000-0000-0000-000000000000"
+  },
+  {
+    "id": "3",
+    "isAnswer": true,
+    "parentMessageId": "question-3"
+  },
+  {
+    "id": "question-4",
+    "isAnswer": false,
+    "parentMessageId": "1"
+  },
+  {
+    "id": "4",
+    "isAnswer": true,
+    "parentMessageId": "question-4"
+  },
+  {
+    "id": "question-5",
+    "isAnswer": false,
+    "parentMessageId": null
+  },
+  {
+    "id": "5",
+    "isAnswer": true,
+    "parentMessageId": "question-5"
+  }
+]

+ 441 - 0
web/app/components/base/chat/__tests__/realWorldMessages.json

@@ -0,0 +1,441 @@
+[
+  {
+    "id": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet random-looking number. I'll start first, 38",
+    "isAnswer": false,
+    "message_files": []
+  },
+  {
+    "id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+    "content": "Sure, I'll play! My number is 57. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "f9d7ff7c-3a3b-4d9a-a289-657817f4caff",
+        "chain_id": null,
+        "message_id": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b",
+        "position": 1,
+        "thought": "Sure, I'll play! My number is 57. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726105791,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38"
+    },
+    "more": {
+      "time": "09/11/2024 09:49 PM",
+      "tokens": 49,
+      "latency": "1.56"
+    },
+    "parentMessageId": "question-ff4c2b43-48a5-47ad-9dc5-08b34ddba61b"
+  },
+  {
+    "id": "question-73bbad14-d915-499d-87bf-0df14d40779d",
+    "content": "58",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "ff4c2b43-48a5-47ad-9dc5-08b34ddba61b"
+  },
+  {
+    "id": "73bbad14-d915-499d-87bf-0df14d40779d",
+    "content": "I choose 83. What's your next number?",
+    "agent_thoughts": [
+      {
+        "id": "f61a3fce-37ac-4f9d-9935-95f97e598dfe",
+        "chain_id": null,
+        "message_id": "73bbad14-d915-499d-87bf-0df14d40779d",
+        "position": 1,
+        "thought": "I choose 83. What's your next number?",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726105795,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "58",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "I choose 83. What's your next number?",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "58"
+    },
+    "more": {
+      "time": "09/11/2024 09:49 PM",
+      "tokens": 68,
+      "latency": "1.33"
+    },
+    "parentMessageId": "question-73bbad14-d915-499d-87bf-0df14d40779d"
+  },
+  {
+    "id": "question-4c5d0841-1206-463e-95d8-71f812877658",
+    "content": "99",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d"
+  },
+  {
+    "id": "4c5d0841-1206-463e-95d8-71f812877658",
+    "content": "I'll go with 112. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "9730d587-9268-4683-9dd9-91a1cab9510b",
+        "chain_id": null,
+        "message_id": "4c5d0841-1206-463e-95d8-71f812877658",
+        "position": 1,
+        "thought": "I'll go with 112. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726105799,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure, I'll play! My number is 57. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "58",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "I choose 83. What's your next number?",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "99",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "I'll go with 112. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "99"
+    },
+    "more": {
+      "time": "09/11/2024 09:50 PM",
+      "tokens": 86,
+      "latency": "1.49"
+    },
+    "parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658"
+  },
+  {
+    "id": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "content": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+    "isAnswer": false,
+    "message_files": []
+  },
+  {
+    "id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+    "content": "Sure! My number is 54. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "1019cd79-d141-4f9f-880a-fc1441cfd802",
+        "chain_id": null,
+        "message_id": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd",
+        "position": 1,
+        "thought": "Sure! My number is 54. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726105809,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38"
+    },
+    "more": {
+      "time": "09/11/2024 09:50 PM",
+      "tokens": 46,
+      "latency": "1.52"
+    },
+    "parentMessageId": "question-cd5affb0-7bc2-4a6f-be7e-25e74595c9dd"
+  },
+  {
+    "id": "question-324bce32-c98c-435d-a66b-bac974ebb5ed",
+    "content": "3306",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd"
+  },
+  {
+    "id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+    "content": "My number is 4729. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "0773bec7-b992-4a53-92b2-20ebaeae8798",
+        "chain_id": null,
+        "message_id": "324bce32-c98c-435d-a66b-bac974ebb5ed",
+        "position": 1,
+        "thought": "My number is 4729. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726105822,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "3306",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "My number is 4729. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "3306"
+    },
+    "more": {
+      "time": "09/11/2024 09:50 PM",
+      "tokens": 66,
+      "latency": "1.30"
+    },
+    "parentMessageId": "question-324bce32-c98c-435d-a66b-bac974ebb5ed"
+  },
+  {
+    "id": "question-684b5396-4e91-4043-88e9-aabe48b21acc",
+    "content": "3306",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "cd5affb0-7bc2-4a6f-be7e-25e74595c9dd"
+  },
+  {
+    "id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+    "content": "My number is 4821. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "5ca650f3-982c-4399-8b95-9ea241c76707",
+        "chain_id": null,
+        "message_id": "684b5396-4e91-4043-88e9-aabe48b21acc",
+        "position": 1,
+        "thought": "My number is 4821. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726107812,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "3306",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "My number is 4821. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "3306"
+    },
+    "more": {
+      "time": "09/11/2024 10:23 PM",
+      "tokens": 66,
+      "latency": "1.48"
+    },
+    "parentMessageId": "question-684b5396-4e91-4043-88e9-aabe48b21acc"
+  },
+  {
+    "id": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c",
+    "content": "1003",
+    "isAnswer": false,
+    "message_files": [],
+    "parentMessageId": "684b5396-4e91-4043-88e9-aabe48b21acc"
+  },
+  {
+    "id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+    "content": "My number is 1456. Your turn!",
+    "agent_thoughts": [
+      {
+        "id": "095cacab-afad-4387-a41d-1662578b8b13",
+        "chain_id": null,
+        "message_id": "19904a7b-7494-4ed8-b72c-1d18668cea8c",
+        "position": 1,
+        "thought": "My number is 1456. Your turn!",
+        "tool": "",
+        "tool_labels": {},
+        "tool_input": "",
+        "created_at": 1726111024,
+        "observation": "",
+        "files": []
+      }
+    ],
+    "feedbackDisabled": false,
+    "isAnswer": true,
+    "message_files": [],
+    "log": [
+      {
+        "role": "user",
+        "text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "Sure! My number is 54. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "3306",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "My number is 4821. Your turn!",
+        "files": []
+      },
+      {
+        "role": "user",
+        "text": "1003",
+        "files": []
+      },
+      {
+        "role": "assistant",
+        "text": "My number is 1456. Your turn!",
+        "files": []
+      }
+    ],
+    "workflow_run_id": null,
+    "conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
+    "input": {
+      "inputs": {},
+      "query": "1003"
+    },
+    "more": {
+      "time": "09/11/2024 11:17 PM",
+      "tokens": 86,
+      "latency": "1.38"
+    },
+    "parentMessageId": "question-19904a7b-7494-4ed8-b72c-1d18668cea8c"
+  }
+]

+ 258 - 0
web/app/components/base/chat/__tests__/utils.spec.ts

@@ -0,0 +1,258 @@
+import { get } from 'lodash'
+import { buildChatItemTree, getThreadMessages } from '../utils'
+import type { ChatItemInTree } from '../types'
+import branchedTestMessages from './branchedTestMessages.json'
+import legacyTestMessages from './legacyTestMessages.json'
+import mixedTestMessages from './mixedTestMessages.json'
+import multiRootNodesMessages from './multiRootNodesMessages.json'
+import multiRootNodesWithLegacyTestMessages from './multiRootNodesWithLegacyTestMessages.json'
+import realWorldMessages from './realWorldMessages.json'
+
+function visitNode(tree: ChatItemInTree | ChatItemInTree[], path: string): ChatItemInTree {
+  return get(tree, path)
+}
+
+describe('build chat item tree and get thread messages', () => {
+  const tree1 = buildChatItemTree(branchedTestMessages as ChatItemInTree[])
+
+  it('should build chat item tree1', () => {
+    const a1 = visitNode(tree1, '0.children.0')
+    expect(a1.id).toBe('1')
+    expect(a1.children).toHaveLength(2)
+
+    const a2 = visitNode(a1, 'children.0.children.0')
+    expect(a2.id).toBe('2')
+    expect(a2.siblingIndex).toBe(0)
+
+    const a3 = visitNode(a2, 'children.0.children.0')
+    expect(a3.id).toBe('3')
+
+    const a4 = visitNode(a1, 'children.1.children.0')
+    expect(a4.id).toBe('4')
+    expect(a4.siblingIndex).toBe(1)
+  })
+
+  it('should get thread messages from tree1, using the last message as the target', () => {
+    const threadChatItems1_1 = getThreadMessages(tree1)
+    expect(threadChatItems1_1).toHaveLength(4)
+
+    const q1 = visitNode(threadChatItems1_1, '0')
+    const a1 = visitNode(threadChatItems1_1, '1')
+    const q4 = visitNode(threadChatItems1_1, '2')
+    const a4 = visitNode(threadChatItems1_1, '3')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q4.id).toBe('question-4')
+    expect(a4.id).toBe('4')
+
+    expect(a4.siblingCount).toBe(2)
+    expect(a4.siblingIndex).toBe(1)
+  })
+
+  it('should get thread messages from tree1, using the message with id 3 as the target', () => {
+    const threadChatItems1_2 = getThreadMessages(tree1, '3')
+    expect(threadChatItems1_2).toHaveLength(6)
+
+    const q1 = visitNode(threadChatItems1_2, '0')
+    const a1 = visitNode(threadChatItems1_2, '1')
+    const q2 = visitNode(threadChatItems1_2, '2')
+    const a2 = visitNode(threadChatItems1_2, '3')
+    const q3 = visitNode(threadChatItems1_2, '4')
+    const a3 = visitNode(threadChatItems1_2, '5')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q2.id).toBe('question-2')
+    expect(a2.id).toBe('2')
+    expect(q3.id).toBe('question-3')
+    expect(a3.id).toBe('3')
+
+    expect(a2.siblingCount).toBe(2)
+    expect(a2.siblingIndex).toBe(0)
+  })
+
+  const tree2 = buildChatItemTree(legacyTestMessages as ChatItemInTree[])
+  it('should work with legacy chat items', () => {
+    expect(tree2).toHaveLength(1)
+    const q1 = visitNode(tree2, '0')
+    const a1 = visitNode(q1, 'children.0')
+    const q2 = visitNode(a1, 'children.0')
+    const a2 = visitNode(q2, 'children.0')
+    const q3 = visitNode(a2, 'children.0')
+    const a3 = visitNode(q3, 'children.0')
+    const q4 = visitNode(a3, 'children.0')
+    const a4 = visitNode(q4, 'children.0')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q2.id).toBe('question-2')
+    expect(a2.id).toBe('2')
+    expect(q3.id).toBe('question-3')
+    expect(a3.id).toBe('3')
+    expect(q4.id).toBe('question-4')
+    expect(a4.id).toBe('4')
+  })
+
+  it('should get thread messages from tree2, using the last message as the target', () => {
+    const threadMessages2 = getThreadMessages(tree2)
+    expect(threadMessages2).toHaveLength(8)
+
+    const q1 = visitNode(threadMessages2, '0')
+    const a1 = visitNode(threadMessages2, '1')
+    const q2 = visitNode(threadMessages2, '2')
+    const a2 = visitNode(threadMessages2, '3')
+    const q3 = visitNode(threadMessages2, '4')
+    const a3 = visitNode(threadMessages2, '5')
+    const q4 = visitNode(threadMessages2, '6')
+    const a4 = visitNode(threadMessages2, '7')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q2.id).toBe('question-2')
+    expect(a2.id).toBe('2')
+    expect(q3.id).toBe('question-3')
+    expect(a3.id).toBe('3')
+    expect(q4.id).toBe('question-4')
+    expect(a4.id).toBe('4')
+
+    expect(a1.siblingCount).toBe(1)
+    expect(a1.siblingIndex).toBe(0)
+    expect(a2.siblingCount).toBe(1)
+    expect(a2.siblingIndex).toBe(0)
+    expect(a3.siblingCount).toBe(1)
+    expect(a3.siblingIndex).toBe(0)
+    expect(a4.siblingCount).toBe(1)
+    expect(a4.siblingIndex).toBe(0)
+  })
+
+  const tree3 = buildChatItemTree(mixedTestMessages as ChatItemInTree[])
+  it('should build mixed chat items tree', () => {
+    expect(tree3).toHaveLength(1)
+
+    const a1 = visitNode(tree3, '0.children.0')
+    expect(a1.id).toBe('1')
+    expect(a1.children).toHaveLength(2)
+
+    const a2 = visitNode(a1, 'children.0.children.0')
+    expect(a2.id).toBe('2')
+    expect(a2.siblingIndex).toBe(0)
+
+    const a3 = visitNode(a2, 'children.0.children.0')
+    expect(a3.id).toBe('3')
+
+    const a4 = visitNode(a1, 'children.1.children.0')
+    expect(a4.id).toBe('4')
+    expect(a4.siblingIndex).toBe(1)
+  })
+
+  it('should get thread messages from tree3, using the last message as the target', () => {
+    const threadMessages3_1 = getThreadMessages(tree3)
+    expect(threadMessages3_1).toHaveLength(4)
+
+    const q1 = visitNode(threadMessages3_1, '0')
+    const a1 = visitNode(threadMessages3_1, '1')
+    const q4 = visitNode(threadMessages3_1, '2')
+    const a4 = visitNode(threadMessages3_1, '3')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q4.id).toBe('question-4')
+    expect(a4.id).toBe('4')
+
+    expect(a4.siblingCount).toBe(2)
+    expect(a4.siblingIndex).toBe(1)
+  })
+
+  it('should get thread messages from tree3, using the message with id 3 as the target', () => {
+    const threadMessages3_2 = getThreadMessages(tree3, '3')
+    expect(threadMessages3_2).toHaveLength(6)
+
+    const q1 = visitNode(threadMessages3_2, '0')
+    const a1 = visitNode(threadMessages3_2, '1')
+    const q2 = visitNode(threadMessages3_2, '2')
+    const a2 = visitNode(threadMessages3_2, '3')
+    const q3 = visitNode(threadMessages3_2, '4')
+    const a3 = visitNode(threadMessages3_2, '5')
+
+    expect(q1.id).toBe('question-1')
+    expect(a1.id).toBe('1')
+    expect(q2.id).toBe('question-2')
+    expect(a2.id).toBe('2')
+    expect(q3.id).toBe('question-3')
+    expect(a3.id).toBe('3')
+
+    expect(a2.siblingCount).toBe(2)
+    expect(a2.siblingIndex).toBe(0)
+  })
+
+  const tree4 = buildChatItemTree(multiRootNodesMessages as ChatItemInTree[])
+  it('should build multi root nodes chat items tree', () => {
+    expect(tree4).toHaveLength(2)
+
+    const a5 = visitNode(tree4, '1.children.0')
+    expect(a5.id).toBe('5')
+    expect(a5.siblingIndex).toBe(1)
+  })
+
+  it('should get thread messages from tree4, using the last message as the target', () => {
+    const threadMessages4 = getThreadMessages(tree4)
+    expect(threadMessages4).toHaveLength(2)
+
+    const a1 = visitNode(threadMessages4, '0.children.0')
+    expect(a1.id).toBe('5')
+  })
+
+  it('should get thread messages from tree4, using the message with id 2 as the target', () => {
+    const threadMessages4_1 = getThreadMessages(tree4, '2')
+    expect(threadMessages4_1).toHaveLength(6)
+    const a1 = visitNode(threadMessages4_1, '1')
+    expect(a1.id).toBe('1')
+    const a2 = visitNode(threadMessages4_1, '3')
+    expect(a2.id).toBe('2')
+    const a3 = visitNode(threadMessages4_1, '5')
+    expect(a3.id).toBe('3')
+  })
+
+  const tree5 = buildChatItemTree(multiRootNodesWithLegacyTestMessages as ChatItemInTree[])
+  it('should work with multi root nodes chat items with legacy chat items', () => {
+    expect(tree5).toHaveLength(2)
+
+    const q5 = visitNode(tree5, '1')
+    expect(q5.id).toBe('question-5')
+    expect(q5.parentMessageId).toBe(null)
+
+    const a5 = visitNode(q5, 'children.0')
+    expect(a5.id).toBe('5')
+    expect(a5.children).toHaveLength(0)
+  })
+
+  it('should get thread messages from tree5, using the last message as the target', () => {
+    const threadMessages5 = getThreadMessages(tree5)
+    expect(threadMessages5).toHaveLength(2)
+
+    const q5 = visitNode(threadMessages5, '0')
+    const a5 = visitNode(threadMessages5, '1')
+
+    expect(q5.id).toBe('question-5')
+    expect(a5.id).toBe('5')
+
+    expect(a5.siblingCount).toBe(2)
+    expect(a5.siblingIndex).toBe(1)
+  })
+
+  const tree6 = buildChatItemTree(realWorldMessages as ChatItemInTree[])
+  it('should work with real world messages', () => {
+    expect(tree6).toMatchSnapshot()
+  })
+
+  it ('should get thread messages from tree6, using the last message as target', () => {
+    const threadMessages6_1 = getThreadMessages(tree6)
+    expect(threadMessages6_1).toMatchSnapshot()
+  })
+
+  it ('should get thread messages from tree6, using specified message as target', () => {
+    const threadMessages6_2 = getThreadMessages(tree6, 'ff4c2b43-48a5-47ad-9dc5-08b34ddba61b')
+    expect(threadMessages6_2).toMatchSnapshot()
+  })
+})

+ 20 - 0
web/app/components/base/chat/chat/answer/index.tsx

@@ -19,6 +19,7 @@ import Citation from '@/app/components/base/chat/chat/citation'
 import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
 import type { AppData } from '@/models/share'
 import AnswerIcon from '@/app/components/base/answer-icon'
+import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
 import cn from '@/utils/classnames'
 import { FileList } from '@/app/components/base/file-uploader'
 
@@ -34,6 +35,7 @@ type AnswerProps = {
   hideProcessDetail?: boolean
   appData?: AppData
   noChatInput?: boolean
+  switchSibling?: (siblingMessageId: string) => void
 }
 const Answer: FC<AnswerProps> = ({
   item,
@@ -47,6 +49,7 @@ const Answer: FC<AnswerProps> = ({
   hideProcessDetail,
   appData,
   noChatInput,
+  switchSibling,
 }) => {
   const { t } = useTranslation()
   const {
@@ -203,6 +206,23 @@ const Answer: FC<AnswerProps> = ({
                 <Citation data={citation} showHitInfo={config?.supportCitationHitInfo} />
               )
             }
+            {item.siblingCount && item.siblingCount > 1 && item.siblingIndex !== undefined && <div className="pt-3.5 flex justify-center items-center text-sm">
+              <button
+                className={`${item.prevSibling ? 'opacity-100' : 'opacity-65'}`}
+                disabled={!item.prevSibling}
+                onClick={() => item.prevSibling && switchSibling?.(item.prevSibling)}
+              >
+                <ChevronRight className="w-[14px] h-[14px] rotate-180 text-gray-500" />
+              </button>
+              <span className="px-2 text-xs text-gray-700">{item.siblingIndex + 1} / {item.siblingCount}</span>
+              <button
+                className={`${item.nextSibling ? 'opacity-100' : 'opacity-65'}`}
+                disabled={!item.nextSibling}
+                onClick={() => item.nextSibling && switchSibling?.(item.nextSibling)}
+              >
+                <ChevronRight className="w-[14px] h-[14px] text-gray-500" />
+              </button>
+            </div>}
           </div>
         </div>
         <More more={more} />

+ 3 - 0
web/app/components/base/chat/chat/index.tsx

@@ -65,6 +65,7 @@ export type ChatProps = {
   hideProcessDetail?: boolean
   hideLogModal?: boolean
   themeBuilder?: ThemeBuilder
+  switchSibling?: (siblingMessageId: string) => void
   showFeatureBar?: boolean
   showFileUpload?: boolean
   onFeatureBarClick?: (state: boolean) => void
@@ -100,6 +101,7 @@ const Chat: FC<ChatProps> = ({
   hideProcessDetail,
   hideLogModal,
   themeBuilder,
+  switchSibling,
   showFeatureBar,
   showFileUpload,
   onFeatureBarClick,
@@ -232,6 +234,7 @@ const Chat: FC<ChatProps> = ({
                       chatAnswerContainerInner={chatAnswerContainerInner}
                       hideProcessDetail={hideProcessDetail}
                       noChatInput={noChatInput}
+                      switchSibling={switchSibling}
                     />
                   )
                 }

+ 5 - 1
web/app/components/base/chat/chat/type.ts

@@ -97,7 +97,11 @@ export type IChatItem = {
   // for agent log
   conversationId?: string
   input?: any
-  parentMessageId?: string
+  parentMessageId?: string | null
+  siblingCount?: number
+  siblingIndex?: number
+  prevSibling?: string
+  nextSibling?: string
 }
 
 export type Metadata = {

+ 4 - 0
web/app/components/base/chat/types.ts

@@ -65,6 +65,10 @@ export type ChatItem = IChatItem & {
   allFiles?: FileEntity[]
 }
 
+export type ChatItemInTree = {
+  children?: ChatItemInTree[]
+} & IChatItem
+
 export type OnSend = (message: string, files?: FileEntity[], last_answer?: ChatItem | null) => void
 
 export type OnRegenerate = (chatItem: ChatItem) => void

+ 126 - 2
web/app/components/base/chat/utils.ts

@@ -1,6 +1,7 @@
 import { addFileInfos, sortAgentSorts } from '../../tools/utils'
 import { UUID_NIL } from './constants'
-import type { ChatItem } from './types'
+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) {
@@ -81,8 +82,131 @@ function getPrevChatList(fetchedMessages: any[]) {
   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)
+    }
+  }
+
+  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,
-  getLastAnswer,
   getPrevChatList,
+  getLastAnswer,
+  buildChatItemTree,
+  getThreadMessages,
 }

+ 41 - 46
web/app/components/workflow/panel/chat-record/index.tsx

@@ -12,59 +12,44 @@ import {
 import { useWorkflowRun } from '../../hooks'
 import UserInput from './user-input'
 import Chat from '@/app/components/base/chat/chat'
-import type { ChatItem } from '@/app/components/base/chat/types'
+import type { ChatItem, ChatItemInTree } from '@/app/components/base/chat/types'
 import { fetchConversationMessages } from '@/service/debug'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import Loading from '@/app/components/base/loading'
-import { UUID_NIL } from '@/app/components/base/chat/constants'
 import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
-
-function appendQAToChatList(newChatList: ChatItem[], item: any) {
-  const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
-  newChatList.push({
-    id: item.id,
-    content: item.answer,
-    feedback: item.feedback,
-    isAnswer: true,
-    citation: item.metadata?.retriever_resources,
-    message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
-    workflow_run_id: item.workflow_run_id,
-  })
-  const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
-  newChatList.push({
-    id: `question-${item.id}`,
-    content: item.query,
-    isAnswer: false,
-    message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
-  })
-}
+import type { IChatItem } from '@/app/components/base/chat/chat/type'
+import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
 
 function getFormattedChatList(messages: any[]) {
-  const newChatList: ChatItem[] = []
-  let nextMessageId = null
-  for (const item of messages) {
-    if (!item.parent_message_id) {
-      appendQAToChatList(newChatList, item)
-      break
-    }
-
-    if (!nextMessageId) {
-      appendQAToChatList(newChatList, item)
-      nextMessageId = item.parent_message_id
-    }
-    else {
-      if (item.id === nextMessageId || nextMessageId === UUID_NIL) {
-        appendQAToChatList(newChatList, item)
-        nextMessageId = item.parent_message_id
-      }
-    }
-  }
-  return newChatList.reverse()
+  const res: ChatItem[] = []
+  messages.forEach((item: any) => {
+    const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || []
+    res.push({
+      id: `question-${item.id}`,
+      content: item.query,
+      isAnswer: false,
+      message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
+      parentMessageId: item.parent_message_id || undefined,
+    })
+    const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
+    res.push({
+      id: item.id,
+      content: item.answer,
+      feedback: item.feedback,
+      isAnswer: true,
+      citation: item.metadata?.retriever_resources,
+      message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
+      workflow_run_id: item.workflow_run_id,
+      parentMessageId: `question-${item.id}`,
+    })
+  })
+  return res
 }
 
 const ChatRecord = () => {
   const [fetched, setFetched] = useState(false)
-  const [chatList, setChatList] = useState<ChatItem[]>([])
+  const [chatItemTree, setChatItemTree] = useState<ChatItemInTree[]>([])
+  const [threadChatItems, setThreadChatItems] = useState<IChatItem[]>([])
   const appDetail = useAppStore(s => s.appDetail)
   const workflowStore = useWorkflowStore()
   const { handleLoadBackupDraft } = useWorkflowRun()
@@ -76,20 +61,29 @@ const ChatRecord = () => {
       try {
         setFetched(false)
         const res = await fetchConversationMessages(appDetail.id, currentConversationID)
-        setChatList(getFormattedChatList((res as any).data))
+
+        const newAllChatItems = getFormattedChatList((res as any).data)
+
+        const tree = buildChatItemTree(newAllChatItems)
+        setChatItemTree(tree)
+        setThreadChatItems(getThreadMessages(tree, newAllChatItems.at(-1)?.id))
       }
       catch (e) {
-        console.error(e)
       }
       finally {
         setFetched(true)
       }
     }
   }, [appDetail, currentConversationID])
+
   useEffect(() => {
     handleFetchConversationMessages()
   }, [currentConversationID, appDetail, handleFetchConversationMessages])
 
+  const switchSibling = useCallback((siblingMessageId: string) => {
+    setThreadChatItems(getThreadMessages(chatItemTree, siblingMessageId))
+  }, [chatItemTree])
+
   return (
     <div
       className={`
@@ -123,7 +117,7 @@ const ChatRecord = () => {
               config={{
                 supportCitationHitInfo: true,
               } as any}
-              chatList={chatList}
+              chatList={threadChatItems}
               chatContainerClassName='px-3'
               chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto'
               chatFooterClassName='px-4 rounded-b-2xl'
@@ -132,6 +126,7 @@ const ChatRecord = () => {
               noChatInput
               allToolIcons={{}}
               showPromptLog
+              switchSibling={switchSibling}
               noSpacing
               chatAnswerContainerInner='!pr-2'
             />

+ 3 - 0
web/babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: ['@babel/preset-env'],
+}

+ 1 - 0
web/jest.config.ts

@@ -99,6 +99,7 @@ const config: Config = {
   // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
   moduleNameMapper: {
     '^@/components/(.*)$': '<rootDir>/components/$1',
+    '^lodash-es$': 'lodash',
   },
 
   // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader