feat: regenerate in Chat, agent and Chatflow app (#7661)

This commit is contained in:
Hash Brown
2024-09-22 03:15:11 +08:00
committed by GitHub
parent b32a7713e0
commit 8c51d06222
51 changed files with 606 additions and 181 deletions

View File

@@ -2,7 +2,6 @@ import {
memo,
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import { RiCloseLine } from '@remixicon/react'
@@ -17,50 +16,70 @@ import type { ChatItem } 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'
function appendQAToChatList(newChatList: ChatItem[], item: any) {
newChatList.push({
id: item.id,
content: item.answer,
feedback: item.feedback,
isAnswer: true,
citation: item.metadata?.retriever_resources,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
workflow_run_id: item.workflow_run_id,
})
newChatList.push({
id: `question-${item.id}`,
content: item.query,
isAnswer: false,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [],
})
}
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 ChatRecord = () => {
const [fetched, setFetched] = useState(false)
const [chatList, setChatList] = useState([])
const [chatList, setChatList] = useState<ChatItem[]>([])
const appDetail = useAppStore(s => s.appDetail)
const workflowStore = useWorkflowStore()
const { handleLoadBackupDraft } = useWorkflowRun()
const historyWorkflowData = useStore(s => s.historyWorkflowData)
const currentConversationID = historyWorkflowData?.conversation_id
const chatMessageList = useMemo(() => {
const res: ChatItem[] = []
if (chatList.length) {
chatList.forEach((item: any) => {
res.push({
id: `question-${item.id}`,
content: item.query,
isAnswer: false,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [],
})
res.push({
id: item.id,
content: item.answer,
feedback: item.feedback,
isAnswer: true,
citation: item.metadata?.retriever_resources,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
workflow_run_id: item.workflow_run_id,
})
})
}
return res
}, [chatList])
const handleFetchConversationMessages = useCallback(async () => {
if (appDetail && currentConversationID) {
try {
setFetched(false)
const res = await fetchConversationMessages(appDetail.id, currentConversationID)
setFetched(true)
setChatList((res as any).data)
setChatList(getFormattedChatList((res as any).data))
}
catch (e) {
console.error(e)
}
finally {
setFetched(true)
}
}
}, [appDetail, currentConversationID])
@@ -101,7 +120,7 @@ const ChatRecord = () => {
config={{
supportCitationHitInfo: true,
} as any}
chatList={chatMessageList}
chatList={chatList}
chatContainerClassName='px-4'
chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto'
chatFooterClassName='px-4 rounded-b-2xl'

View File

@@ -18,7 +18,7 @@ import ConversationVariableModal from './conversation-variable-modal'
import { useChat } from './hooks'
import type { ChatWrapperRefType } from './index'
import Chat from '@/app/components/base/chat/chat'
import type { OnSend } from '@/app/components/base/chat/types'
import type { ChatItem, OnSend } from '@/app/components/base/chat/types'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import {
fetchSuggestedQuestions,
@@ -58,6 +58,8 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({ showConv
const {
conversationId,
chatList,
chatListRef,
handleUpdateChatList,
handleStop,
isResponding,
suggestedQuestions,
@@ -73,19 +75,36 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({ showConv
taskId => stopChatMessageResponding(appDetail!.id, taskId),
)
const doSend = useCallback<OnSend>((query, files) => {
const doSend = useCallback<OnSend>((query, files, last_answer) => {
handleSend(
{
query,
files,
inputs: workflowStore.getState().inputs,
conversation_id: conversationId,
parent_message_id: last_answer?.id || chatListRef.current.at(-1)?.id || null,
},
{
onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
},
)
}, [conversationId, handleSend, workflowStore, appDetail])
}, [chatListRef, conversationId, handleSend, workflowStore, appDetail])
const doRegenerate = useCallback((chatItem: ChatItem) => {
const index = chatList.findIndex(item => item.id === chatItem.id)
if (index === -1)
return
const prevMessages = chatList.slice(0, index)
const question = prevMessages.pop()
const lastAnswer = prevMessages.at(-1)
if (!question)
return
handleUpdateChatList(prevMessages)
doSend(question.content, question.message_files, (!lastAnswer || lastAnswer.isOpeningStatement) ? undefined : lastAnswer)
}, [chatList, handleUpdateChatList, doSend])
useImperativeHandle(ref, () => {
return {
@@ -107,6 +126,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({ showConv
chatFooterClassName='px-4 rounded-bl-2xl'
chatFooterInnerClassName='pb-4 w-full max-w-full mx-auto'
onSend={doSend}
onRegenerate={doRegenerate}
onStopResponding={handleStop}
chatNode={(
<>

View File

@@ -387,6 +387,8 @@ export const useChat = (
return {
conversationId: conversationId.current,
chatList,
chatListRef,
handleUpdateChatList,
handleSend,
handleStop,
handleRestart,