Потоковая передача
Установите "stream": true, и тот же самый эндпоинт будет возвращать ответ постепенно, через server-sent events, вместо того чтобы ждать полного завершения генерации.
Зачем использовать потоковую передачу
Запрос без потоковой передачи блокируется, пока модель не закончит генерировать весь ответ целиком, и только потом возвращает его целиком за один раз. Для короткого ответа это почти незаметно. Для длинного это означает ожидание в несколько секунд (иногда дольше), в течение которых на экране ничего не происходит, прежде чем появится хоть какой-то текст. Потоковая передача решает эту проблему, отправляя ответ по частям, по мере того как модель его генерирует, так что клиент может начать отрисовывать текст уже через доли секунды после отправки запроса, а не ждать всё целиком. Это разница между чат-интерфейсом, который ощущается мгновенным, и тем, который кажется зависшим на каждом сообщении. Это опциональная возможность, и она не стоит ничего дополнительно.
Включение потоковой передачи
{
"model": "tai-4.1",
"messages": [{"role": "user", "content": "1'den 5'e kadar say"}],
"stream": true
}
Content-Type ответа становится text/event-stream вместо application/json. Каждая строка события имеет префикс data: , а соединение остаётся открытым и отправляет по одному событию на каждую порцию нового контента, пока поток не завершится буквальной строкой data: [DONE].
Формат чанка
Большинство чанков несут небольшой фрагмент только что сгенерированного текста в choices[0].delta.content:
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {"content": "1"}, "finish_reason": null}]}
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {"content": ", 2"}, "finish_reason": null}]}
Последовательное соединение всех значений delta.content по порядку, по всем чанкам, восстанавливает полный текст ответа. Формирование этой конкатенации — задача клиента; API ни на каком этапе не отправляет накопленный текст, только очередной инкремент, добавляемый каждым чанком.
Финальный чанк
Последний чанк, содержащий контент, несёт пустой delta и настоящий finish_reason ("stop" или "tool_calls", если модель запрашивает вызов инструмента, см. Вызов инструментов):
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}
Чанк с использованием токенов
После финального чанка отправляется ещё один чанк с пустым массивом choices и заполненным объектом usage, того же формата, что и поле usage в ответе без потоковой передачи:
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3, "total_tokens": 5}}
data: [DONE]
Клиент, которому важен только видимый текст, может безопасно игнорировать любой чанк с пустым массивом choices; клиент, который хочет показать использование токенов или оценочную стоимость по завершении, должен искать именно этот чанк, а не полагаться на то, что финальный чанк его содержит.
Чтение потока в коде
# Python, с использованием официального пакета openai stream = client.chat.completions.create( model="tai-4.1", messages=[{"role": "user", "content": "Kısa bir şiir yaz"}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)
Официальные SDK OpenAI (для Python и Node) уже умеют разбирать этот формат событий, поскольку это тот же формат, который использует их собственный API; никакой специфики tAI в клиенте не требуется, кроме указания base_url как https://api.artfical.com/v1, как описано на странице Аутентификация.
Поток всегда завершается корректно
Даже если что-то идёт не так на середине пути — генерация завершилась неудачей, запрос истёк по внутреннему таймауту, причина не важна — поток всё равно заканчивается видимым сигналом, а не просто обрывом соединения без объяснений. В этом случае вы увидите чанк с содержимым, включающим короткое уведомление в скобках, финальный чанк с "finish_reason": "stop", а затем [DONE] — то же корректно оформленное завершение, что и у обычного успешного потока. Клиенту следует всегда считать появление [DONE] однозначным концом хода, и не следует считать успешным поток, завершившийся без него; оборванное соединение без [DONE] означает, что запрос нужно считать неудавшимся и повторить, точно так же, как для запроса без потоковой передачи, у которого истёк таймаут.