Ворклог по задаче "Проработать обработчик ошибок в ИИ чате"
Вот что чатгпт говорит:
Да, обрыв запроса в браузере может приводить к отмене генерации у провайдера — если ваш бэкенд связывает эти два соединения и передаёт отмену дальше. Но отсутствие поддержки потоковых ответов в современном Chrome — маловероятная причина: fetch() поддерживает чтение ответа через ReadableStream. MDN
Здесь нужно разделить два участка: браузер → ваш сервер и ваш сервер → провайдер. Если браузер оборвал запрос, сервер может отменить запрос провайдеру через AbortController. Тогда обработчик ошибок действительно может принять эту отмену за «провайдер прервал генерацию». Сам текст ошибки ещё не доказывает, кто первым оборвал соединение. Отмена через AbortSignal может завершать HTTP-запрос с AbortError. Node.js v26.10.0 Documentation
Проверить наличие API можно прямо в консоли браузера:
const supportsResponseStreaming =
typeof fetch === 'function' &&
typeof ReadableStream === 'function' &&
typeof Response === 'function' &&
'body' in Response.prototype &&
typeof ReadableStream.prototype.getReader === 'function';
console.log({ supportsResponseStreaming });
Это проверяет поддержку чтения потокового ответа. Отдельной браузерной функции «поддерживается HTTP Streamable» нет; если речь об обычном запросе с JSON и потоковом ответе агента, нужна именно эта возможность. Потоковая отправка тела запроса — другая возможность, со своими ограничениями. MDN
Но true не гарантирует, что поток нормально проходит по всей цепочке. Для этого нужен тестовый endpoint на вашем сервере, который отправляет, например, пять сообщений с интервалом в секунду, и проверка в браузере:
async function testStream(url) {
const started = performance.now();
const response = await fetch(url, { cache: 'no-store' });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
if (!response.body) throw new Error('Нет тела ответа');
const reader = response.body.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log({
elapsedMs: Math.round(performance.now() - started),
bytes: value.byteLength,
});
}
console.log('Поток завершён');
} catch (error) {
console.error('Ошибка чтения потока:', error);
} finally {
reader.releaseLock();
}
}
testStream('/api/stream-test');
Адрес здесь условный — endpoint нужно реализовать. Чтение через getReader() позволяет наблюдать поступление частей ответа. Границы сетевых частей не обязаны совпадать с границами сообщений сервера. MDN
Интерпретация:
- Части приходят постепенно — поток проходит.
- Всё приходит одним куском в конце — где-то есть буферизация.
- Чтение падает с ошибкой — соединение или обработка потока нарушены; причина требует сопоставления с серверным логом.
Для вашего случая решающая проверка — порядок событий в логах одного запроса: отключение клиента, вызов отмены запроса провайдеру, ошибка чтения ответа провайдера. Если сначала отключается клиент, а затем ваш код отменяет генерацию, гипотеза подтверждается. Если первым повреждается поток от провайдера — причина на другом участке.
Разница «ноутбук / мобильный» сама по себе пока указывает лишь на различие условий: сеть, VPN, расширения, версия фронтенда или его логика отмены запроса. Выбрать конкретную причину по этому признаку нельзя.