|
@@ -1,6 +1,7 @@
|
|
|
import {
|
|
|
useCallback,
|
|
|
useEffect,
|
|
|
+ useMemo,
|
|
|
useRef,
|
|
|
useState,
|
|
|
} from 'react'
|
|
@@ -12,8 +13,10 @@ import { v4 as uuidV4 } from 'uuid'
|
|
|
import type {
|
|
|
ChatConfig,
|
|
|
ChatItem,
|
|
|
+ ChatItemInTree,
|
|
|
Inputs,
|
|
|
} from '../types'
|
|
|
+import { getThreadMessages } from '../utils'
|
|
|
import type { InputForm } from './type'
|
|
|
import {
|
|
|
getProcessedInputs,
|
|
@@ -46,7 +49,7 @@ export const useChat = (
|
|
|
inputs: Inputs
|
|
|
inputsForm: InputForm[]
|
|
|
},
|
|
|
- prevChatList?: ChatItem[],
|
|
|
+ prevChatTree?: ChatItemInTree[],
|
|
|
stopChat?: (taskId: string) => void,
|
|
|
) => {
|
|
|
const { t } = useTranslation()
|
|
@@ -56,14 +59,48 @@ export const useChat = (
|
|
|
const hasStopResponded = useRef(false)
|
|
|
const [isResponding, setIsResponding] = useState(false)
|
|
|
const isRespondingRef = useRef(false)
|
|
|
- const [chatList, setChatList] = useState<ChatItem[]>(prevChatList || [])
|
|
|
- const chatListRef = useRef<ChatItem[]>(prevChatList || [])
|
|
|
const taskIdRef = useRef('')
|
|
|
const [suggestedQuestions, setSuggestQuestions] = useState<string[]>([])
|
|
|
const conversationMessagesAbortControllerRef = useRef<AbortController | null>(null)
|
|
|
const suggestedQuestionsAbortControllerRef = useRef<AbortController | null>(null)
|
|
|
const params = useParams()
|
|
|
const pathname = usePathname()
|
|
|
+
|
|
|
+ const [chatTree, setChatTree] = useState<ChatItemInTree[]>(prevChatTree || [])
|
|
|
+ const chatTreeRef = useRef<ChatItemInTree[]>(chatTree)
|
|
|
+ const [targetMessageId, setTargetMessageId] = useState<string>()
|
|
|
+ const threadMessages = useMemo(() => getThreadMessages(chatTree, targetMessageId), [chatTree, targetMessageId])
|
|
|
+
|
|
|
+ const getIntroduction = useCallback((str: string) => {
|
|
|
+ return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
|
|
|
+ }, [formSettings?.inputs, formSettings?.inputsForm])
|
|
|
+
|
|
|
+ /** Final chat list that will be rendered */
|
|
|
+ const chatList = useMemo(() => {
|
|
|
+ const ret = [...threadMessages]
|
|
|
+ if (config?.opening_statement) {
|
|
|
+ const index = threadMessages.findIndex(item => item.isOpeningStatement)
|
|
|
+
|
|
|
+ if (index > -1) {
|
|
|
+ ret[index] = {
|
|
|
+ ...ret[index],
|
|
|
+ content: getIntroduction(config.opening_statement),
|
|
|
+ suggestedQuestions: config.suggested_questions,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ret.unshift({
|
|
|
+ id: `${Date.now()}`,
|
|
|
+ content: getIntroduction(config.opening_statement),
|
|
|
+ isAnswer: true,
|
|
|
+ isOpeningStatement: true,
|
|
|
+ suggestedQuestions: config.suggested_questions,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ret
|
|
|
+ }, [threadMessages, config?.opening_statement, getIntroduction, config?.suggested_questions])
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
setAutoFreeze(false)
|
|
|
return () => {
|
|
@@ -71,43 +108,50 @@ export const useChat = (
|
|
|
}
|
|
|
}, [])
|
|
|
|
|
|
- const handleUpdateChatList = useCallback((newChatList: ChatItem[]) => {
|
|
|
- setChatList(newChatList)
|
|
|
- chatListRef.current = newChatList
|
|
|
+ /** Find the target node by bfs and then operate on it */
|
|
|
+ const produceChatTreeNode = useCallback((targetId: string, operation: (node: ChatItemInTree) => void) => {
|
|
|
+ return produce(chatTreeRef.current, (draft) => {
|
|
|
+ const queue: ChatItemInTree[] = [...draft]
|
|
|
+ while (queue.length > 0) {
|
|
|
+ const current = queue.shift()!
|
|
|
+ if (current.id === targetId) {
|
|
|
+ operation(current)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if (current.children)
|
|
|
+ queue.push(...current.children)
|
|
|
+ }
|
|
|
+ })
|
|
|
}, [])
|
|
|
+
|
|
|
+ type UpdateChatTreeNode = {
|
|
|
+ (id: string, fields: Partial<ChatItemInTree>): void
|
|
|
+ (id: string, update: (node: ChatItemInTree) => void): void
|
|
|
+ }
|
|
|
+
|
|
|
+ const updateChatTreeNode: UpdateChatTreeNode = useCallback((
|
|
|
+ id: string,
|
|
|
+ fieldsOrUpdate: Partial<ChatItemInTree> | ((node: ChatItemInTree) => void),
|
|
|
+ ) => {
|
|
|
+ const nextState = produceChatTreeNode(id, (node) => {
|
|
|
+ if (typeof fieldsOrUpdate === 'function') {
|
|
|
+ fieldsOrUpdate(node)
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Object.keys(fieldsOrUpdate).forEach((key) => {
|
|
|
+ (node as any)[key] = (fieldsOrUpdate as any)[key]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ setChatTree(nextState)
|
|
|
+ chatTreeRef.current = nextState
|
|
|
+ }, [produceChatTreeNode])
|
|
|
+
|
|
|
const handleResponding = useCallback((isResponding: boolean) => {
|
|
|
setIsResponding(isResponding)
|
|
|
isRespondingRef.current = isResponding
|
|
|
}, [])
|
|
|
|
|
|
- const getIntroduction = useCallback((str: string) => {
|
|
|
- return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
|
|
|
- }, [formSettings?.inputs, formSettings?.inputsForm])
|
|
|
- useEffect(() => {
|
|
|
- if (config?.opening_statement) {
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const index = draft.findIndex(item => item.isOpeningStatement)
|
|
|
-
|
|
|
- if (index > -1) {
|
|
|
- draft[index] = {
|
|
|
- ...draft[index],
|
|
|
- content: getIntroduction(config.opening_statement),
|
|
|
- suggestedQuestions: config.suggested_questions,
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- draft.unshift({
|
|
|
- id: `${Date.now()}`,
|
|
|
- content: getIntroduction(config.opening_statement),
|
|
|
- isAnswer: true,
|
|
|
- isOpeningStatement: true,
|
|
|
- suggestedQuestions: config.suggested_questions,
|
|
|
- })
|
|
|
- }
|
|
|
- }))
|
|
|
- }
|
|
|
- }, [config?.opening_statement, getIntroduction, config?.suggested_questions, handleUpdateChatList])
|
|
|
-
|
|
|
const handleStop = useCallback(() => {
|
|
|
hasStopResponded.current = true
|
|
|
handleResponding(false)
|
|
@@ -123,50 +167,50 @@ export const useChat = (
|
|
|
conversationId.current = ''
|
|
|
taskIdRef.current = ''
|
|
|
handleStop()
|
|
|
- const newChatList = config?.opening_statement
|
|
|
- ? [{
|
|
|
- id: `${Date.now()}`,
|
|
|
- content: config.opening_statement,
|
|
|
- isAnswer: true,
|
|
|
- isOpeningStatement: true,
|
|
|
- suggestedQuestions: config.suggested_questions,
|
|
|
- }]
|
|
|
- : []
|
|
|
- handleUpdateChatList(newChatList)
|
|
|
+ setChatTree([])
|
|
|
setSuggestQuestions([])
|
|
|
- }, [
|
|
|
- config,
|
|
|
- handleStop,
|
|
|
- handleUpdateChatList,
|
|
|
- ])
|
|
|
+ }, [handleStop])
|
|
|
|
|
|
- const updateCurrentQA = useCallback(({
|
|
|
+ const updateCurrentQAOnTree = useCallback(({
|
|
|
+ parentId,
|
|
|
responseItem,
|
|
|
- questionId,
|
|
|
- placeholderAnswerId,
|
|
|
+ placeholderQuestionId,
|
|
|
questionItem,
|
|
|
}: {
|
|
|
+ parentId?: string
|
|
|
responseItem: ChatItem
|
|
|
- questionId: string
|
|
|
- placeholderAnswerId: string
|
|
|
+ placeholderQuestionId: string
|
|
|
questionItem: ChatItem
|
|
|
}) => {
|
|
|
- const newListWithAnswer = produce(
|
|
|
- chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
|
|
|
- (draft) => {
|
|
|
- if (!draft.find(item => item.id === questionId))
|
|
|
- draft.push({ ...questionItem })
|
|
|
-
|
|
|
- draft.push({ ...responseItem })
|
|
|
+ let nextState: ChatItemInTree[]
|
|
|
+ const currentQA = { ...questionItem, children: [{ ...responseItem, children: [] }] }
|
|
|
+ if (!parentId && !chatTree.some(item => [placeholderQuestionId, questionItem.id].includes(item.id))) {
|
|
|
+ // QA whose parent is not provided is considered as a first message of the conversation,
|
|
|
+ // and it should be a root node of the chat tree
|
|
|
+ nextState = produce(chatTree, (draft) => {
|
|
|
+ draft.push(currentQA)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // find the target QA in the tree and update it; if not found, insert it to its parent node
|
|
|
+ nextState = produceChatTreeNode(parentId!, (parentNode) => {
|
|
|
+ const questionNodeIndex = parentNode.children!.findIndex(item => [placeholderQuestionId, questionItem.id].includes(item.id))
|
|
|
+ if (questionNodeIndex === -1)
|
|
|
+ parentNode.children!.push(currentQA)
|
|
|
+ else
|
|
|
+ parentNode.children![questionNodeIndex] = currentQA
|
|
|
})
|
|
|
- handleUpdateChatList(newListWithAnswer)
|
|
|
- }, [handleUpdateChatList])
|
|
|
+ }
|
|
|
+ setChatTree(nextState)
|
|
|
+ chatTreeRef.current = nextState
|
|
|
+ }, [chatTree, produceChatTreeNode])
|
|
|
|
|
|
const handleSend = useCallback(async (
|
|
|
url: string,
|
|
|
data: {
|
|
|
query: string
|
|
|
files?: FileEntity[]
|
|
|
+ parent_message_id?: string
|
|
|
[key: string]: any
|
|
|
},
|
|
|
{
|
|
@@ -183,12 +227,15 @@ export const useChat = (
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
- const questionId = `question-${Date.now()}`
|
|
|
+ const parentMessage = threadMessages.find(item => item.id === data.parent_message_id)
|
|
|
+
|
|
|
+ const placeholderQuestionId = `question-${Date.now()}`
|
|
|
const questionItem = {
|
|
|
- id: questionId,
|
|
|
+ id: placeholderQuestionId,
|
|
|
content: data.query,
|
|
|
isAnswer: false,
|
|
|
message_files: data.files,
|
|
|
+ parentMessageId: data.parent_message_id,
|
|
|
}
|
|
|
|
|
|
const placeholderAnswerId = `answer-placeholder-${Date.now()}`
|
|
@@ -196,18 +243,27 @@ export const useChat = (
|
|
|
id: placeholderAnswerId,
|
|
|
content: '',
|
|
|
isAnswer: true,
|
|
|
+ parentMessageId: questionItem.id,
|
|
|
+ siblingIndex: parentMessage?.children?.length ?? chatTree.length,
|
|
|
}
|
|
|
|
|
|
- const newList = [...chatListRef.current, questionItem, placeholderAnswerItem]
|
|
|
- handleUpdateChatList(newList)
|
|
|
+ setTargetMessageId(parentMessage?.id)
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ responseItem: placeholderAnswerItem,
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ })
|
|
|
|
|
|
// answer
|
|
|
- const responseItem: ChatItem = {
|
|
|
+ const responseItem: ChatItemInTree = {
|
|
|
id: placeholderAnswerId,
|
|
|
content: '',
|
|
|
agent_thoughts: [],
|
|
|
message_files: [],
|
|
|
isAnswer: true,
|
|
|
+ parentMessageId: questionItem.id,
|
|
|
+ siblingIndex: parentMessage?.children?.length ?? chatTree.length,
|
|
|
}
|
|
|
|
|
|
handleResponding(true)
|
|
@@ -268,7 +324,9 @@ export const useChat = (
|
|
|
}
|
|
|
|
|
|
if (messageId && !hasSetResponseId) {
|
|
|
+ questionItem.id = `question-${messageId}`
|
|
|
responseItem.id = messageId
|
|
|
+ responseItem.parentMessageId = questionItem.id
|
|
|
hasSetResponseId = true
|
|
|
}
|
|
|
|
|
@@ -279,11 +337,11 @@ export const useChat = (
|
|
|
if (messageId)
|
|
|
responseItem.id = messageId
|
|
|
|
|
|
- updateCurrentQA({
|
|
|
- responseItem,
|
|
|
- questionId,
|
|
|
- placeholderAnswerId,
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
})
|
|
|
},
|
|
|
async onCompleted(hasError?: boolean) {
|
|
@@ -304,43 +362,32 @@ export const useChat = (
|
|
|
if (!newResponseItem)
|
|
|
return
|
|
|
|
|
|
- const newChatList = produce(chatListRef.current, (draft) => {
|
|
|
- const index = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- if (index !== -1) {
|
|
|
- const question = draft[index - 1]
|
|
|
- draft[index - 1] = {
|
|
|
- ...question,
|
|
|
- }
|
|
|
- draft[index] = {
|
|
|
- ...draft[index],
|
|
|
- content: newResponseItem.answer,
|
|
|
- log: [
|
|
|
- ...newResponseItem.message,
|
|
|
- ...(newResponseItem.message[newResponseItem.message.length - 1].role !== 'assistant'
|
|
|
- ? [
|
|
|
- {
|
|
|
- role: 'assistant',
|
|
|
- text: newResponseItem.answer,
|
|
|
- files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
|
|
|
- },
|
|
|
- ]
|
|
|
- : []),
|
|
|
- ],
|
|
|
- more: {
|
|
|
- time: formatTime(newResponseItem.created_at, 'hh:mm A'),
|
|
|
- tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens,
|
|
|
- latency: newResponseItem.provider_response_latency.toFixed(2),
|
|
|
- },
|
|
|
- // for agent log
|
|
|
- conversationId: conversationId.current,
|
|
|
- input: {
|
|
|
- inputs: newResponseItem.inputs,
|
|
|
- query: newResponseItem.query,
|
|
|
- },
|
|
|
- }
|
|
|
- }
|
|
|
+ updateChatTreeNode(responseItem.id, {
|
|
|
+ content: newResponseItem.answer,
|
|
|
+ log: [
|
|
|
+ ...newResponseItem.message,
|
|
|
+ ...(newResponseItem.message[newResponseItem.message.length - 1].role !== 'assistant'
|
|
|
+ ? [
|
|
|
+ {
|
|
|
+ role: 'assistant',
|
|
|
+ text: newResponseItem.answer,
|
|
|
+ files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ : []),
|
|
|
+ ],
|
|
|
+ more: {
|
|
|
+ time: formatTime(newResponseItem.created_at, 'hh:mm A'),
|
|
|
+ tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens,
|
|
|
+ latency: newResponseItem.provider_response_latency.toFixed(2),
|
|
|
+ },
|
|
|
+ // for agent log
|
|
|
+ conversationId: conversationId.current,
|
|
|
+ input: {
|
|
|
+ inputs: newResponseItem.inputs,
|
|
|
+ query: newResponseItem.query,
|
|
|
+ },
|
|
|
})
|
|
|
- handleUpdateChatList(newChatList)
|
|
|
}
|
|
|
if (config?.suggested_questions_after_answer?.enabled && !hasStopResponded.current && onGetSuggestedQuestions) {
|
|
|
try {
|
|
@@ -360,11 +407,11 @@ export const useChat = (
|
|
|
if (lastThought)
|
|
|
responseItem.agent_thoughts![responseItem.agent_thoughts!.length - 1].message_files = [...(lastThought as any).message_files, file]
|
|
|
|
|
|
- updateCurrentQA({
|
|
|
- responseItem,
|
|
|
- questionId,
|
|
|
- placeholderAnswerId,
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
})
|
|
|
},
|
|
|
onThought(thought) {
|
|
@@ -372,6 +419,7 @@ export const useChat = (
|
|
|
const response = responseItem as any
|
|
|
if (thought.message_id && !hasSetResponseId)
|
|
|
response.id = thought.message_id
|
|
|
+
|
|
|
if (response.agent_thoughts.length === 0) {
|
|
|
response.agent_thoughts.push(thought)
|
|
|
}
|
|
@@ -387,11 +435,11 @@ export const useChat = (
|
|
|
responseItem.agent_thoughts!.push(thought)
|
|
|
}
|
|
|
}
|
|
|
- updateCurrentQA({
|
|
|
- responseItem,
|
|
|
- questionId,
|
|
|
- placeholderAnswerId,
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
})
|
|
|
},
|
|
|
onMessageEnd: (messageEnd) => {
|
|
@@ -401,43 +449,36 @@ export const useChat = (
|
|
|
id: messageEnd.metadata.annotation_reply.id,
|
|
|
authorName: messageEnd.metadata.annotation_reply.account.name,
|
|
|
})
|
|
|
- const baseState = chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId)
|
|
|
- const newListWithAnswer = produce(
|
|
|
- baseState,
|
|
|
- (draft) => {
|
|
|
- if (!draft.find(item => item.id === questionId))
|
|
|
- draft.push({ ...questionItem })
|
|
|
-
|
|
|
- draft.push({
|
|
|
- ...responseItem,
|
|
|
- })
|
|
|
- })
|
|
|
- handleUpdateChatList(newListWithAnswer)
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
return
|
|
|
}
|
|
|
responseItem.citation = messageEnd.metadata?.retriever_resources || []
|
|
|
const processedFilesFromResponse = getProcessedFilesFromResponse(messageEnd.files || [])
|
|
|
responseItem.allFiles = uniqBy([...(responseItem.allFiles || []), ...(processedFilesFromResponse || [])], 'id')
|
|
|
|
|
|
- const newListWithAnswer = produce(
|
|
|
- chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
|
|
|
- (draft) => {
|
|
|
- if (!draft.find(item => item.id === questionId))
|
|
|
- draft.push({ ...questionItem })
|
|
|
-
|
|
|
- draft.push({ ...responseItem })
|
|
|
- })
|
|
|
- handleUpdateChatList(newListWithAnswer)
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
onMessageReplace: (messageReplace) => {
|
|
|
responseItem.content = messageReplace.answer
|
|
|
},
|
|
|
onError() {
|
|
|
handleResponding(false)
|
|
|
- const newChatList = produce(chatListRef.current, (draft) => {
|
|
|
- draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
})
|
|
|
- handleUpdateChatList(newChatList)
|
|
|
},
|
|
|
onWorkflowStarted: ({ workflow_run_id, task_id }) => {
|
|
|
taskIdRef.current = task_id
|
|
@@ -446,89 +487,84 @@ export const useChat = (
|
|
|
status: WorkflowRunningStatus.Running,
|
|
|
tracing: [],
|
|
|
}
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
- onWorkflowFinished: ({ data }) => {
|
|
|
- responseItem.workflowProcess!.status = data.status as WorkflowRunningStatus
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
+ onWorkflowFinished: ({ data: workflowFinishedData }) => {
|
|
|
+ responseItem.workflowProcess!.status = workflowFinishedData.status as WorkflowRunningStatus
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
- onIterationStart: ({ data }) => {
|
|
|
+ onIterationStart: ({ data: iterationStartedData }) => {
|
|
|
responseItem.workflowProcess!.tracing!.push({
|
|
|
- ...data,
|
|
|
+ ...iterationStartedData,
|
|
|
status: WorkflowRunningStatus.Running,
|
|
|
} as any)
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
- onIterationFinish: ({ data }) => {
|
|
|
+ onIterationFinish: ({ data: iterationFinishedData }) => {
|
|
|
const tracing = responseItem.workflowProcess!.tracing!
|
|
|
- const iterationIndex = tracing.findIndex(item => item.node_id === data.node_id
|
|
|
- && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
|
|
|
+ const iterationIndex = tracing.findIndex(item => item.node_id === iterationFinishedData.node_id
|
|
|
+ && (item.execution_metadata?.parallel_id === iterationFinishedData.execution_metadata?.parallel_id || item.parallel_id === iterationFinishedData.execution_metadata?.parallel_id))!
|
|
|
tracing[iterationIndex] = {
|
|
|
...tracing[iterationIndex],
|
|
|
- ...data,
|
|
|
+ ...iterationFinishedData,
|
|
|
status: WorkflowRunningStatus.Succeeded,
|
|
|
} as any
|
|
|
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
- onNodeStarted: ({ data }) => {
|
|
|
- if (data.iteration_id)
|
|
|
+ onNodeStarted: ({ data: nodeStartedData }) => {
|
|
|
+ if (nodeStartedData.iteration_id)
|
|
|
return
|
|
|
|
|
|
responseItem.workflowProcess!.tracing!.push({
|
|
|
- ...data,
|
|
|
+ ...nodeStartedData,
|
|
|
status: WorkflowRunningStatus.Running,
|
|
|
} as any)
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
+ })
|
|
|
},
|
|
|
- onNodeFinished: ({ data }) => {
|
|
|
- if (data.iteration_id)
|
|
|
+ onNodeFinished: ({ data: nodeFinishedData }) => {
|
|
|
+ if (nodeFinishedData.iteration_id)
|
|
|
return
|
|
|
|
|
|
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
|
|
|
if (!item.execution_metadata?.parallel_id)
|
|
|
- return item.node_id === data.node_id
|
|
|
+ return item.node_id === nodeFinishedData.node_id
|
|
|
|
|
|
- return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata.parallel_id)
|
|
|
+ return item.node_id === nodeFinishedData.node_id && (item.execution_metadata?.parallel_id === nodeFinishedData.execution_metadata.parallel_id)
|
|
|
+ })
|
|
|
+ responseItem.workflowProcess!.tracing[currentIndex] = nodeFinishedData as any
|
|
|
+
|
|
|
+ updateCurrentQAOnTree({
|
|
|
+ placeholderQuestionId,
|
|
|
+ questionItem,
|
|
|
+ responseItem,
|
|
|
+ parentId: data.parent_message_id,
|
|
|
})
|
|
|
- responseItem.workflowProcess!.tracing[currentIndex] = data as any
|
|
|
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
|
|
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
|
|
- draft[currentIndex] = {
|
|
|
- ...draft[currentIndex],
|
|
|
- ...responseItem,
|
|
|
- }
|
|
|
- }))
|
|
|
},
|
|
|
onTTSChunk: (messageId: string, audio: string) => {
|
|
|
if (!audio || audio === '')
|
|
@@ -542,11 +578,13 @@ export const useChat = (
|
|
|
})
|
|
|
return true
|
|
|
}, [
|
|
|
- config?.suggested_questions_after_answer,
|
|
|
- updateCurrentQA,
|
|
|
t,
|
|
|
+ chatTree.length,
|
|
|
+ threadMessages,
|
|
|
+ config?.suggested_questions_after_answer,
|
|
|
+ updateCurrentQAOnTree,
|
|
|
+ updateChatTreeNode,
|
|
|
notify,
|
|
|
- handleUpdateChatList,
|
|
|
handleResponding,
|
|
|
formatTime,
|
|
|
params.token,
|
|
@@ -556,76 +594,61 @@ export const useChat = (
|
|
|
])
|
|
|
|
|
|
const handleAnnotationEdited = useCallback((query: string, answer: string, index: number) => {
|
|
|
- handleUpdateChatList(chatListRef.current.map((item, i) => {
|
|
|
- if (i === index - 1) {
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- content: query,
|
|
|
- }
|
|
|
- }
|
|
|
- if (i === index) {
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- content: answer,
|
|
|
- annotation: {
|
|
|
- ...item.annotation,
|
|
|
- logAnnotation: undefined,
|
|
|
- } as any,
|
|
|
- }
|
|
|
- }
|
|
|
- return item
|
|
|
- }))
|
|
|
- }, [handleUpdateChatList])
|
|
|
+ const targetQuestionId = chatList[index - 1].id
|
|
|
+ const targetAnswerId = chatList[index].id
|
|
|
+
|
|
|
+ updateChatTreeNode(targetQuestionId, {
|
|
|
+ content: query,
|
|
|
+ })
|
|
|
+ updateChatTreeNode(targetAnswerId, {
|
|
|
+ content: answer,
|
|
|
+ annotation: {
|
|
|
+ ...chatList[index].annotation,
|
|
|
+ logAnnotation: undefined,
|
|
|
+ } as any,
|
|
|
+ })
|
|
|
+ }, [chatList, updateChatTreeNode])
|
|
|
+
|
|
|
const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => {
|
|
|
- handleUpdateChatList(chatListRef.current.map((item, i) => {
|
|
|
- if (i === index - 1) {
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- content: query,
|
|
|
- }
|
|
|
- }
|
|
|
- if (i === index) {
|
|
|
- const answerItem = {
|
|
|
- ...item,
|
|
|
- content: item.content,
|
|
|
- annotation: {
|
|
|
- id: annotationId,
|
|
|
- authorName,
|
|
|
- logAnnotation: {
|
|
|
- content: answer,
|
|
|
- account: {
|
|
|
- id: '',
|
|
|
- name: authorName,
|
|
|
- email: '',
|
|
|
- },
|
|
|
- },
|
|
|
- } as Annotation,
|
|
|
- }
|
|
|
- return answerItem
|
|
|
- }
|
|
|
- return item
|
|
|
- }))
|
|
|
- }, [handleUpdateChatList])
|
|
|
- const handleAnnotationRemoved = useCallback((index: number) => {
|
|
|
- handleUpdateChatList(chatListRef.current.map((item, i) => {
|
|
|
- if (i === index) {
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- content: item.content,
|
|
|
- annotation: {
|
|
|
- ...(item.annotation || {}),
|
|
|
+ const targetQuestionId = chatList[index - 1].id
|
|
|
+ const targetAnswerId = chatList[index].id
|
|
|
+
|
|
|
+ updateChatTreeNode(targetQuestionId, {
|
|
|
+ content: query,
|
|
|
+ })
|
|
|
+
|
|
|
+ updateChatTreeNode(targetAnswerId, {
|
|
|
+ content: chatList[index].content,
|
|
|
+ annotation: {
|
|
|
+ id: annotationId,
|
|
|
+ authorName,
|
|
|
+ logAnnotation: {
|
|
|
+ content: answer,
|
|
|
+ account: {
|
|
|
id: '',
|
|
|
- } as Annotation,
|
|
|
- }
|
|
|
- }
|
|
|
- return item
|
|
|
- }))
|
|
|
- }, [handleUpdateChatList])
|
|
|
+ name: authorName,
|
|
|
+ email: '',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ } as Annotation,
|
|
|
+ })
|
|
|
+ }, [chatList, updateChatTreeNode])
|
|
|
+
|
|
|
+ const handleAnnotationRemoved = useCallback((index: number) => {
|
|
|
+ const targetAnswerId = chatList[index].id
|
|
|
+
|
|
|
+ updateChatTreeNode(targetAnswerId, {
|
|
|
+ content: chatList[index].content,
|
|
|
+ annotation: {
|
|
|
+ ...(chatList[index].annotation || {}),
|
|
|
+ id: '',
|
|
|
+ } as Annotation,
|
|
|
+ })
|
|
|
+ }, [chatList, updateChatTreeNode])
|
|
|
|
|
|
return {
|
|
|
chatList,
|
|
|
- chatListRef,
|
|
|
- handleUpdateChatList,
|
|
|
+ setTargetMessageId,
|
|
|
conversationId: conversationId.current,
|
|
|
isResponding,
|
|
|
setIsResponding,
|