Гайд · TNWS AI
Как правильно обрабатывать streaming SSE в OpenRouter API
Парсер потокового ответа OpenRouter: stream:true, data-строки, keep-alive комментарии, маркер [DONE], финальный usage и восстановление текста.
Что именно проверено
Материал отвечает на русскоязычный запрос «как включить streaming OpenRouter API». Факты и имена параметров сверены 12 сентября 2026 года с официальной документацией OpenRouter. Здесь нет обещания, что любой model slug и любой upstream-провайдер будут доступны всегда: каталог и поддерживаемые параметры меняются, поэтому финальная проверка выполняется на конкретном endpoint непосредственно перед вводом в эксплуатацию.
Применимость
Любая модель OpenRouter через Chat Completions с stream:true. Транспорт использует Server-Sent Events; JSON нельзя декодировать до получения полной data-строки.
Этот сценарий полезен разработчику backend, владельцу чат-бота или команде автоматизации, которой нужен наблюдаемый результат. Ключевой принцип: проверять не только текст модели, но и метаданные маршрутизации, структуру ответа, учет токенов и негативный путь. HTTP 200 означает, что транспорт сработал; он не доказывает, что модель соблюла бизнес-формат.
Подтвержденные факты
- Поток включается параметром stream:true.
- Текст приходит в choices[0].delta.content.
- Строки, начинающиеся с двоеточия, являются SSE-комментариями и не должны декодироваться как JSON.
- Финальный chunk содержит usage, а
data: [DONE]завершает поток.
Параметры OpenRouter задаются на двух разных уровнях. model, messages, response_format относятся к запросу генерации. Объект provider управляет выбором upstream-endpoint. Плагины маршрутизаторов передаются в plugins. Не переносите поле из одного уровня в другой: JSON останется синтаксически корректным, но настройка может не дать ожидаемого эффекта.
Пошаговая настройка
- Сформулируйте проверяемый результат. Для запроса «как включить streaming OpenRouter API» не принимайте субъективное «вроде работает»: заранее запишите ожидаемый формат и признаки ошибки.
- Создайте отдельный API-ключ для тестового приложения и передавайте его через переменную окружения
OPENROUTER_API_KEY. Не вставляйте секрет в клиентский JavaScript, мобильный APK, скриншот или репозиторий. - Соберите минимальный запрос из примера ниже. Он изолирует именно механизм статьи; дополнительные фреймворки, ретраи и бизнес-логику добавляйте после первого успешного ответа.
- Отправьте запрос на
https://openrouter.ai/api/v1/chat/completions. При ошибке сначала сохраните HTTP-код, тело ответа и request id, а уже затем решайте, допустим ли повтор. - Сравните результат с контрольным ожиданием: Интерфейс постепенно получает связный текст, комментарии не вызывают JSONDecodeError, а после финального события сохранены usage и полный собранный ответ.
- Прогоните положительный и отрицательный тест. Отрицательный тест должен доказать, что приложение не принимает невалидный формат, запрещенную модель или ослабленную политику после fallback.
- Только после этого добавьте таймаут, ограниченный retry с jitter, метрики и безопасное логирование. Повторяйте лишь идемпотентные операции или используйте собственный ключ идемпотентности.
Минимальный рабочий пример
buffer = ""
for chunk in response.iter_content(1024, decode_unicode=True):
buffer += chunk
while "\n" in buffer:
line, buffer = buffer.split("\n", 1)
line = line.strip()
if not line or line.startswith(":"): continue
if not line.startswith("data: "): continue
payload = line[6:]
if payload == "[DONE]": break
event = json.loads(payload)
text += event.get("choices", [{}])[0].get("delta", {}).get("content", "")
if event.get("usage"): usage = event["usage"]
Значения в угловых скобках — не реальные идентификаторы. Их нужно заменить slug из актуальной страницы модели OpenRouter. Такой шаблон безопаснее статьи с быстро устаревающим списком моделей или провайдеров. Токен доступа в пример намеренно не включен.
Готовый тестовый промпт
Задача: проверь конфигурацию «как включить streaming OpenRouter API».
Верни только результат в указанном формате, без вступления.
Если входных данных недостаточно, верни явную ошибку INPUT_MISSING и перечисли недостающие поля.
Не придумывай факты и не изменяй ограничения запроса.
Промпт нужен для проверки дисциплины ответа, но он не заменяет параметры API. Например, просьба «верни JSON» не дает тех же гарантий, что response_format; фраза «не сохраняй данные» не заменяет ZDR-фильтр; просьба «выбери быструю модель» не заменяет настройки маршрутизации. Управляющие ограничения следует задавать структурированными полями запроса, а затем повторно проверять в приложении.
Контрольный вход и ожидаемый результат
Контрольный вход уже встроен в пример. Не меняйте одновременно prompt, модель и маршрутизацию: иначе невозможно понять причину различий. Сначала выполните запрос пять раз с одной конфигурацией, сохраните HTTP-код, id, model, finish_reason, usage и длительность. Затем измените ровно один параметр и повторите серию.
Ожидаемый результат: Интерфейс постепенно получает связный текст, комментарии не вызывают JSONDecodeError, а после финального события сохранены usage и полный собранный ответ.
Если текст формально правильный, но отсутствует model или usage, не подменяйте их нулями без отдельного статуса. Различайте «ноль», «поле не поддерживается» и «финальное событие не было прочитано». Для JSON-ответов применяйте декодер и схему. Для обычного текста проверяйте длину, обязательные маркеры и отсутствие служебных фрагментов.
Критерии приемки
- буфер сохраняет неполные строки
- keep-alive пропускаются
- DONE завершает цикл
- частичный ответ помечается при обрыве
- usage записан после финала
Хороший продакшен-тест хранит эталонный вход и машинно проверяемое условие, а не полный эталонный текст. Генеративная модель может переформулировать корректный ответ. Проверяйте факты, набор полей, enum, диапазоны, ссылки на исходные документы и запрет на нежелательные действия. Отдельно измеряйте качество и инфраструктурные параметры: быстрый, дешевый, но неверный ответ не проходит приемку.
Типичные ошибки и что не делать
- Считать каждый сетевой chunk одной JSON-записью.
- Отбрасывать остаток неполной строки между чтениями.
- Закрывать соединение после текста и терять финальный usage.
Также не делайте бесконечный retry на 4xx. Ошибки авторизации, неподдерживаемый параметр и пустой набор endpoint обычно требуют изменения конфигурации, а не повтора того же тела. Для 429 и временных 5xx используйте ограниченное число повторов с экспоненциальной задержкой и случайным разбросом. Не записывайте исходные персональные данные в логи ради отладки.
Проверка перед продакшеном
Создайте таблицу из 20–50 реальных, но обезличенных примеров: короткий запрос, длинный контекст, пустое поле, кириллица, числа, конфликтующие инструкции и заведомо неподдерживаемая комбинация. Для каждого примера укажите допустимый результат. Запускайте набор при смене model slug, provider preferences, SDK или системного промпта.
После запуска постройте три метрики: доля технически успешных запросов, доля ответов, прошедших содержательную валидацию, и стоимость успешного результата. Последняя метрика полезнее средней цены запроса: она учитывает повторы и отбраковку. Для потоковой выдачи добавьте время до первого токена и долю соединений, дочитанных до финального события.
FAQ
Можно ли проверять настройку одним запросом?
Нет. Один ответ подтверждает только один маршрут в один момент. Нужна серия и хотя бы один отрицательный тест.
Нужно ли хранить API-ключ в браузере?
Нет. Запросы с постоянным ключом отправляют через backend, а секрет хранят в менеджере секретов или переменной окружения.
Почему HTTP 200 недостаточно?
Потому что ответ может иметь неверный формат, быть обрезанным или прийти от неожиданной модели либо endpoint.
Что сохранять для диагностики?
HTTP-код, request id, model, provider при доступности, finish_reason, usage, длительность и результат локальной валидации — без лишних персональных данных.
Официальный источник
Частые вопросы
Достаточно ли HTTP 200?
Нет, нужно проверить формат, model, finish_reason и usage.
Где хранить API-ключ?
Только на backend в переменной окружения или менеджере секретов.
Нужен ли отрицательный тест?
Да, он подтверждает безопасную обработку ошибки и запрет тихой деградации.
Можно ли доверять model slug навсегда?
Нет, каталог и endpoint нужно проверять перед изменением продакшена.
Читайте также
Как анализировать PDF через OpenRouter API без повторного парсинга
Передача PDF URL или Base64, file-parser, native/cloudflare-ai/mistral-ocr, annotations и повторное использование hash для экономии.
Как снизить стоимость OpenRouter с вариантом :floor
Как работает :floor: сортировка endpoint по цене, допуск flex tier, отличие от provider.sort=price и расчет стоимости успешного результата.
Как использовать Free Models Router OpenRouter и проверить выбранную модель
Запуск openrouter/free: ограничения бесплатной маршрутизации, проверка model, поддержка vision/tools/JSON и критерии, когда нельзя использовать в продакшене.
Комментарии
Пока тихо. Скажите первое слово