halfoff.devДокументация
Получить ключ
Models API

OpenAI Chat Completions

Вызывайте GPT, Claude и Gemini за полцены через уже имеющийся OpenAI SDK. Usage в потоке, вызов функций, рассуждения и Responses API.

Любой код, написанный под OpenAI SDK, может работать на Half Off Dev, если заменить базовый URL на https://halfoff.dev/v1, а ключ на свой. POST /v1/chat/completions следует определению OpenAI, а тело пересылается как есть, поэтому справочник API OpenAI применим поле в поле. Вы платите половину прайсовой цены за каждый токен, а тот же клиент может обращаться и к Claude, и к Gemini.

Переключите клиент#

curl https://halfoff.dev/v1/chat/completions \
  -H "Authorization: Bearer $HALFOFF_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "gpt-6.1-sol",
    "messages": [
      {"role": "system", "content": "You are a terse SQL tutor."},
      {"role": "user", "content": "When should I use a LEFT JOIN?"}
    ]
  }'

Поскольку SDK читают OPENAI_BASE_URL и OPENAI_API_KEY, большинству приложений, говорящих на протоколе OpenAI, достаточно задать эти два значения. Несколько примеров приведено в разделе OpenAI-совместимые приложения.

Какие модели можно вызывать здесь#

Все текстовые модели из GET /v1/models сегодня принимают Chat Completions, включая Claude и Gemini. Это самый простой путь, если нужен один клиент для всех вендоров.

СемействоПримеры idЧто важно знать
GPTgpt-6.1-sol, gpt-6-astra, gpt-5.6-terra, gpt-5.6-lunaРодной формат.
Claudeclaude-sonnet-5.5, claude-opus-5.5Для cache_control или настроек thinking вызывайте их через Anthropic Messages.
Geminigemini-3.1-pro, gemini-3-flashДля thinkingConfig и других полей, специфичных для Gemini, используйте Gemini API.

Параметры, принадлежащие одному вендору, гарантированно работают только в формате этого вендора. См. нативный формат для каждого семейства моделей.

Потоковая передача с usage в конце#

Включите stream: true. Шлюз всегда добавляет stream_options.include_usage: true, поэтому последний фрагмент перед data: [DONE] содержит пустой массив choices и usage вызова. Прежде чем читать delta, проверяйте, что choices не пуст.

stream = client.chat.completions.create(
    model="gpt-6.1-sol",
    messages=[{"role": "user", "content": "List the planets in order from the sun."}],
    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("\nTokens:", chunk.usage.prompt_tokens, "in,", chunk.usage.completion_tokens, "out")

Точный формат протокола и то, как сбои проявляются посреди потока, описаны в разделе Потоковая передача.

Вызов функций#

Перечислите функции в tools. Когда модель хочет вызвать одну из них, finish_reason становится tool_calls; выполните функцию и отправьте обратно сообщение tool с соответствующим tool_call_id.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "check_stock",
        "description": "Return how many units of a product are in stock.",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
        },
    },
}]
messages = [{"role": "user", "content": "Do we still have SKU KB-204 in stock?"}]

completion = client.chat.completions.create(model="gpt-6.1-sol", messages=messages, tools=tools)
msg = completion.choices[0].message
if msg.tool_calls:
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = {"sku": args["sku"], "units": 17}  # your code here
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
    completion = client.chat.completions.create(model="gpt-6.1-sol", messages=messages, tools=tools)
print(completion.choices[0].message.content)

tool_choice, parallel_tool_calls и response_format пересылаются без изменений.

Уровень рассуждений#

Модели GPT-5 и GPT-6 думают перед ответом. Насколько усердно, задаётся через reasoning_effort, а общий объём вывода, включая рассуждения, ограничивается через max_completion_tokens. Допустимые значения effort зависят от модели; они перечислены в руководстве OpenAI по рассуждениям.

completion = client.chat.completions.create(
    model="gpt-6.1-sol",
    reasoning_effort="high",
    max_completion_tokens=8000,
    messages=[{"role": "user", "content": "Why does 0.1 + 0.2 != 0.3 in most languages?"}],
)
print(completion.choices[0].message.content)
print("reasoning tokens:", completion.usage.completion_tokens_details.reasoning_tokens)

Chat Completions не возвращает текст рассуждений. Его токены входят в usage.completion_tokens и тарифицируются по цене выходных токенов.

Responses API#

Для моделей GPT POST /v1/responses обслуживает OpenAI Responses API, на котором работает Codex CLI. Тот же базовый URL, тот же ключ; client.responses.create() менять не нужно.

curl https://halfoff.dev/v1/responses \
  -H "Authorization: Bearer $HALFOFF_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "gpt-6.1-sol",
    "instructions": "Reply with a single word.",
    "input": "What is the opposite of latency-bound?"
  }'

Потоковая передача (stream: true) доставляет типизированные события API, от response.created через response.output_text.delta до response.completed; тарифицируется usage из response.completed. Что следует помнить:

  • На сервере ничего не хранится. GET, DELETE и отмена на /v1/responses/{id} возвращают 404, а previous_response_id работает, только пока разговор случайно остаётся на одном канале. Отправляйте полную историю в input с "store": false, как это делает Codex.
  • Только GPT. Любая другая модель отвечает 404 с not served in responses format; используйте Chat Completions или её нативный API.
  • instructions в ответе повторяет то, что вы отправили, или null, если ничего не отправляли.
  • prompt_cache_key удерживает разговор на одном канале, как и в Chat Completions.

Кеширование промптов#

Модели OpenAI кешируют длинные префиксы промптов без запроса. Кешированная часть отражается в usage.prompt_tokens_details.cached_tokens и тарифицируется по сниженной цене чтения кеша. prompt_cache_key повышает долю попаданий, а также сообщает шлюзу, какие запросы образуют один разговор. См. Кеширование промптов.

Что не поддерживается#

Chat Completions и Responses API — конечные точки в формате OpenAI. Следующее отвечает 404:

  • Сохранённые ответы: GET, DELETE или отмена на /v1/responses/{id}.
  • Эмбеддинги: /v1/embeddings.
  • Аудио: /v1/audio/*.
  • Устаревшие Completions, Assistants, файлы, пакеты, дообучение и модерация.

client.models.list() работает с GET /v1/models; получение одной модели по id — нет.

Заголовки запроса#

ЗаголовокПоведение
Authorization: BearerВаш ключ Half Off Dev. Обязателен.
openai-betaПересылается без изменений.
x-halfoff-groupНеобязателен. Выбирает группу каналов для этого запроса, если ключ ещё не привязан к группе.
x-session-idНеобязателен. Помечает запросы как один разговор для маршрутизации с учётом кеша. prompt_cache_key или user в теле действуют так же.

Ошибки имеют формат OpenAI, {"error": {"message": "...", "type": "...", "code": "..."}}; см. Ошибки.