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 | Что важно знать |
|---|---|---|
| GPT | gpt-6.1-sol, gpt-6-astra, gpt-5.6-terra, gpt-5.6-luna | Родной формат. |
| Claude | claude-sonnet-5.5, claude-opus-5.5 | Для cache_control или настроек thinking вызывайте их через Anthropic Messages. |
| Gemini | gemini-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": "..."}}; см. Ошибки.