Unverified Commit fb62017e authored by zxhlyh's avatar zxhlyh Committed by GitHub

Fix/embedding chat (#899)

Co-authored-by: 's avatarJoel <iamjoel007@gmail.com>
parent 9adbeade
...@@ -12,13 +12,12 @@ import AppUnavailable from '../../base/app-unavailable' ...@@ -12,13 +12,12 @@ import AppUnavailable from '../../base/app-unavailable'
import useConversation from './hooks/use-conversation' import useConversation from './hooks/use-conversation'
import s from './style.module.css' import s from './style.module.css'
import { ToastContext } from '@/app/components/base/toast' import { ToastContext } from '@/app/components/base/toast'
import Sidebar from '@/app/components/share/chatbot/sidebar'
import ConfigScene from '@/app/components/share/chatbot/config-scence' import ConfigScene from '@/app/components/share/chatbot/config-scence'
import Header from '@/app/components/share/header' import Header from '@/app/components/share/header'
import { /* delConversation, */ fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, fetchSuggestedQuestions, pinConversation, sendChatMessage, stopChatMessageResponding, unpinConversation, updateFeedback } from '@/service/share' import { fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, fetchSuggestedQuestions, sendChatMessage, stopChatMessageResponding, updateFeedback } from '@/service/share'
import type { ConversationItem, SiteInfo } from '@/models/share' import type { ConversationItem, SiteInfo } from '@/models/share'
import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug' import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug'
import type { Feedbacktype, IChatItem } from '@/app/components/app/chat' import type { Feedbacktype, IChatItem } from '@/app/components/app/chat/type'
import Chat from '@/app/components/app/chat' import Chat from '@/app/components/app/chat'
import { changeLanguage } from '@/i18n/i18next-config' import { changeLanguage } from '@/i18n/i18next-config'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
...@@ -26,7 +25,7 @@ import Loading from '@/app/components/base/loading' ...@@ -26,7 +25,7 @@ import Loading from '@/app/components/base/loading'
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel' import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
import { userInputsFormToPromptVariables } from '@/utils/model-config' import { userInputsFormToPromptVariables } from '@/utils/model-config'
import type { InstalledApp } from '@/models/explore' import type { InstalledApp } from '@/models/explore'
// import Confirm from '@/app/components/base/confirm' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
export type IMainProps = { export type IMainProps = {
isInstalledApp?: boolean isInstalledApp?: boolean
...@@ -52,8 +51,6 @@ const Main: FC<IMainProps> = ({ ...@@ -52,8 +51,6 @@ const Main: FC<IMainProps> = ({
const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null) const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
const [inited, setInited] = useState<boolean>(false) const [inited, setInited] = useState<boolean>(false)
const [plan, setPlan] = useState<string>('basic') // basic/plus/pro const [plan, setPlan] = useState<string>('basic') // basic/plus/pro
// in mobile, show sidebar by click button
const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false)
// Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client. // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client.
useEffect(() => { useEffect(() => {
if (siteInfo?.title) { if (siteInfo?.title) {
...@@ -124,37 +121,6 @@ const Main: FC<IMainProps> = ({ ...@@ -124,37 +121,6 @@ const Main: FC<IMainProps> = ({
setControlUpdateConversationList(Date.now()) setControlUpdateConversationList(Date.now())
} }
const handlePin = async (id: string) => {
await pinConversation(isInstalledApp, installedAppInfo?.id, id)
notify({ type: 'success', message: t('common.api.success') })
noticeUpdateList()
}
const handleUnpin = async (id: string) => {
await unpinConversation(isInstalledApp, installedAppInfo?.id, id)
notify({ type: 'success', message: t('common.api.success') })
noticeUpdateList()
}
const [isShowConfirm, { setTrue: showConfirm, setFalse: hideConfirm }] = useBoolean(false)
const [toDeleteConversationId, setToDeleteConversationId] = useState('')
const handleDelete = (id: string) => {
setToDeleteConversationId(id)
hideSidebar() // mobile
showConfirm()
}
// const didDelete = async () => {
// await delConversation(isInstalledApp, installedAppInfo?.id, toDeleteConversationId)
// notify({ type: 'success', message: t('common.api.success') })
// hideConfirm()
// if (currConversationId === toDeleteConversationId)
// handleConversationIdChange('-1')
// noticeUpdateList()
// }
const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null) const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
const [speechToTextConfig, setSpeechToTextConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null) const [speechToTextConfig, setSpeechToTextConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
...@@ -235,20 +201,6 @@ const Main: FC<IMainProps> = ({ ...@@ -235,20 +201,6 @@ const Main: FC<IMainProps> = ({
} }
useEffect(handleConversationSwitch, [currConversationId, inited]) useEffect(handleConversationSwitch, [currConversationId, inited])
const handleConversationIdChange = (id: string) => {
if (id === '-1') {
createNewChat()
setConversationIdChangeBecauseOfNew(true)
}
else {
setConversationIdChangeBecauseOfNew(false)
}
// trigger handleConversationSwitch
setCurrConversationId(id, appId)
setIsShowSuggestion(false)
hideSidebar()
}
/* /*
* chat info. chat is under conversation. * chat info. chat is under conversation.
*/ */
...@@ -416,6 +368,7 @@ const Main: FC<IMainProps> = ({ ...@@ -416,6 +368,7 @@ const Main: FC<IMainProps> = ({
const [suggestQuestions, setSuggestQuestions] = useState<string[]>([]) const [suggestQuestions, setSuggestQuestions] = useState<string[]>([])
const [messageTaskId, setMessageTaskId] = useState('') const [messageTaskId, setMessageTaskId] = useState('')
const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false) const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
const [shouldReload, setShouldReload] = useState(false)
const handleSend = async (message: string) => { const handleSend = async (message: string) => {
if (isResponsing) { if (isResponsing) {
...@@ -500,7 +453,9 @@ const Main: FC<IMainProps> = ({ ...@@ -500,7 +453,9 @@ const Main: FC<IMainProps> = ({
setIsShowSuggestion(true) setIsShowSuggestion(true)
} }
}, },
onError() { onError(errorMessage, errorCode) {
if (['provider_not_initialize', 'completion_request_error'].includes(errorCode as string))
setShouldReload(true)
setResponsingFalse() setResponsingFalse()
// role back placeholder answer // role back placeholder answer
setChatList(produce(getChatList(), (draft) => { setChatList(produce(getChatList(), (draft) => {
...@@ -525,31 +480,11 @@ const Main: FC<IMainProps> = ({ ...@@ -525,31 +480,11 @@ const Main: FC<IMainProps> = ({
notify({ type: 'success', message: t('common.api.success') }) notify({ type: 'success', message: t('common.api.success') })
} }
const renderSidebar = () => { const handleReload = () => {
if (!appId || !siteInfo || !promptConfig) setCurrConversationId('-1', appId, false)
return null setChatNotStarted()
return ( setShouldReload(false)
<Sidebar createNewChat()
list={conversationList}
isClearConversationList={isClearConversationList}
pinnedList={pinnedConversationList}
isClearPinnedConversationList={isClearPinnedConversationList}
onMoreLoaded={onMoreLoaded}
onPinnedMoreLoaded={onPinnedMoreLoaded}
isNoMore={!hasMore}
isPinnedNoMore={!hasPinnedMore}
onCurrentIdChange={handleConversationIdChange}
currentId={currConversationId}
copyRight={siteInfo.copyright || siteInfo.title}
isInstalledApp={isInstalledApp}
installedAppId={installedAppInfo?.id}
siteInfo={siteInfo}
onPin={handlePin}
onUnpin={handleUnpin}
controlUpdateList={controlUpdateConversationList}
onDelete={handleDelete}
/>
)
} }
const difyIcon = ( const difyIcon = (
...@@ -571,24 +506,9 @@ const Main: FC<IMainProps> = ({ ...@@ -571,24 +506,9 @@ const Main: FC<IMainProps> = ({
icon_background={siteInfo.icon_background} icon_background={siteInfo.icon_background}
isEmbedScene={true} isEmbedScene={true}
isMobile={isMobile} isMobile={isMobile}
// onShowSideBar={showSidebar}
// onCreateNewChat={() => handleConversationIdChange('-1')}
/> />
<div className={'flex bg-white overflow-hidden'}> <div className={'flex bg-white overflow-hidden'}>
{/* sidebar */}
{/* {!isMobile && renderSidebar()} */}
{/* {isMobile && isShowSidebar && (
<div className='fixed inset-0 z-50'
style={{ backgroundColor: 'rgba(35, 56, 118, 0.2)' }}
onClick={hideSidebar}
>
<div className='inline-block' onClick={e => e.stopPropagation()}>
{renderSidebar()}
</div>
</div>
)} */}
{/* main */}
<div className={cn( <div className={cn(
isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)]', isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)]',
'flex-grow flex flex-col overflow-y-auto', 'flex-grow flex flex-col overflow-y-auto',
...@@ -606,7 +526,22 @@ const Main: FC<IMainProps> = ({ ...@@ -606,7 +526,22 @@ const Main: FC<IMainProps> = ({
onInputsChange={setCurrInputs} onInputsChange={setCurrInputs}
plan={plan} plan={plan}
></ConfigScene> ></ConfigScene>
{
shouldReload && (
<div className='flex items-center justify-between mb-5 px-4 py-2 bg-[#FEF0C7]'>
<div className='flex items-center text-xs font-medium text-[#DC6803]'>
<AlertTriangle className='mr-2 w-4 h-4' />
{t('share.chat.temporarySystemIssue')}
</div>
<div
className='flex items-center px-3 h-7 bg-white shadow-xs rounded-md text-xs font-medium text-gray-700 cursor-pointer'
onClick={handleReload}
>
{t('share.chat.tryToSolve')}
</div>
</div>
)
}
{ {
hasSetInputs && ( hasSetInputs && (
<div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}> <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto mb-3.5 overflow-hidden')}>
......
...@@ -27,6 +27,8 @@ const translation = { ...@@ -27,6 +27,8 @@ const translation = {
title: 'Delete conversation', title: 'Delete conversation',
content: 'Are you sure you want to delete this conversation?', content: 'Are you sure you want to delete this conversation?',
}, },
tryToSolve: 'Try to solve',
temporarySystemIssue: 'Sorry, temporary system issue.',
}, },
generation: { generation: {
tabs: { tabs: {
......
...@@ -23,6 +23,8 @@ const translation = { ...@@ -23,6 +23,8 @@ const translation = {
title: '删除对话', title: '删除对话',
content: '您确定要删除此对话吗?', content: '您确定要删除此对话吗?',
}, },
tryToSolve: '尝试解决',
temporarySystemIssue: '抱歉,临时系统问题。',
}, },
generation: { generation: {
tabs: { tabs: {
......
...@@ -28,12 +28,13 @@ export type IOnDataMoreInfo = { ...@@ -28,12 +28,13 @@ export type IOnDataMoreInfo = {
taskId?: string taskId?: string
messageId: string messageId: string
errorMessage?: string errorMessage?: string
errorCode?: string
} }
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
export type IOnThought = (though: ThoughtItem) => void export type IOnThought = (though: ThoughtItem) => void
export type IOnCompleted = (hasError?: boolean) => void export type IOnCompleted = (hasError?: boolean) => void
export type IOnError = (msg: string) => void export type IOnError = (msg: string, code?: string) => void
type IOtherOptions = { type IOtherOptions = {
isPublicAPI?: boolean isPublicAPI?: boolean
...@@ -102,6 +103,7 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted ...@@ -102,6 +103,7 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted
conversationId: undefined, conversationId: undefined,
messageId: '', messageId: '',
errorMessage: bufferObj.message, errorMessage: bufferObj.message,
errorCode: bufferObj.code,
}) })
hasError = true hasError = true
onCompleted && onCompleted(true) onCompleted && onCompleted(true)
...@@ -345,7 +347,7 @@ export const ssePost = (url: string, fetchOptions: any, { isPublicAPI = false, o ...@@ -345,7 +347,7 @@ export const ssePost = (url: string, fetchOptions: any, { isPublicAPI = false, o
return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => { return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
if (moreInfo.errorMessage) { if (moreInfo.errorMessage) {
// debugger // debugger
onError?.(moreInfo.errorMessage) onError?.(moreInfo.errorMessage, moreInfo.errorCode)
if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.') if (moreInfo.errorMessage !== 'AbortError: The user aborted a request.')
Toast.notify({ type: 'error', message: moreInfo.errorMessage }) Toast.notify({ type: 'error', message: moreInfo.errorMessage })
return return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment