Стриминг
Форматы событий SSE в трёх диалектах и советы по разбору.
Все три диалекта поддерживают стриминг по Server-Sent Events (SSE): дельты приходят по мере генерации, ждать полного ответа не нужно. Форматы событий различаются по диалектам и совпадают с официальными API.
Формат OpenAI (Responses)
Укажите "stream": true в теле запроса. События содержат поле типа: response.created, response.output_text.delta, response.completed:
stream = client.responses.create(
model="claude-fable-5",
input="Tell me a story",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)data: {"type":"response.created", ...}
data: {"type":"response.output_text.delta","delta":"Once"}
data: {"type":"response.output_text.delta","delta":" upon"}
data: {"type":"response.completed","response":{..., "usage":{...}}}Формат Anthropic (Messages)
Тоже "stream": true, но события используют именованные события SSE (строки event: + data:): message_start → content_block_start → серия content_block_delta → content_block_stop → message_delta → message_stop.
event: message_start
data: {"type":"message_start","message":{...}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Once"}}
event: message_stop
data: {"type":"message_stop"}Формат Gemini
Вызывайте действие :streamGenerateContent с ?alt=sse; каждая строка data: — фрагмент GenerateContentResponse, дельта-текст в candidates[].content.parts[].text. Без alt=sse вернётся полный JSON-массив (без стриминга).
curl "https://api.soleapi.com/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse" \
-H "x-goog-api-key: $SOLEAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contents": [{"parts": [{"text": "Tell me a story"}]}]}'Советы по разбору
- События SSE разделяются пустыми строками, но TCP-сегментация может разорвать событие между чтениями — буферизуйте ввод и разбирайте только целые кадры. Это самая частая ошибка в самописных парсерах.
- Для долгих генераций увеличьте таймаут чтения до 5+ минут, чтобы клиент не обрывал reasoning-модели во время долгих пауз.
- Повтор после обрыва — это новый запрос (поток нельзя возобновить с середины); рассмотрите откат на нестриминговый вызов.
- При отрисовке UI группируйте дельты с троттлингом, чтобы каждая мелкая дельта не вызывала отдельную перерисовку.
- Использование токенов при стриминге приходит в финальном событии; списание происходит после завершения потока.