@@ -155,6 +155,8 @@ type StreamRuntime = { lastOffset: number modelContent: string hasStreamEvents: boolean + receivedStart: boolean + streamCompleted: boolean streamFailed: boolean } @@ -176,9 +178,7 @@ async function consumeSseStream( switch (sseEvent.event) { case 'start': { const data = sseEvent.data as { message_uuid?: string } - if (data?.message_uuid) { - handlers.onStart?.(data.message_uuid) - } + handlers.onStart?.(data?.message_uuid ?? '') break } case 'token': { @@ -222,6 +222,8 @@ export function useChatModel( lastOffset: 0, modelContent: '', hasStreamEvents: false, + receivedStart: false, + streamCompleted: false, streamFailed: false, }) @@ -242,6 +244,8 @@ export function useChatModel( lastOffset: 0, modelContent, hasStreamEvents: false, + receivedStart: false, + streamCompleted: false, streamFailed: false, } }, []) @@ -268,7 +272,13 @@ export function useChatModel( await consumeSseStream(response.body, { onStart: (messageUuid) => { + runtime.receivedStart = true runtime.hasStreamEvents = true + + if (!messageUuid) { + return + } + inputMessageUuidRef.current = messageUuid if (!runtime.modelContent) { @@ -335,6 +345,8 @@ export function useChatModel( }) }, onDone: (content) => { + runtime.streamCompleted = true + const inputUuid = inputMessageUuidRef.current if (!inputUuid) { return @@ -342,7 +354,6 @@ export function useChatModel( clearChatStreamSession(currentChat) runtime.modelContent = content - runtime.hasStreamEvents = false setMessages((prev) => { if (!prev) { @@ -367,7 +378,9 @@ export function useChatModel( return { shouldReconnect: runtime.streamFailed && (runtime.hasStreamEvents || hasActiveSession), streamFailed: runtime.streamFailed, - missingStart: !runtime.hasStreamEvents && !options.isReconnect && !hasActiveSession, + missingStart: !runtime.receivedStart && !options.isReconnect && !runtime.streamCompleted, + receivedStart: runtime.receivedStart, + streamCompleted: runtime.streamCompleted, } }, [currentChat, modelType, persistStreamSession] @@ -613,12 +626,18 @@ export function useChatModel( if (error.detail) { showMessage(error.detail) } else { + console.error('[useChatModel] sendMessage: HTTP error without detail', { + chatUid: currentChat, + status, + result, + }) showMessage('Непредвиденная ошибка, попробуйте еще раз') } } else { setMessages((prev) => [...(prev ?? []), ...(result as Message[])]) } - } catch { + } catch (error) { + console.error('[useChatModel] sendMessage: request failed', { chatUid: currentChat, error }) setMessages((prev) => { const withoutPlaceholder = prev?.slice(0, -2) ?? [] return [...withoutPlaceholder, { ...userMessage, is_sent: false }] @@ -688,12 +707,17 @@ export function useChatModel( } if (result.missingStart) { + console.error('[useChatModel] sendMessageStream: missing start event', { + chatUid: currentChat, + result, + inputMessageUuid: inputMessageUuidRef.current, + }) clearChatStreamSession(currentChat) markOptimisticUserMessageFailed() setMessages((prev) => removeStreamingModelMessage(prev)) showMessage('Непредвиденная ошибка, попробуйте еще раз') } - } catch { + } catch (error) { if (abortController.signal.aborted) { return } @@ -709,6 +733,12 @@ export function useChatModel( markOptimisticUserMessageFailed() setMessages((prev) => removeStreamingModelMessage(prev)) + console.error('[useChatModel] sendMessageStream: request failed', { + chatUid: currentChat, + error, + inputMessageUuid: inputMessageUuidRef.current, + streamRuntime: streamRuntimeRef.current, + }) showMessage('Непредвиденная ошибка, попробуйте еще раз') } finally { isSendingRef.current = false