@@ -85,8 +85,9 @@ export const chatMessagesApi = { }) }, - reconnectMessageStream: (chatUid: string, messageUid: string, offset: number, token: string, signal?: AbortSignal) => { - const url = `${getApiUrl() + '/api'}/chats/${chatUid}/messages/${messageUid}/stream?offset=${offset}` + reconnectMessageStream: (chatUid: string, offset: number, token: string, signal?: AbortSignal) => { + const baseUrl = `${getApiUrl() + '/api'}/chats/${chatUid}/messages/stream` + const url = offset > 0 ? `${baseUrl}?offset=${offset}` : baseUrl return fetch(url, { method: 'GET', @@ -1,3 +1,37 @@ +.markdown { + max-width: 100%; + overflow-wrap: anywhere; + word-break: break-word; + + p, + li, + h1, + h2, + h3, + h4, + h5, + h6, + blockquote { + overflow-wrap: anywhere; + word-break: break-word; + } + + table { + display: block; + max-width: 100%; + overflow-x: auto; + } + + pre { + max-width: 100%; + white-space: pre-wrap; + word-break: break-word; + overflow-x: hidden; + } +} + .inlineCode { border-radius: 15px; + white-space: pre-wrap; + word-break: break-word; } @@ -14,7 +14,7 @@ export const Markdown = ({ content, id = 'markdown'}: IProps) => { const LazyCode = dynamic(() => import('#/widgets/chat-gpt-field/ui/code')) return ( -
+
message.uid === TEMP_USER_MESSAGE_UID || message.uid === pendingUid + ) + + if (options.isReconnect && !hasPlaceholder) { + return ensureModelMessageForStream(prev, messageUuid, modelType, runtime.modelContent) + } + return prev.map((message) => { if (message.uid === TEMP_USER_MESSAGE_UID) { return { ...message, uid: messageUuid } @@ -358,10 +362,12 @@ export function useChatModel( }, }) + const hasActiveSession = readChatStreamSession(currentChat) !== null + return { - shouldReconnect: runtime.streamFailed && runtime.hasStreamEvents, + shouldReconnect: runtime.streamFailed && (runtime.hasStreamEvents || hasActiveSession), streamFailed: runtime.streamFailed, - missingStart: !runtime.hasStreamEvents && !options.isReconnect && !inputMessageUuidRef.current, + missingStart: !runtime.hasStreamEvents && !options.isReconnect && !hasActiveSession, } }, [currentChat, modelType, persistStreamSession] @@ -406,7 +412,6 @@ export function useChatModel( try { const response = await chatMessagesApi.reconnectMessageStream( chatUid, - inputMessageUuid, streamOffset, data.access, abortController.signal @@ -469,16 +474,21 @@ export function useChatModel( const tryReconnectOnLoad = useCallback( async (chatUid: string, loadedMessages: Message[]) => { const session = readChatStreamSession(chatUid) - const reconnectTarget = session ?? detectInProgressStream(loadedMessages) + const detected = detectInProgressStream(loadedMessages) - if (!reconnectTarget) { + if (!session && !detected) { return } + const inputMessageUuid = + session?.inputMessageUuid && session.inputMessageUuid !== STREAMING_PENDING_UID + ? session.inputMessageUuid + : (detected?.inputMessageUuid ?? session?.inputMessageUuid ?? STREAMING_PENDING_UID) + await reconnectToStream( chatUid, - reconnectTarget.inputMessageUuid, - reconnectTarget.lastOffset, + inputMessageUuid, + session?.lastOffset ?? detected?.lastOffset ?? 0, loadedMessages, session?.modelContent ) @@ -635,10 +645,17 @@ export function useChatModel( createStreamingModelMessage(STREAMING_PENDING_UID, streamCreatedAtRef.current, modelType), ]) + writeChatStreamSession(currentChat, { + inputMessageUuid: STREAMING_PENDING_UID, + lastOffset: 0, + modelContent: getWaitingForModelContent(modelType), + }) + try { const response = await chatMessagesApi.sendMessageStream(currentChat, requestBody, data.access, abortController.signal) if (!response.ok) { + clearChatStreamSession(currentChat) markOptimisticUserMessageFailed() setMessages((prev) => removeStreamingModelMessage(prev)) showMessage(await parseStreamErrorResponse(response)) @@ -646,6 +663,7 @@ export function useChatModel( } if (!response.body) { + clearChatStreamSession(currentChat) markOptimisticUserMessageFailed() setMessages((prev) => removeStreamingModelMessage(prev)) showMessage('Пустой ответ сервера') @@ -654,10 +672,13 @@ export function useChatModel( const result = await processMessageStream(response) - if (result.shouldReconnect && inputMessageUuidRef.current) { + if (result.shouldReconnect) { + const inputUuid = + inputMessageUuidRef.current ?? readChatStreamSession(currentChat)?.inputMessageUuid ?? STREAMING_PENDING_UID + await reconnectToStream( currentChat, - inputMessageUuidRef.current, + inputUuid, streamRuntimeRef.current.lastOffset, undefined, streamRuntimeRef.current.modelContent, @@ -667,6 +688,7 @@ export function useChatModel( } if (result.missingStart) { + clearChatStreamSession(currentChat) markOptimisticUserMessageFailed() setMessages((prev) => removeStreamingModelMessage(prev)) showMessage('Непредвиденная ошибка, попробуйте еще раз') @@ -676,19 +698,17 @@ export function useChatModel( return } - const inputUuid = inputMessageUuidRef.current - const runtime = streamRuntimeRef.current - - if (inputUuid && runtime.hasStreamEvents) { - persistStreamSession(currentChat, inputUuid) - await reconnectToStream(currentChat, inputUuid, runtime.lastOffset, undefined, runtime.modelContent, { + const session = readChatStreamSession(currentChat) + if (session) { + const inputUuid = inputMessageUuidRef.current ?? session.inputMessageUuid + await reconnectToStream(currentChat, inputUuid, session.lastOffset, undefined, session.modelContent, { silent: true, }) return } - markOptimisticUserMessageFailed(inputUuid) - setMessages((prev) => removeStreamingModelMessage(prev, inputUuid)) + markOptimisticUserMessageFailed() + setMessages((prev) => removeStreamingModelMessage(prev)) showMessage('Непредвиденная ошибка, попробуйте еще раз') } finally { isSendingRef.current = false @@ -77,9 +77,10 @@ export function BotMessage(props: any) { @@ -111,7 +112,7 @@ export function BotMessage(props: any) { })} - + { if (props.message.file) { @@ -120,7 +121,12 @@ export function BotMessage(props: any) { }} className='smallScroll' sx={{ + flex: 1, + minWidth: 0, overflowY: 'auto', + overflowX: 'hidden', + overflowWrap: 'anywhere', + wordBreak: 'break-word', position: 'relative', padding: '15px 23px', border: `1px solid #303035`, @@ -153,7 +159,7 @@ export function BotMessage(props: any) { )} - +