> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# В чём разница между вызовами с потоковой передачей и без неё?

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

## Краткий ответ

<Info>
  **Три предложения:**

  1. **Streaming и non-streaming полностью определяются вашим собственным кодом** — полем `stream` в теле запроса. Один и тот же ключ, одна и та же модель, один и тот же endpoint: если оно переключается туда-сюда, это делает ваш код клиента (или SDK / framework, который его оборачивает). **Шлюз никогда не переключает это случайным образом.**
  2. **Оба режима возвращают одинаковый конечный результат и тарифицируются одинаково.** Единственные различия — *когда* вы получаете текст и *как* вы его разбираете.
  3. **Как выбрать**: человек смотрит на экран → streaming; программу потребляет результат (разбор JSON, пакетные задачи, вызовы tools) → non-streaming.
</Info>

## Различия в сравнении

| Аспект                        | Потоковая передача `stream: true`                                                                                    | Без потоковой передачи (по умолчанию)                     |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Параметр запроса              | `stream: true`                                                                                                       | не указывается или `stream: false`                        |
| Формат ответа                 | поток событий SSE (`text/event-stream`), много `data:` фрагментов, завершается `data: [DONE]`                        | один полный объект JSON                                   |
| Чтение текста                 | накапливать `choices[0].delta.content` фрагмент за фрагментом                                                        | читать `choices[0].message.content` напрямую              |
| Время до первого байта (TTFB) | быстрое, обычно 1–3 с на обычных моделях                                                                             | ≈ общее время генерации                                   |
| Общая задержка                | примерно такая же, как при без потоковой передачи                                                                    | примерно такая же, как при потоковой передаче             |
| `usage`                       | **по умолчанию не возвращается**; требуется `stream_options: {"include_usage": true}`                                | всегда присутствует в теле ответа                         |
| Форма ошибки                  | соединение уже открыто, поэтому ошибки могут возникать в середине потока и должны обрабатываться внутри цикла чтения | один HTTP status code + error JSON — самый простой случай |
| Длительные паузы без данных   | редко (данные продолжают поступать)                                                                                  | часто (соединение молчит всю генерацию)                   |
| Сложность интеграции          | средняя: инкрементальная сборка, разбор SSE, отключение буферизации                                                  | низкая: один запрос, один разбор                          |
| Тарификация                   | за token                                                                                                             | **точно такая же**                                        |
| Лог консоли                   | `is_stream = true`                                                                                                   | `is_stream = false`                                       |

## Почему мои запросы переключаются между потоковой передачей и непотоковым режимом?

Это самый частый вопрос, и ответ такой: **что-то на вашей стороне это меняет.** Пройдитесь по этому списку — почти всегда совпадает один из пунктов:

<AccordionGroup>
  <Accordion title="1. `stream` — это переменная или значение конфигурации в вашем коде">
    Классический случай: `stream=config.get("stream", False)` или `stream=is_web_request`. Разные точки входа вызывают одну и ту же функцию с разными значениями, и по логам кажется, будто режим переключается случайно.

    **Как проверить**: выведите фактическое тело запроса, которое вы отправляете, и посмотрите на поле `stream`.
  </Accordion>

  <Accordion title="2. У разных SDK и фреймворков разные значения по умолчанию">
    Одна и та же бизнес-логика ведёт себя по-разному в зависимости от клиента:

    * OpenAI SDK `chat.completions.create()`: **без потоковой передачи** по умолчанию
    * `client.chat.completions.stream()` или `with_streaming_response`: **потоковая передача**
    * Обёртки вроде LangChain / LlamaIndex: зависит от того, вызываете ли вы `invoke` или `stream`, и передавали ли вы `streaming=True` при создании объекта модели
    * Настольные клиенты, инструменты агентов, платформы рабочих процессов: обычно в настройках есть переключатель «потоковый вывод», а значения по умолчанию отличаются

    **Как проверить**: убедитесь, какая точка входа фактически выполнила вызов.
  </Accordion>

  <Accordion title="3. Один ключ используется несколькими приложениями">
    Один ключ, используемый и в веб-интерфейсе чата (потоковая передача), и в ночной пакетной задаче (без потоковой передачи), создаёт логи, которые при совместном просмотре выглядят случайными.

    **Как проверить**: создайте отдельные tokens для каждого сценария использования — тогда логи сами разделятся. См. [Управление token](/ru/faq/token-management).
  </Accordion>

  <Accordion title="4. Промежуточное устройство сгладило потоковую передачу">
    Вы действительно отправили `stream: true`, но Nginx, корпоративный gateway или какой-то proxy **буферизовал** ответ — сервер отправлял его порциями, proxy удержал его и выпустил весь сразу, и это ощущается как отсутствие потоковой передачи.

    **Как проверить**: один раз протестируйте, обойдя proxy; отключите буферизацию в Nginx (`proxy_buffering off;`). Обратите внимание, что в этом случае лог консоли по-прежнему показывает `is_stream = true`, потому что gateway действительно выполнил потоковую передачу.
  </Accordion>
</AccordionGroup>

<Tip>
  **Чтобы подтвердить, что именно сделало конкретное обращение**: проверьте поле `is_stream` в логе консоли или получите его массово через [Log Query API](/en/api-capabilities/log-query). Это источник истины — куда надёжнее, чем субъективное впечатление.
</Tip>

## Выбор по сценарию

<CardGroup cols={2}>
  <Card title="Используйте потоковую передачу" icon="zap">
    * Чат-интерфейсы и боты поддержки — пользователям нужна немедленная обратная связь
    * Плагины IDE / ассистенты для программирования (Claude Code, Cursor и т. д.)
    * Генерация длинных материалов (длинные статьи, длинные переводы, большие блоки кода)
    * Длительные задачи для моделей с рассуждением — по крайней мере вы можете видеть прогресс
    * Везде, где пользователь может нажать «стоп» в середине генерации
  </Card>

  <Card title="Используйте режим без потоковой передачи" icon="package">
    * Структурированный вывод: вам нужен весь JSON для `json.loads()`
    * Разбор аргументов function-calling / tool-call
    * Пакетная обработка, автономные задачи, запланированные задачи
    * Серверные сценарии, где важен только итоговый результат и никто не ждет
    * Быстрая проверка, отладка, написание тест-кейсов
  </Card>
</CardGroup>

Несколько особых случаев:

| Сценарий                                          | Рекомендация                                              | Примечание                                                                                                                                                              |
| ------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Генерация изображений / редактирование            | без потоковой передачи                                    | Совместимый с OpenAI `/v1/images/generations` не принимает `stream`; собственный API Gemini для генерации изображений имеет отдельный `:streamGenerateContent` эндпоинт |
| Генерация видео                                   | не связано с потоковой передачей                          | Асинхронная задача + polling, см. [Асинхронные API для генерации изображений/видео](/ru/faq/image-async-api)                                                            |
| Embedding / Rerank                                | без потоковой передачи                                    | У этих эндпоинтов нет понятия потоковой передачи                                                                                                                        |
| Модели с рассуждением (обдумывание / рассуждение) | потоковая передача удобнее, но **не устраняет тайм-ауты** | Во время рассуждения модель может вообще ничего не выдавать — см. заблуждение 1 ниже                                                                                    |

## Затраты на интеграцию: одна и та же задача в обе стороны

<Tabs>
  <Tab title="Python без потоковой передачи">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="sk-your-apiyi-key",
        base_url="https://api.apiyi.com/v1",
    )

    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": "Explain quantum computing"}],
        timeout=120,
    )

    # Full text in one line
    print(resp.choices[0].message.content)
    # usage is right there in the response body
    print(resp.usage.total_tokens)
    ```
  </Tab>

  <Tab title="Python с потоковой передачей">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="sk-your-apiyi-key",
        base_url="https://api.apiyi.com/v1",
    )

    stream = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": "Explain quantum computing"}],
        stream=True,
        stream_options={"include_usage": True},   # without this, no usage
        timeout=120,
    )

    chunks = []
    for chunk in stream:
        # the final usage chunk has an empty choices array — check before indexing
        if chunk.choices and chunk.choices[0].delta.content:
            piece = chunk.choices[0].delta.content
            chunks.append(piece)
            print(piece, end="", flush=True)
        if chunk.usage:
            print(f"\nUsage: {chunk.usage.total_tokens} tokens")

    full_text = "".join(chunks)   # assemble it yourself if you need the whole thing
    ```
  </Tab>

  <Tab title="Node.js с потоковой передачей">
    ```javascript theme={null}
    import OpenAI from "openai";

    const client = new OpenAI({
      apiKey: "sk-your-apiyi-key",
      baseURL: "https://api.apiyi.com/v1",
    });

    const stream = await client.chat.completions.create({
      model: "gpt-5.4",
      messages: [{ role: "user", content: "Explain quantum computing" }],
      stream: true,
      stream_options: { include_usage: true },
    });

    let full = "";
    for await (const chunk of stream) {
      const delta = chunk.choices?.[0]?.delta?.content;
      if (delta) {
        full += delta;
        process.stdout.write(delta);
      }
      if (chunk.usage) console.log("\nUsage:", chunk.usage.total_tokens);
    }
    ```
  </Tab>

  <Tab title="cURL в сравнении">
    ```bash theme={null}
    # Non-streaming: one complete JSON
    curl https://api.apiyi.com/v1/chat/completions \
      -H "Authorization: Bearer $APIYI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gpt-5.4",
        "messages": [{"role": "user", "content": "Hello"}]
      }'

    # Streaming: a series of data: chunks, ending with data: [DONE]
    # -N disables curl's own buffering, otherwise it still looks like one blob
    curl -N https://api.apiyi.com/v1/chat/completions \
      -H "Authorization: Bearer $APIYI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "gpt-5.4",
        "messages": [{"role": "user", "content": "Hello"}],
        "stream": true,
        "stream_options": {"include_usage": true}
      }'
    ```
  </Tab>
</Tabs>

<Note>
  **Собственный формат Claude (`/v1/messages`) использует другой протокол потоковой передачи**: SSE с именованными событиями Anthropic (`message_start` / `content_block_delta` / `message_delta` и т. д.), а не единообразные чанки OpenAI `data:`, и `usage` разбивается между событиями `message_start` и `message_delta`. Полное руководство по парсингу: [Нативный формат Claude: ответы с потоковой передачей и без нее](/ru/api-capabilities/claude-response-handling).
</Note>

## Тарификация и использование: одинаково в любом случае

<Warning>
  **streaming не дешевле и не дороже.** Тарификация идет по token и не зависит от того, как передаются байты.

  **Отключение посередине все равно тарифицируется** — после того как вы достигнете `Ctrl+C` или у вашего клиента истечет таймаут, генерация на upstream все равно будет завершена, и запрос будет оплачен обычным образом. Поэтому «обрубить stream раньше, чтобы сэкономить» не работает.
</Warning>

Два подводных камня, связанных с `usage`:

1. **streaming по умолчанию не возвращает usage.** На endpoint, совместимых с OpenAI, вы должны передать `stream_options: {"include_usage": true}`; после этого usage приходит в финальном чанке (чей массив `choices` пуст — проверьте перед индексированием). На APIYI это подтверждено на нескольких моделях.
2. **Не сверяйте ваш счет с `usage`, которые возвращает API**, особенно с полями, связанными с кэшем. Возвращаемые значения не всегда совпадают с тем, что было фактически тарифицировано; факт попадания в кэш определяется по **«деталям тарификации кэша» в логах консоли**. См. [Как объясняется тарификация кэша](/ru/faq/cache-billing).

В любом случае лог консоли записывает количество token, задержку и тарификацию для каждого вызова — режим передачи не имеет значения. Значения полей: [Понимание деталей тарификации в логах](/ru/faq/log-billing-explained).

## Шесть распространённых заблуждений

<AccordionGroup>
  <Accordion title="1. Потоковая передача предотвращает тайм-ауты">
    **Это не так.** Потоковая передача лишь позволяет *первому* token прийти раньше. Она не сокращает общее время генерации и не гарантирует непрерывный поток данных.

    Модели с рассуждением (`gemini-3.1-pro-preview`, `gpt-5.6-sol`, `gpt-5.5-pro` и т. д.) **могут вообще не выдавать ничего на этапе размышления**, что точно так же приводит к срабатыванию тайм-аута чтения у клиента.

    Правильное решение — задавать значения timeout для каждого сценария — см. [Как избежать тайм-аутов API](/ru/faq/timeout-configuration).
  </Accordion>

  <Accordion title="2. Потоковая передача быстрее">
    **Первый байт приходит быстрее; в целом — нет.** Для одной и той же модели и одного и того же prompt потоковая передача и без потоковой передачи завершаются примерно за одно и то же время.

    Потоковая передача даёт вам *ощущение* скорости: пользователь видит движение уже через секунду, а не смотрит на индикатор загрузки 30 секунд. Если никто не смотрит на экран, это значение равно нулю.
  </Accordion>

  <Accordion title="3. Потоковая передача дешевле или тарифицирует только то, что вы получили">
    **Нет.** См. «Тарификация и использование» выше: тарификация идентична, и при отключении на середине запрос всё равно тарифицируется.
  </Accordion>

  <Accordion title="4. Каждая модель и каждый endpoint поддерживают потоковую передачу">
    **Нет.** Модели текстового чата обычно поддерживают; endpoints для генерации изображений, embedding и rerank не имеют понятия потоковой передачи и либо проигнорируют `stream`, либо отклонят его.

    У некоторых моделей есть дополнительные ограничения для определённых сочетаний параметров в режиме потоковой передачи. Если не уверены, сначала добейтесь, чтобы вызов работал без потоковой передачи, а затем добавьте `stream: true`.
  </Accordion>

  <Accordion title="5. Без потоковой передачи надёжнее">
    **У обоих есть свои режимы отказа.**

    * Риски без потоковой передачи: соединение молчит на протяжении всей генерации, поэтому прокси, CDN и корпоративные шлюзы могут разорвать его по тайм-ауту простоя. При очень больших телах ответа (base64-вывод изображений легко достигает десятков МБ) вы также можете столкнуться с зависшим завершающим обработчиком — см. [Запросы, которые полностью передали данные, но так и не вернулись](/ru/api-capabilities/image-tail-stall) и [В журнале указано завершение, но клиент ничего не получает](/ru/faq/log-duration-vs-client-wait).
    * Риски потоковой передачи: она плохо совместима с промежуточными устройствами, которые не поддерживают SSE или принудительно буферизуют данные; разбор на стороне клиента сложнее, и в нём легко допустить малозаметную ошибку.

    Также учтите: `api-cf.apiyi.com` (эндпоинт CDN) имеет примерно 100-секундный предел для запроса, который **затрагивает оба режима**. Для длительных запросов используйте `api.apiyi.com` или `vip.apiyi.com` — см. [Руководство по настройке Base URL](/ru/faq/base-url-config).
  </Accordion>

  <Accordion title="6. Нельзя получить полный ответ из stream">
    **Можно — вы просто собираете его сами.** Если последовательно объединить `delta.content` каждого chunk, вы получите в точности тот же `message.content`, что и без потоковой передачи.

    Если собранный текст выглядит неполным, проверьте три вещи: не проигнорировали ли вы `finish_reason`, не вышли ли вы из цикла до получения `data: [DONE]` и не обрезал ли ответ промежуточный узел.
  </Accordion>
</AccordionGroup>

## Потоковая передача не работает? Четыре шага

<Steps>
  <Step title="Убедитесь, что тело запроса действительно содержит stream: true">
    Выведите JSON, который вы реально отправляете. При использовании библиотек-обёрток «Я думал, что передал это» и «это было передано» часто оказываются разными вещами.
  </Step>

  <Step title="Проверьте напрямую с помощью curl -N">
    Обойдите свой код и любой прокси, используя команду на вкладке «cURL бок о бок» выше. Если curl показывает, что фрагменты приходят постепенно, на стороне сервера всё в порядке, а проблема в вашем клиенте или промежуточном устройстве.
  </Step>

  <Step title="Проверьте буферизацию промежуточного устройства">
    Добавьте `proxy_buffering off;` в Nginx. Корпоративные шлюзы и средства безопасности могут сканировать `text/event-stream` как единый полезный груз — попросите сетевого администратора разрешить его прохождение.
  </Step>

  <Step title="Проверьте логику разбора">
    Читайте SSE построчно, пропускайте пустые строки и строки комментариев, начинающиеся с `:`, и останавливайтесь на `data: [DONE]`. Последний фрагмент, содержащий `usage`, имеет пустой массив `choices` — не обращайтесь к нему по индексу.
  </Step>
</Steps>

<Tip>
  Если вы дошли до этого места без ответа, **свяжитесь со службой поддержки с `request_id`** — журнал консоли прямо показывает, был ли этот вызов обработан как потоковая передача, а также его общую задержку и время до первого байта.
</Tip>

## Связанная документация

<CardGroup cols={2}>
  <Card title="Как избежать таймаутов API" icon="timer" href="/ru/faq/timeout-configuration">
    Значения таймаута по сценариям и почему потоковая передача не спасает вас
  </Card>

  <Card title="Руководство по настройке Base URL" icon="link" href="/ru/faq/base-url-config">
    Различия между эндпоинтами и 100-секундный предел узла CDN
  </Card>

  <Card title="Лог показывает завершение, но ответа нет" icon="stethoscope" href="/ru/faq/log-duration-vs-client-wait">
    Классическая проблема большого ответа без потоковой передачи, с таймингом сегментов
  </Card>

  <Card title="Claude: потоковая передача и режим без потоковой передачи" icon="braces" href="/ru/api-capabilities/claude-response-handling">
    Разбор собственного SSE-протокола named-event от Anthropic
  </Card>

  <Card title="API генерации текста" icon="message-square" href="/ru/api-capabilities/text-generation">
    Полный список параметров и примеры вызова
  </Card>

  <Card title="Понимание деталей тарификации в логах" icon="file-text" href="/ru/faq/log-billing-explained">
    Что означает каждое поле лога в консоли, включая is\_stream
  </Card>
</CardGroup>
