123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import { useCallback, useEffect, useMemo } from 'react'
- import Chat from '../chat'
- import type {
- ChatConfig,
- ChatItem,
- OnSend,
- } from '../types'
- import { useChat } from '../chat/hooks'
- import { getLastAnswer } from '../utils'
- import { useChatWithHistoryContext } from './context'
- import Header from './header'
- import ConfigPanel from './config-panel'
- import {
- fetchSuggestedQuestions,
- getUrl,
- stopChatMessageResponding,
- } from '@/service/share'
- import AnswerIcon from '@/app/components/base/answer-icon'
- const ChatWrapper = () => {
- const {
- appParams,
- appPrevChatList,
- currentConversationId,
- currentConversationItem,
- inputsForms,
- newConversationInputs,
- handleNewConversationCompleted,
- isMobile,
- isInstalledApp,
- appId,
- appMeta,
- handleFeedback,
- currentChatInstanceRef,
- appData,
- themeBuilder,
- } = useChatWithHistoryContext()
- const appConfig = useMemo(() => {
- const config = appParams || {}
- return {
- ...config,
- file_upload: {
- ...(config as any).file_upload,
- fileUploadConfig: (config as any).system_parameters,
- },
- supportFeedback: true,
- opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,
- } as ChatConfig
- }, [appParams, currentConversationItem?.introduction, currentConversationId])
- const {
- chatList,
- chatListRef,
- handleUpdateChatList,
- handleSend,
- handleStop,
- isResponding,
- suggestedQuestions,
- } = useChat(
- appConfig,
- {
- inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
- inputsForm: inputsForms,
- },
- appPrevChatList,
- taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
- )
- useEffect(() => {
- if (currentChatInstanceRef.current)
- currentChatInstanceRef.current.handleStop = handleStop
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [])
- const doSend: OnSend = useCallback((message, files, last_answer) => {
- const data: any = {
- query: message,
- files,
- inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
- conversation_id: currentConversationId,
- parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
- }
- handleSend(
- getUrl('chat-messages', isInstalledApp, appId || ''),
- data,
- {
- onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
- onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
- isPublicAPI: !isInstalledApp,
- },
- )
- }, [
- chatListRef,
- 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 = getLastAnswer(prevMessages)
- if (!question)
- return
- handleUpdateChatList(prevMessages)
- doSend(question.content, question.message_files, lastAnswer)
- }, [chatList, handleUpdateChatList, doSend])
- 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
- return (
- <div
- className='h-full bg-chatbot-bg overflow-hidden'
- >
- <Chat
- appData={appData}
- config={appConfig}
- chatList={chatList}
- isResponding={isResponding}
- chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
- chatFooterClassName='pb-4'
- chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
- onSend={doSend}
- inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
- inputsForm={inputsForms}
- onRegenerate={doRegenerate}
- onStopResponding={handleStop}
- chatNode={chatNode}
- allToolIcons={appMeta?.tool_icons || {}}
- onFeedback={handleFeedback}
- suggestedQuestions={suggestedQuestions}
- answerIcon={answerIcon}
- hideProcessDetail
- themeBuilder={themeBuilder}
- />
- </div>
- )
- }
- export default ChatWrapper
|