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

# Потоковые ответы и вызов инструментов через BetterToken API

> Настройте потоковые ответы и вызов инструментов в BetterToken API: события Chat Completions, Responses и Messages, схемы функций и передача результатов.

Включите потоковую выдачу через `stream: true` и опишите инструменты, которые выполняет ваше приложение, в формате выбранного API. Поддержка потоков и вызова инструментов зависит от модели. Прежде чем использовать эти возможности в приложении, проверьте небольшой запрос с вашим Model ID.

## Подготовка

Сначала выполните обычный текстовый запрос по [примерам SDK](/guides/sdk-examples.md). Скопируйте точный Model ID из [каталога моделей](https://bettertoken.ai/pricing) для нужного протокола. Критерии проверки описаны в [руководстве по выбору модели](/faq/model-calling/model-selection-guide.md).

Установите `openai` и `anthropic` в виртуальное окружение Python вашего проекта, затем замените заполнители в том же терминале:

```bash theme={null}
python -m pip install openai anthropic
export BETTERTOKEN_API_KEY="YOUR_API_KEY"
export BETTERTOKEN_MODEL_ID="YOUR_MODEL_ID"
```

Каждый блок Python ниже можно запускать отдельно. Тайм-аут 120 секунд выбран для этих коротких тестов. Для приложения используйте [рекомендации по тайм-аутам](/best-practices.md#задайте-большой-timeout). Перед настройкой `max_tokens` или `max_output_tokens` проверьте [лимит выходных токенов](/faq/model-calling/max-tokens.md) модели.

## Потоковая выдача текста

В потоковом режиме сервер передаёт события server-sent events (SSE) в одном HTTP-ответе. Отдельное событие SSE не обязательно содержит законченное предложение или полный JSON-объект аргументов.

| API | Настройка запроса | Текстовые события или фрагменты | Завершение ответа |
| - | - | - | - |
| [Chat Completions](/api-reference/chat-completions.md) | `stream: true` | `choices[].delta.content`; фрагменты вызовов инструментов — `delta.tool_calls` | Элемент ответа содержит `finish_reason`; необработанный поток заканчивается на `data: [DONE]` |
| [Responses](/api-reference/responses.md) | `stream: true` | Типизированные события, например `response.output_text.delta` | `response.completed`; также обрабатывайте события ошибки и неполного ответа |
| [Messages](/api-reference/messages.md) | `stream: true` | `content_block_delta` с `text_delta`; аргументы инструментов — `input_json_delta` | `message_delta` содержит причину остановки, за ним следует `message_stop` |

SDK разбирает формат SSE. Примеры выводят текстовые фрагменты и сведения о завершении; в приложении также обрабатывайте события ошибок.

**Chat Completions**

```python theme={null}
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai/v1",
    timeout=120.0,
    max_retries=0,
)
with client.chat.completions.create(
    model=os.environ["BETTERTOKEN_MODEL_ID"],
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
    stream=True,
) as stream:
    for chunk in stream:
        for choice in chunk.choices:
            if choice.delta.content:
                print(choice.delta.content, end="", flush=True)
            if choice.finish_reason:
                print("\nFinish reason:", choice.finish_reason)
```

**Responses**

```python theme={null}
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai/v1",
    timeout=120.0,
    max_retries=0,
)
with client.responses.create(
    model=os.environ["BETTERTOKEN_MODEL_ID"],
    input="Say hello in one sentence.",
    stream=True,
) as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)
        elif event.type in (
            "response.completed", "response.failed", "response.incomplete", "error"
        ):
            print("\n", event)
```

**Messages**

```python theme={null}
import os
from anthropic import Anthropic

client = Anthropic(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai",
    timeout=120.0,
    max_retries=0,
)
with client.messages.create(
    model=os.environ["BETTERTOKEN_MODEL_ID"],
    max_tokens=256,
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
    stream=True,
) as stream:
    for event in stream:
        if event.type == "content_block_delta" and event.delta.type == "text_delta":
            print(event.delta.text, end="", flush=True)
        elif event.type == "message_delta":
            print("\nStop reason:", event.delta.stop_reason)
```

## Опишите инструмент погоды для каждого протокола

Модель запрашивает вызов функции, а ваше приложение проверяет аргументы, выполняет функцию и возвращает результат. В примерах функция `get_weather` принимает аргумент `location` и возвращает **фиксированные демонстрационные данные, а не текущую погоду**. Подключать погодный сервис или получать дополнительный ключ не нужно.

| Поле запроса или ответа | Chat Completions | Responses | Messages |
| - | - | - | - |
| Запрос пользователя | `messages` | `input` | `messages` |
| Объявление инструмента в `tools[]` | `type: "function"`; `name`, `description` и `parameters` внутри `function` | `type: "function"`; `name`, `description` и `parameters` на верхнем уровне инструмента | `name`, `description` и `input_schema` |
| Возвращённый вызов | `choices[].message.tool_calls[]` | Элемент `output[]` с `type: "function_call"` | Блок `content[]` с `type: "tool_use"` |
| Аргументы | JSON-строка в `function.arguments` | JSON-строка в `arguments` | Объект в `input` |
| Передача результата | Сообщение с `role: "tool"`, `tool_call_id` и `content` | Входной элемент с `type: "function_call_output"`, `call_id` и строкой `output` | Блок пользовательского сообщения с `type: "tool_result"`, `tool_use_id` и `content` |

Поддержка зависит от модели и протокола. Проверьте небольшой запрос с инструментом для вашего Model ID, даже если обычный текст уже работает. Объявление инструмента не включает поиск или другие инструменты, выполняемые на стороне поставщика.

## Выполните вызов и верните результат

Эти примеры без потоковой выдачи показывают один цикл вызова инструмента, чтобы вы могли изучить аргументы целиком. Каждый пример обрабатывает все вызовы из первого ответа и отправляет один последующий запрос. Для более длинного агентного сценария задайте ограничение числа итераций и условие остановки.

**Chat Completions**

```python theme={null}
import json
import os
from openai import OpenAI

def get_weather(location):
    if not isinstance(location, str) or not location.strip():
        raise ValueError("location must be a non-empty string")
    return {"location": location, "temperature_c": 20, "source": "demo"}

client = OpenAI(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai/v1", timeout=120.0, max_retries=0,
)
model = os.environ["BETTERTOKEN_MODEL_ID"]
schema = {
    "type": "object",
    "properties": {"location": {"type": "string"}},
    "required": ["location"], "additionalProperties": False,
}
tools = [{"type": "function", "function": {
    "name": "get_weather", "description": "Get demonstration weather for a location.",
    "parameters": schema,
}}]
messages = [{"role": "user", "content": "Call get_weather for Paris and report the demo result."}]
first = client.chat.completions.create(model=model, messages=messages, tools=tools)
message = first.choices[0].message
calls = message.tool_calls or []
if not calls:
    print("No tool call returned:", message.content)
else:
    messages.append(message.model_dump(exclude_none=True))
    for call in calls:
        if call.type != "function" or call.function.name != "get_weather":
            raise ValueError("Unexpected tool")
        args = json.loads(call.function.arguments)
        result = get_weather(**args)
        messages.append({
            "role": "tool", "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(model=model, messages=messages, tools=tools)
    print(final.choices[0].message.model_dump(exclude_none=True))
```

**Responses**

```python theme={null}
import json
import os
from openai import OpenAI

def get_weather(location):
    if not isinstance(location, str) or not location.strip():
        raise ValueError("location must be a non-empty string")
    return {"location": location, "temperature_c": 20, "source": "demo"}

client = OpenAI(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai/v1", timeout=120.0, max_retries=0,
)
model = os.environ["BETTERTOKEN_MODEL_ID"]
schema = {
    "type": "object",
    "properties": {"location": {"type": "string"}},
    "required": ["location"], "additionalProperties": False,
}
tools = [{
    "type": "function", "name": "get_weather",
    "description": "Get demonstration weather for a location.", "parameters": schema,
}]
history = [{"role": "user", "content": "Call get_weather for Paris and report the demo result."}]
first = client.responses.create(model=model, input=history, tools=tools)
calls = [item for item in first.output if item.type == "function_call"]
if not calls:
    print("No tool call returned:", first.output)
else:
    history.extend(first.output)
    for call in calls:
        if call.name != "get_weather":
            raise ValueError("Unexpected tool")
        args = json.loads(call.arguments)
        result = get_weather(**args)
        history.append({
            "type": "function_call_output", "call_id": call.call_id,
            "output": json.dumps(result),
        })
    final = client.responses.create(model=model, input=history, tools=tools)
    print(final.output)
```

**Messages**

```python theme={null}
import json
import os
from anthropic import Anthropic

def get_weather(location):
    if not isinstance(location, str) or not location.strip():
        raise ValueError("location must be a non-empty string")
    return {"location": location, "temperature_c": 20, "source": "demo"}

client = Anthropic(
    api_key=os.environ["BETTERTOKEN_API_KEY"],
    base_url="https://bettertoken.ai", timeout=120.0, max_retries=0,
)
model = os.environ["BETTERTOKEN_MODEL_ID"]
schema = {
    "type": "object",
    "properties": {"location": {"type": "string"}},
    "required": ["location"], "additionalProperties": False,
}
tools = [{
    "name": "get_weather", "description": "Get demonstration weather for a location.",
    "input_schema": schema,
}]
messages = [{"role": "user", "content": "Call get_weather for Paris and report the demo result."}]
first = client.messages.create(model=model, max_tokens=1024, messages=messages, tools=tools)
calls = [block for block in first.content if block.type == "tool_use"]
if first.stop_reason == "max_tokens":
    raise RuntimeError("Response truncated; inspect it before executing tools")
if not calls:
    print("No tool call returned:", first.content)
else:
    messages.append({"role": "assistant", "content": first.content})
    results = []
    for call in calls:
        if call.name != "get_weather":
            raise ValueError("Unexpected tool")
        result = get_weather(**call.input)
        results.append({
            "type": "tool_result", "tool_use_id": call.id,
            "content": json.dumps(result),
        })
    messages.append({"role": "user", "content": results})
    final = client.messages.create(model=model, max_tokens=1024, messages=messages, tools=tools)
    print(final.content)
```

Сохраните полный ответ ассистента перед добавлением результатов инструментов. Для Responses оставляйте все элементы `output`, включая элементы рассуждений, если они вернулись. Для Messages сохраняйте блоки содержимого и их порядок. Привязывайте результаты к полученным идентификаторам вызовов, не создавайте новые ID.

## Частые вопросы

### Можно ли выполнять инструмент по мере поступления аргументов?

Соберите фрагменты по индексу вызова или ID элемента, затем разберите и проверьте полные аргументы. Неполный JSON может не разбираться. Прежде чем объединять потоки и инструменты, запустите примеры без потоковой выдачи выше.

### Почему текст работает, а инструменты — нет?

Поддержка зависит от модели и протокола. Проверьте небольшой вызов инструмента с вашим Model ID. Сверьте схему инструмента, полученные аргументы, ID результата и порядок сообщений. О типизированной проверке JSON читайте в [FAQ по структурированному выводу](/faq/model-calling/structured-json-pydantic.md).

### Нужно ли повторять запрос, если поток прервался?

Перед повтором проверьте запись запроса. Разрыв соединения не показывает, успел ли запрос завершиться. Повтор создаёт новую попытку; он не должен повторно выполнять уже совершённое действие инструмента. См. [ошибки и правила повторов](/api-reference/errors.md).

## См. также

* [Настройка SDK и авторизация](/guides/sdk-examples.md)
* [Обзор API](/api-reference/introduction.md)
* [Лимит выходных токенов](/faq/model-calling/max-tokens.md)
* [Структурированный JSON и Pydantic](/faq/model-calling/structured-json-pydantic.md)
* [Настройка тайм-аутов](/best-practices.md#задайте-большой-timeout)
* [Ошибки и ограничения](/api-reference/errors.md)

## Официальные источники

Источники описывают поля исходных протоколов. Перед использованием проверьте нужную возможность с вашим BetterToken Model ID.

* [Вызов функций OpenAI](https://developers.openai.com/api/docs/guides/function-calling)
* [Потоковые ответы OpenAI](https://developers.openai.com/api/docs/guides/streaming-responses)
* [Объявление инструментов Anthropic](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools)
* [Результаты инструментов Anthropic](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls)
* [Потоковые Messages Anthropic](https://platform.claude.com/docs/en/build-with-claude/streaming)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.