Стриминг ответов
Со стримингом ответ приходит по частям, по мере генерации: первые слова видны через доли секунды, а не после того, как модель допишет всё. Стриминг работает в Chat Completions, Responses API, Anthropic Messages и API для Cursor и стоит столько же, сколько обычный запрос.
Как включить
Добавьте в запрос "stream": true. Ответ придёт потоком server-sent events (SSE). Флаг -N не даёт curl копить вывод, и события печатаются сразу:
curl -N https://infergate.ru/api/v1/chat/completions \
-H "Authorization: Bearer $INFERGATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Напиши короткое стихотворение"}],
"stream": true
}'SDK OpenAI разбирает поток сам:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://infergate.ru/api/v1",
api_key=os.environ["INFERGATE_API_KEY"],
)
stream = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Напиши короткое стихотворение"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage.cost_rub, "₽")Chat Completions
Каждое событие — строка data: с объектом chat.completion.chunk. Текст приходит кусками в choices[0].delta.content, вызовы инструментов — в choices[0].delta.tool_calls. Поток заканчивается строкой data: [DONE].
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"Над"}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" рекой"}}]}
data: [DONE]Расход токенов и стоимость приходят в последнем фрагменте перед [DONE], в поле usage. В этом фрагменте может не быть текста, а массив choices — оказаться пустым: проверяйте его, прежде чем читать choices[0]. Строки, которые начинаются с двоеточия (: keep-alive), — служебные, их можно пропускать; SDK делают это сами.
Так же устроен поток на адресе для Cursor.
Responses API
События типизированы, как в Responses API OpenAI: у каждого есть поле type. Обычный порядок такой: response.created, response.in_progress, response.output_item.added, response.content_part.added, затем куски текста в поле delta событий response.output_text.delta, после них response.output_text.done, response.content_part.done, response.output_item.done и в конце response.completed.
Событие response.completed содержит весь ответ целиком, а в response.usage — расход токенов и стоимость запроса.
Anthropic Messages
Поток в формате Anthropic: у каждого события есть строка event: с типом и строка data: с данными. Порядок такой:
message_start— начало ответа;content_block_start, затемcontent_block_deltaиcontent_block_stop— для каждого блока ответа. Текст — вdelta.text, рассуждения — вdelta.thinking, аргументы вызова инструмента — кусками JSON вdelta.partial_json;message_delta— причина остановки (stop_reason) и расход токенов вusage;message_stop— конец ответа.
Стоимости в этом формате нет — её показывает журнал запросов в личном кабинете.
Ошибки и обрывы
- Ошибки, найденные до начала генерации, приходят обычным HTTP-ответом с кодом ошибки, а не потоком. Например, если
max_tokensбольше, чем допускает модель, запрос получит422с кодомinvalid_max_tokens. Остальные коды — на странице «Ошибки». - Если генерация сорвалась, когда поток уже шёл, он закончится без
[DONE](в Anthropic Messages — безmessage_stop). Считайте такой ответ неполным и повторите запрос. Если поток оборвался до того, как пришёл расход, запрос не оплачивается. - Если вы сами закроете соединение раньше времени, генерация, как правило, не остановится: модель допишет ответ, и он будет оплачен целиком.
Повтор потокового запроса с заголовком Idempotency-Key приходит одним JSON, а не потоком.
Где стриминга нет
Эмбеддинги, реранкинг, решения и распознавание речи возвращают результат одним ответом, поле stream на них не влияет. Генерация изображений с "stream": true получает 422. Видео создаётся асинхронной задачей. Синтез речи отдаёт аудиофайл, который начинает приходить, как только готовы первые данные.