diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py index 2fba3e0af0..fe06201982 100644 --- a/api/controllers/console/app/message.py +++ b/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) diff --git a/web/__mocks__/mime.js b/web/__mocks__/mime.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 6d643a01a3..af678b0f9e 100644 --- a/web/app/components/app/log/list.tsx +++ b/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([]) - const fetchedMessages = useRef([]) const [hasMore, setHasMore] = useState(true) const [varValues, setVarValues] = useState>({}) - const fetchData = async () => { + + const [allChatItems, setAllChatItems] = useState([]) + const [chatItemTree, setChatItemTree] = useState([]) + const [threadChatItems, setThreadChatItems] = useState([]) + + 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} /> - : (items.length < 8 && !hasMore) + : threadChatItems.length < 8 ?
:
{t('appLog.detail.loading')}...
} @@ -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} /> diff --git a/web/app/components/base/chat/__tests__/__snapshots__/utils.spec.ts.snap b/web/app/components/base/chat/__tests__/__snapshots__/utils.spec.ts.snap new file mode 100644 index 0000000000..070975bfa7 --- /dev/null +++ b/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": [], + }, +] +`; diff --git a/web/app/components/base/chat/__tests__/branchedTestMessages.json b/web/app/components/base/chat/__tests__/branchedTestMessages.json new file mode 100644 index 0000000000..30e0a82cb5 --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/legacyTestMessages.json b/web/app/components/base/chat/__tests__/legacyTestMessages.json new file mode 100644 index 0000000000..2dab58985a --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/mixedTestMessages.json b/web/app/components/base/chat/__tests__/mixedTestMessages.json new file mode 100644 index 0000000000..14789d9518 --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/multiRootNodesMessages.json b/web/app/components/base/chat/__tests__/multiRootNodesMessages.json new file mode 100644 index 0000000000..782ccb7f94 --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/multiRootNodesWithLegacyTestMessages.json b/web/app/components/base/chat/__tests__/multiRootNodesWithLegacyTestMessages.json new file mode 100644 index 0000000000..5eadc726e5 --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/realWorldMessages.json b/web/app/components/base/chat/__tests__/realWorldMessages.json new file mode 100644 index 0000000000..858052c77f --- /dev/null +++ b/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" + } +] diff --git a/web/app/components/base/chat/__tests__/utils.spec.ts b/web/app/components/base/chat/__tests__/utils.spec.ts new file mode 100644 index 0000000000..c602ac8a99 --- /dev/null +++ b/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() + }) +}) diff --git a/web/app/components/base/chat/chat/answer/index.tsx b/web/app/components/base/chat/chat/answer/index.tsx index 50f51f521f..1ff390bd58 100644 --- a/web/app/components/base/chat/chat/answer/index.tsx +++ b/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 = ({ item, @@ -47,6 +49,7 @@ const Answer: FC = ({ hideProcessDetail, appData, noChatInput, + switchSibling, }) => { const { t } = useTranslation() const { @@ -203,6 +206,23 @@ const Answer: FC = ({ ) } + {item.siblingCount && item.siblingCount > 1 && item.siblingIndex !== undefined &&
+ + {item.siblingIndex + 1} / {item.siblingCount} + +
} diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index 742632a1ad..22020066b4 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/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 = ({ hideProcessDetail, hideLogModal, themeBuilder, + switchSibling, showFeatureBar, showFileUpload, onFeatureBarClick, @@ -232,6 +234,7 @@ const Chat: FC = ({ chatAnswerContainerInner={chatAnswerContainerInner} hideProcessDetail={hideProcessDetail} noChatInput={noChatInput} + switchSibling={switchSibling} /> ) } diff --git a/web/app/components/base/chat/chat/type.ts b/web/app/components/base/chat/chat/type.ts index 40cc32e859..7f22ba05b7 100644 --- a/web/app/components/base/chat/chat/type.ts +++ b/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 = { diff --git a/web/app/components/base/chat/types.ts b/web/app/components/base/chat/types.ts index 402392ac2a..8d9dacdcd7 100644 --- a/web/app/components/base/chat/types.ts +++ b/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 diff --git a/web/app/components/base/chat/utils.ts b/web/app/components/base/chat/utils.ts index 3840f6a2b8..9205225f9f 100644 --- a/web/app/components/base/chat/utils.ts +++ b/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 = {} + const rootNodes: ChatItemInTree[] = [] + const childrenCount: Record = {} + + 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, } diff --git a/web/app/components/workflow/panel/chat-record/index.tsx b/web/app/components/workflow/panel/chat-record/index.tsx index 2786310288..2c76e89b0c 100644 --- a/web/app/components/workflow/panel/chat-record/index.tsx +++ b/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([]) + const [chatItemTree, setChatItemTree] = useState([]) + const [threadChatItems, setThreadChatItems] = useState([]) 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 (
{ 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' /> diff --git a/web/babel.config.js b/web/babel.config.js new file mode 100644 index 0000000000..e6196ef3d3 --- /dev/null +++ b/web/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['@babel/preset-env'], +} diff --git a/web/jest.config.ts b/web/jest.config.ts index d7c68308cb..232f90252d 100644 --- a/web/jest.config.ts +++ b/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/(.*)$': '/components/$1', + '^lodash-es$': 'lodash', }, // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader