Lovely RouterДокументация
Получить ключ
Models API

Потоковая передача

Получайте токены по мере генерации во всех трёх форматах API. Как шлюз скрывает ранние сбои, как проявляются зависания и ошибки посреди потока и сколько стоит отменённый поток.

Потоки используют server-sent events в собственном формате вендора. Байты, которые вы получаете, — это байты, выданные моделью; шлюз лишь следит за ними, чтобы подсчитать usage и защитить вас от сбоев.

Включение потоковой передачи#

APIКак запросить потокЧем заканчивается поток
Anthropic Messages"stream": true в телеevent: message_stop
OpenAI Chat Completions"stream": true в телеdata: [DONE]
GeminiPOST /v1beta/models/{model}:streamGenerateContent?alt=sseСобытие, у кандидата которого есть finishReason

Код приведён в руководствах по SDK: Anthropic, OpenAI, Gemini.

Как выглядит поток#

SSE
event: message_start
data: {"type":"message_start","message":{"id":"msg_01HCz7","type":"message","role":"assistant","model":"claude-sonnet-5-5","content":[],"stop_reason":null,"usage":{"input_tokens":14,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1}}}

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"One, two, three."}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}}

event: message_stop
data: {"type":"message_stop"}

Ранние сбои повторяются за вас#

Иногда канал принимает запрос, а затем отказывает, не выдав ничего, например отправив message_start, а за ним ошибку перегрузки. Шлюз придерживает начало каждого потока, пока не придёт первый токен содержимого (текст, thinking или вызов инструмента). Если канал отказывает до этого момента, до вас ещё ничего не дошло, поэтому шлюз повторяет запрос на другом канале и вы получаете чистый поток с самого начала.

Канал, который просто медленно стартует, тоже учитывается: для моделей Claude без extended thinking канал, не выдавший первый токен за 10 секунд (плюс 1 секунда на каждые 10K токенов промпта), заменяется другим, если только он не последний доступный. Запросы с thinking могут длиться столько, сколько думает модель. Непотоковые запросы подчиняются тому же правилу.

Что это значит для вашего клиента:

  • HTTP-статус и заголовки приходят вместе с первым токеном, поэтому время до первого байта равно времени до первого токена.
  • Если все каналы отказывают до первого токена, вы получаете обычный HTTP-ответ с ошибкой в формате ошибок вашего API, а не оборванный поток.
  • Удержание длится не более 15 секунд. Модель, которая молча думает дольше, начинает передачу в этот момент, и с этого времени запрос закреплён за своим каналом.
  • Давайте ответу не менее 30 секунд на старт. Запрос также может сначала несколько секунд ждать свободной мощности; см. Лимиты запросов и переключение.

Зависания и ошибки посреди потока#

Когда токены пошли, запрос закреплён за одним каналом и больше не может переместиться. Завершить его досрочно могут ещё две вещи.

Зависание. Если канал ничего не отправляет 3 минуты, шлюз завершает поток событием ошибки в формате вашего API и закрывает соединение:

SSE
event: error
data: {"type":"error","error":{"type":"api_error","message":"Upstream stopped responding mid-stream"}}

Ошибка от канала. Канал может сообщить об ошибке внутри идущего потока, например о перегрузке модели. Вместо собственного текста канала вы получаете обобщённое событие ошибки в формате вашего API: overloaded_error (в OpenAI — rate_limit_exceeded, в Gemini — RESOURCE_EXHAUSTED), если канал занят, иначе api_error (в Gemini — UNAVAILABLE) с сообщением The model provider returned an error. Please retry.

В обоих случаях поток заканчивается без своего обычного последнего события (message_stop, data: [DONE] или finishReason). Считайте поток, закончившийся без него, неполным: сохраните или отбросьте частичный вывод, а затем повторите весь запрос, если вам нужен полный ответ. SDK Anthropic и OpenAI выбрасывают исключение, получив событие ошибки.

Отмена потока#

Чтобы остановить генерацию, закройте соединение, например выйдя из цикла или вызвав stream.close() / controller.abort(). Шлюз останавливает модель и списывает плату за то, что было выдано до этого момента:

  • Для входа берётся число, которое поток уже сообщил (Anthropic сообщает его в начале), а если ничего не сообщалось — оценка примерно в один токен на четыре символа вашего запроса.
  • Для выхода берётся большее из двух: то, что поток сообщил на этот момент, или оценка примерно в один токен на четыре символа уже отправленного вам текста. Оборванный поток редко сообщает итоговое число выходных токенов, поэтому обычно применяется оценка.

Поток, завершившийся ошибкой зависания, тарифицируется так же — за то, что вы получили. Каждый отменённый или зависший поток отображается в Логах со своей суммой списания. См. Модели и цены.