187 lines
5.1 KiB
TypeScript
Raw Normal View History

2024-02-04 16:10:46 +08:00
import { useCallback, useEffect, useMemo } from 'react'
import Chat from '../chat'
import type {
ChatConfig,
ChatItem,
2024-02-04 16:10:46 +08:00
OnSend,
} from '../types'
import { useChat } from '../chat/hooks'
import { useChatWithHistoryContext } from './context'
import Header from './header'
import ConfigPanel from './config-panel'
import {
fetchSuggestedQuestions,
getUrl,
stopChatMessageResponding,
2024-02-04 16:10:46 +08:00
} from '@/service/share'
import AnswerIcon from '@/app/components/base/answer-icon'
2024-02-04 16:10:46 +08:00
const ChatWrapper = () => {
const {
appParams,
appPrevChatList,
currentConversationId,
currentConversationItem,
inputsForms,
newConversationInputs,
handleNewConversationCompleted,
isMobile,
isInstalledApp,
appId,
appMeta,
handleFeedback,
currentChatInstanceRef,
appData,
2024-08-02 15:08:14 +08:00
themeBuilder,
2024-02-04 16:10:46 +08:00
} = useChatWithHistoryContext()
const appConfig = useMemo(() => {
const config = appParams || {}
return {
...config,
supportFeedback: true,
2024-02-12 22:22:57 +08:00
opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
2024-02-04 16:10:46 +08:00
} as ChatConfig
2024-02-12 22:22:57 +08:00
}, [appParams, currentConversationItem?.introduction, currentConversationId])
2024-02-04 16:10:46 +08:00
const {
chatList,
chatListRef,
handleUpdateChatList,
2024-02-04 16:10:46 +08:00
handleSend,
handleStop,
isResponding,
2024-02-04 16:10:46 +08:00
suggestedQuestions,
} = useChat(
appConfig,
2024-02-12 22:22:57 +08:00
{
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
promptVariables: inputsForms,
},
2024-02-04 16:10:46 +08:00
appPrevChatList,
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
2024-02-04 16:10:46 +08:00
)
useEffect(() => {
if (currentChatInstanceRef.current)
currentChatInstanceRef.current.handleStop = handleStop
}, [])
const doSend: OnSend = useCallback((message, files, last_answer) => {
2024-02-04 16:10:46 +08:00
const data: any = {
query: message,
inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
conversation_id: currentConversationId,
parent_message_id: last_answer?.id || chatListRef.current.at(-1)?.id || null,
2024-02-04 16:10:46 +08:00
}
if (appConfig?.file_upload?.image.enabled && files?.length)
data.files = files
handleSend(
getUrl('chat-messages', isInstalledApp, appId || ''),
data,
{
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
},
)
}, [
chatListRef,
2024-02-04 16:10:46 +08:00
appConfig,
currentConversationId,
currentConversationItem,
handleSend,
newConversationInputs,
handleNewConversationCompleted,
isInstalledApp,
appId,
])
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])
2024-02-04 16:10:46 +08:00
const chatNode = useMemo(() => {
if (inputsForms.length) {
return (
<>
<Header
isMobile={isMobile}
title={currentConversationItem?.name || ''}
/>
{
!currentConversationId && (
<div className={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}>
<div className='mb-6' />
<ConfigPanel />
<div
className='my-6 h-[1px]'
style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}
/>
</div>
)
}
</>
)
}
return (
<Header
isMobile={isMobile}
title={currentConversationItem?.name || ''}
/>
)
}, [
currentConversationId,
inputsForms,
currentConversationItem,
isMobile,
])
const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
? <AnswerIcon
iconType={appData.site.icon_type}
icon={appData.site.icon}
background={appData.site.icon_background}
imageUrl={appData.site.icon_url}
/>
: null
2024-02-04 16:10:46 +08:00
return (
<Chat
appData={appData}
2024-02-04 16:10:46 +08:00
config={appConfig}
chatList={chatList}
isResponding={isResponding}
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-full ${isMobile && 'px-4'}`}
2024-02-04 16:10:46 +08:00
chatFooterClassName='pb-4'
chatFooterInnerClassName={`mx-auto w-full max-w-full ${isMobile && 'px-4'}`}
2024-02-04 16:10:46 +08:00
onSend={doSend}
onRegenerate={doRegenerate}
2024-02-04 16:10:46 +08:00
onStopResponding={handleStop}
chatNode={chatNode}
allToolIcons={appMeta?.tool_icons || {}}
onFeedback={handleFeedback}
suggestedQuestions={suggestedQuestions}
answerIcon={answerIcon}
hideProcessDetail
2024-08-02 15:08:14 +08:00
themeBuilder={themeBuilder}
2024-02-04 16:10:46 +08:00
/>
)
}
export default ChatWrapper