Чтение потокового ответа
Обработка событий протокола без подмены итогового ответа частичным содержимым.
Укажите stream: true для эндпоинта/модели с постепенным выводом. Сохраняйте соединение открытым и разбирайте Server-Sent Events как поток. HTTP-чанки и события SSE могут не совпадать по границам: накапливайте неполные строки и блоки событий до разбора их JSON.
Запрос
curl -N https://api.b2b-llm.com/v1/chat/completions \
-H "Authorization: Bearer $B2B_API_KEY" -H 'Content-Type: application/json' \
-d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Write a two-line project update."}],"stream":true}'
События ответа
data: {"choices":[{"index":0,"delta":{"content":"Project"}}]}
data: {"choices":[{"index":0,"delta":{"content":" update"},"finish_reason":null}]}
data: [DONE]
Это пример Chat Completions. Anthropic использует именованные события, например message_start, content_block_delta и message_stop; Responses — события ответа и выходных элементов. Не применяйте обработчик только Chat к другому семейству событий.
Ошибки и учёт usage
Соединение с успешным началом позже может передать ошибку поставщика или завершиться до конечного события. Считайте частичный текст частичным, сохраняйте ID запроса и показывайте понятное состояние ошибки. Не отправляйте платный запрос повторно автоматически только из-за потери соединения браузером. Usage может появиться только в конечном событии или позднее в истории завершённого расчёта; ранний чанк не является итоговой записью учёта.
Потоковый режим и собственный отложенный async: true нельзя сочетать. Отключайте буферизацию своего промежуточного сервиса только при сохранении границ событий и отмены выбранного протокола.
Сверка потока с использованием проекта
Запрос организации может сохранить HTTP200, хотя исход потока — provider_error, transport_error или incomplete. Responses должен дойти до response.completed, собственный Messages — до message_stop, а Chat — завершить каждый запрошенный вариант либо передать [DONE]. Само закрытие транспорта не подтверждает завершение. Не повторяйте частичный запрос проекта автоматически.
Источником расчёта остаются реальные счётчики поставщика, даже если usage получен частично. Для успешно завершённого текстового потока без счётчиков шлюз помечает запасную оценку как estimated и считает декодированные байты сгенерированных delta, а не повторяющиеся транспортные оболочки; заданный максимум вывода ограничивает оценку. Неуспешный ответ без измерений показывается как неизвестное usage для проверки, без автоматического списания по транспортным байтам. Это не подтверждает отсутствие расходов у поставщика. Сохраните ID запроса для финансового расследования компании; права членства/проекта не меняются.
Поддержание соединения — не прогресс проекта
Между событиями содержимого интеграция может получать строки комментариев SSE (:). Они поддерживают установленный поток, но не должны попадать в сгенерированный отчёт, счётчик токенов или usage компании. Читайте полные события SSE с разделителями LF, CRLF или CR, даже когда строка приходит несколькими чтениями.
В Chat Completions причина завершения может предшествовать итоговому usage поставщика; продолжайте чтение до [DONE]. Responses и собственный Messages заканчиваются на response.completed и message_stop. Шлюз закрывает чтение поставщика на правильном конечном событии протокола, не удерживая запрос до закрытия сокета поставщиком.
Длительное молчание поставщика приводит к ошибке потока, а не выдуманному тексту или успешному завершению. Сохраните частичный результат и ID запроса; новая платная попытка требует явного решения пользователя. Ожидание первых HTTP-заголовков — отдельный этап, на котором комментариев SSE ещё нет; полученный комментарий не доказывает, что поставщик генерирует содержимое проекта.