| displayScene?: DisplayScene | displayScene?: DisplayScene | ||||
| useCurrentUserAvatar?: boolean | useCurrentUserAvatar?: boolean | ||||
| isResponsing?: boolean | isResponsing?: boolean | ||||
| canStopResponsing?: boolean | |||||
| abortResponsing?: () => void | abortResponsing?: () => void | ||||
| controlClearQuery?: number | controlClearQuery?: number | ||||
| controlFocus?: number | controlFocus?: number | ||||
| displayScene, | displayScene, | ||||
| useCurrentUserAvatar, | useCurrentUserAvatar, | ||||
| isResponsing, | isResponsing, | ||||
| canStopResponsing, | |||||
| abortResponsing, | abortResponsing, | ||||
| controlClearQuery, | controlClearQuery, | ||||
| controlFocus, | controlFocus, | ||||
| { | { | ||||
| !isHideSendInput && ( | !isHideSendInput && ( | ||||
| <div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}> | <div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}> | ||||
| {isResponsing && ( | |||||
| {(isResponsing && canStopResponsing) && ( | |||||
| <div className='flex justify-center mb-4'> | <div className='flex justify-center mb-4'> | ||||
| <Button className='flex items-center space-x-1 bg-white' onClick={() => abortResponsing?.()}> | <Button className='flex items-center space-x-1 bg-white' onClick={() => abortResponsing?.()}> | ||||
| {stopIcon} | {stopIcon} |
| import Chat from '@/app/components/app/chat' | import Chat from '@/app/components/app/chat' | ||||
| import ConfigContext from '@/context/debug-configuration' | import ConfigContext from '@/context/debug-configuration' | ||||
| import { ToastContext } from '@/app/components/base/toast' | import { ToastContext } from '@/app/components/base/toast' | ||||
| import { fetchConvesationMessages, fetchSuggestedQuestions, sendChatMessage, sendCompletionMessage } from '@/service/debug' | |||||
| import { fetchConvesationMessages, fetchSuggestedQuestions, sendChatMessage, sendCompletionMessage, stopChatMessageResponding } from '@/service/debug' | |||||
| import Button from '@/app/components/base/button' | import Button from '@/app/components/base/button' | ||||
| import type { ModelConfig as BackendModelConfig } from '@/types/app' | import type { ModelConfig as BackendModelConfig } from '@/types/app' | ||||
| import { promptVariablesToUserInputsForm } from '@/utils/model-config' | import { promptVariablesToUserInputsForm } from '@/utils/model-config' | ||||
| const doShowSuggestion = isShowSuggestion && !isResponsing | const doShowSuggestion = isShowSuggestion && !isResponsing | ||||
| const [suggestQuestions, setSuggestQuestions] = useState<string[]>([]) | const [suggestQuestions, setSuggestQuestions] = useState<string[]>([]) | ||||
| const [messageTaskId, setMessageTaskId] = useState('') | |||||
| const onSend = async (message: string) => { | const onSend = async (message: string) => { | ||||
| if (isResponsing) { | if (isResponsing) { | ||||
| notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) | notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) | ||||
| getAbortController: (abortController) => { | getAbortController: (abortController) => { | ||||
| setAbortController(abortController) | setAbortController(abortController) | ||||
| }, | }, | ||||
| onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId }: any) => { | |||||
| onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => { | |||||
| responseItem.content = responseItem.content + message | responseItem.content = responseItem.content + message | ||||
| if (isFirstMessage && newConversationId) { | if (isFirstMessage && newConversationId) { | ||||
| setConversationId(newConversationId) | setConversationId(newConversationId) | ||||
| _newConversationId = newConversationId | _newConversationId = newConversationId | ||||
| } | } | ||||
| setMessageTaskId(taskId) | |||||
| if (messageId) | if (messageId) | ||||
| responseItem.id = messageId | responseItem.id = messageId | ||||
| feedbackDisabled | feedbackDisabled | ||||
| useCurrentUserAvatar | useCurrentUserAvatar | ||||
| isResponsing={isResponsing} | isResponsing={isResponsing} | ||||
| abortResponsing={() => { | |||||
| canStopResponsing={!!messageTaskId} | |||||
| abortResponsing={async () => { | |||||
| abortController?.abort() | abortController?.abort() | ||||
| await stopChatMessageResponding(appId, messageTaskId) | |||||
| setResponsingFalse() | setResponsingFalse() | ||||
| }} | }} | ||||
| isShowSuggestion={doShowSuggestion} | isShowSuggestion={doShowSuggestion} |
| import Sidebar from '@/app/components/share/chat/sidebar' | import Sidebar from '@/app/components/share/chat/sidebar' | ||||
| import ConfigSence from '@/app/components/share/chat/config-scence' | import ConfigSence from '@/app/components/share/chat/config-scence' | ||||
| import Header from '@/app/components/share/header' | import Header from '@/app/components/share/header' | ||||
| import { fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, fetchSuggestedQuestions, sendChatMessage, 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' | ||||
| const [isShowSuggestion, setIsShowSuggestion] = useState(false) | const [isShowSuggestion, setIsShowSuggestion] = useState(false) | ||||
| const doShowSuggestion = isShowSuggestion && !isResponsing | const doShowSuggestion = isShowSuggestion && !isResponsing | ||||
| const [suggestQuestions, setSuggestQuestions] = useState<string[]>([]) | const [suggestQuestions, setSuggestQuestions] = useState<string[]>([]) | ||||
| const [messageTaskId, setMessageTaskId] = useState('') | |||||
| const handleSend = async (message: string) => { | const handleSend = async (message: string) => { | ||||
| if (isResponsing) { | if (isResponsing) { | ||||
| notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) | notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) | ||||
| getAbortController: (abortController) => { | getAbortController: (abortController) => { | ||||
| setAbortController(abortController) | setAbortController(abortController) | ||||
| }, | }, | ||||
| onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId }: any) => { | |||||
| onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => { | |||||
| responseItem.content = responseItem.content + message | responseItem.content = responseItem.content + message | ||||
| responseItem.id = messageId | responseItem.id = messageId | ||||
| if (isFirstMessage && newConversationId) | if (isFirstMessage && newConversationId) | ||||
| tempNewConversationId = newConversationId | tempNewConversationId = newConversationId | ||||
| setMessageTaskId(taskId) | |||||
| // closesure new list is outdated. | // closesure new list is outdated. | ||||
| const newListWithAnswer = produce( | const newListWithAnswer = produce( | ||||
| getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId), | getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId), | ||||
| isHideFeedbackEdit | isHideFeedbackEdit | ||||
| onFeedback={handleFeedback} | onFeedback={handleFeedback} | ||||
| isResponsing={isResponsing} | isResponsing={isResponsing} | ||||
| abortResponsing={() => { | |||||
| canStopResponsing={!!messageTaskId} | |||||
| abortResponsing={async () => { | |||||
| abortController?.abort() | abortController?.abort() | ||||
| await stopChatMessageResponding(appId, messageTaskId, isInstalledApp, installedAppInfo?.id) | |||||
| setResponsingFalse() | setResponsingFalse() | ||||
| }} | }} | ||||
| checkCanSend={checkCanSend} | checkCanSend={checkCanSend} |
| } | } | ||||
| export type IOnDataMoreInfo = { | export type IOnDataMoreInfo = { | ||||
| conversationId: string | undefined | |||||
| conversationId?: string | |||||
| taskId?: string | |||||
| messageId: string | messageId: string | ||||
| errorMessage?: string | errorMessage?: string | ||||
| } | } | ||||
| // can not use format here. Because message is splited. | // can not use format here. Because message is splited. | ||||
| onData(unicodeToChar(bufferObj.answer), isFirstMessage, { | onData(unicodeToChar(bufferObj.answer), isFirstMessage, { | ||||
| conversationId: bufferObj.conversation_id, | conversationId: bufferObj.conversation_id, | ||||
| taskId: bufferObj.task_id, | |||||
| messageId: bufferObj.id, | messageId: bufferObj.id, | ||||
| }) | }) | ||||
| isFirstMessage = false | isFirstMessage = false |
| }, { onData, onCompleted, onError, getAbortController }) | }, { onData, onCompleted, onError, getAbortController }) | ||||
| } | } | ||||
| export const stopChatMessageResponding = async (appId: string, taskId: string) => { | |||||
| return post(`apps/${appId}/chat-messages/${taskId}/stop`) | |||||
| } | |||||
| export const sendCompletionMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError }: { | export const sendCompletionMessage = async (appId: string, body: Record<string, any>, { onData, onCompleted, onError }: { | ||||
| onData: IOnData | onData: IOnData | ||||
| onCompleted: IOnCompleted | onCompleted: IOnCompleted |
| }, { onData, onCompleted, isPublicAPI: !isInstalledApp, onError, getAbortController }) | }, { onData, onCompleted, isPublicAPI: !isInstalledApp, onError, getAbortController }) | ||||
| } | } | ||||
| export const stopChatMessageResponding = async (appId: string, taskId: string, isInstalledApp: boolean, installedAppId = '') => { | |||||
| return getAction('post', isInstalledApp)(getUrl(`chat-messages/${taskId}/stop`, isInstalledApp, installedAppId)) | |||||
| } | |||||
| export const sendCompletionMessage = async (body: Record<string, any>, { onData, onCompleted, onError }: { | export const sendCompletionMessage = async (body: Record<string, any>, { onData, onCompleted, onError }: { | ||||
| onData: IOnData | onData: IOnData | ||||
| onCompleted: IOnCompleted | onCompleted: IOnCompleted |