В этом материале
Streaming требует разбирать события протокола, а не произвольные сетевые куски. Отдельно фиксируйте начало ответа, полезный текст, завершение и возможную ошибку внутри потока.
Streaming OpenRouter позволяет показывать ответ по мере генерации. Для этого клиент передаёт stream: true и читает поток событий выбранного API. Разница с обычным запросом заключается не только в выводе текста: приложение должно правильно обрабатывать границы событий, завершение и отказ после начала передачи.
Форматы Chat Completions и Responses различаются. Не объединяйте их парсеры только потому, что оба сервиса передают данные постепенно. Описание ниже проверено по документации 1 ноября 2026 года.
Выберите один протокол
Руководство streaming OpenRouter описывает передачу через SSE. Для Chat Completions полезный текст поступает в соответствующих частях выбора ответа; Responses использует собственные типы событий. Сначала сохраните формат, который реально запрашивает ваше приложение.
HTTP-клиент может возвращать байтовые части в произвольном размере. Один такой фрагмент не обязательно равен целому событию или целому JSON. Поэтому чтение каждого сетевого куска через отдельный JSON.parse ненадёжно даже при исправном сервере.
Разбирайте SSE с буфером
Правила разделения событий и служебных комментариев определяет спецификация Server-Sent Events. Используйте совместимый парсер либо аккуратно сохраняйте незавершённый остаток между чтениями. Декодирование UTF-8 также должно учитывать символ, разделённый между байтовыми фрагментами.
Условный тест: передайте сохранённую запись событий парсеру сначала целиком, затем кусками по несколько байтов. Итоговый текст и последовательность состояний должны совпасть. Это проверка клиентского кода; для неё не требуется каждый раз оплачивать новый модельный вызов.
Измеряйте разные моменты
| Событие | Что оно подтверждает |
|---|---|
| Пришли заголовки | Соединение и ответ HTTP начались |
| Получен первый полезный текст | Пользователь может начать читать |
| Получено штатное завершение | Поток завершился по правилам выбранного API |
| Получен учёт использования | Есть данные для последующей сверки расходов |
Не называйте время заголовков временем первого токена. Комментарий поддержания соединения тоже не является сгенерированным текстом. Эти различия особенно важны при сравнении GPU-провайдеров с разными очередями и стадиями подготовки.
Обрабатывайте частичный ответ
После начала потока соединение может оборваться или принести событие ошибки. Интерфейс должен различать завершённый ответ и сохранённый фрагмент. При повторе лучше явно показывать новую попытку, чем незаметно приклеивать текст другой генерации к предыдущему результату.
Если ответ используется для запуска инструмента, частичного текста недостаточно для выполнения действия. Дождитесь завершённой структуры, проверьте аргументы и примените правила приложения. Успешный HTTP-статус не разрешает автоматически выполнять то, что ещё не закончено.
Пустое событие, роль сообщения и служебная статистика не должны добавлять видимые символы в ответ. В тестовом интерфейсе проверьте эти случаи отдельно: иначе пользователь увидит технические поля или приложение преждевременно решит, что генерация уже закончена.
Свяжите поток с эксплуатацией
Сохраняйте идентификатор операции и итоговое состояние независимо от пользовательского текста. Проверьте закрытие вкладки, отмену, долгую паузу и событие ошибки. Для потокового режима нужен собственный сценарий нагрузочной проверки, потому что поддерживаемое открытое соединение занимает ресурсы промежуточного сервера.
Дальше полезны ошибки внутри потока и компромисс задержки и пропускной способности GPU. Планирование инфраструктуры начинается с реального профиля потоковых запросов, а не только с рекламного числа токенов в секунду.