В OpenAI-совместимых API теперь есть два основных формата запросов: давно используемый /v1/chat/completions и более новый /v1/responses. GetVibe API поддерживает оба с одним ключом и одним базовым URL — https://getvibeapi.com/v1. Разберёмся, чем они отличаются на практике, какой выбрать и о какой ловушке нужно помнить при переходе между ними.
Кратко
| Chat Completions | Responses | |
|---|---|---|
| Эндпоинт | POST /v1/chat/completions | POST /v1/responses |
| Входные данные | массив messages | input: строка или массив элементов |
| Системный промпт | сообщение с ролью system | поле instructions |
| Ответ | choices[0].message | массив output: рассуждение, сообщение, вызовы функций |
| Лимит вывода | max_tokens | max_output_tokens |
| Кто использует | большинство SDK, фреймворков и чат-приложений | Codex CLI и более новые SDK для агентов |
Формат не меняет модель, цену или количество токенов: в любом случае вы платите за входные и выходные токены модели.
Один и тот же запрос в обоих форматах
Chat Completions:
curl https://getvibeapi.com/v1/chat/completions \
-H "Authorization: Bearer $GETVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"messages": [
{"role": "system", "content": "Ответьте одним предложением."},
{"role": "user", "content": "Что означает HTTP 429?"}
],
"max_tokens": 300
}'
Responses:
curl https://getvibeapi.com/v1/responses \
-H "Authorization: Bearer $GETVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-luna",
"instructions": "Ответьте одним предложением.",
"input": "Что означает HTTP 429?",
"max_output_tokens": 300
}'
В Chat Completions текст находится в choices[0].message.content. В Responses он находится внутри output: у моделей с рассуждением первым элементом является элемент типа reasoning, а ответом — элемент типа message. Вам не нужно перебирать массив вручную: в Python SDK есть свойство output_text.
import os
from openai import OpenAI
client = OpenAI(base_url="https://getvibeapi.com/v1", api_key=os.environ["GETVIBE_API_KEY"])
chat = client.chat.completions.create(
model="gpt-6-luna",
messages=[{"role": "system", "content": "Ответьте одним предложением."},
{"role": "user", "content": "Что означает HTTP 429?"}],
)
print(chat.choices[0].message.content)
resp = client.responses.create(
model="gpt-6-luna",
instructions="Ответьте одним предложением.",
input="Что означает HTTP 429?",
)
print(resp.output_text)
Responses не ограничен моделями OpenAI. Перед публикацией мы отправили один и тот же запрос Responses моделям gpt-6-luna, claude-sonnet-5.5 и deepseek-v4.1-flash, и все три ответили. DeepSeek вернула элемент reasoning перед элементом message — именно так, как описано выше.
История диалога: передавайте её самостоятельно
Это единственное, что нужно знать перед переходом. Собственный Responses API OpenAI может хранить диалог на сервере, поэтому в следующем запросе можно указать предыдущий запрос с помощью previous_response_id. GetVibe API не хранит ваши диалоги, поэтому previous_response_id не поддерживается, а store игнорируется. Когда мы попробовали это сделать, запрос был отклонён с ошибкой 400.
Рабочий подход такой же, как в Chat Completions: передавайте всю историю в input.
history = [{"role": "user", "content": "Меня зовут Ana. Запомни это."}]
first = client.responses.create(model="gpt-6-luna", input=history)
history.append({"role": "assistant", "content": first.output_text})
history.append({"role": "user", "content": "Как меня зовут? Одним словом."})
second = client.responses.create(model="gpt-6-luna", input=history)
print(second.output_text) # Ana
Инструменты: другая структура
Вызов функций работает в обоих форматах, но определение инструмента выглядит по-разному. В Chat Completions поля функции вложены в объект function, а в Responses находятся непосредственно в инструменте. Вызов поступает как отдельный элемент function_call, а результат вы возвращаете как элемент function_call_output с тем же call_id.
import json
tools = [{
"type": "function",
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": False,
},
}]
items = [{"role": "user", "content": "Какая погода в Денвере?"}]
resp = client.responses.create(model="gpt-6-luna", input=items, tools=tools)
for item in resp.output:
if item.type == "function_call":
result = {"city": "Denver", "temp_f": 54, "sky": "overcast"} # ваша функция
items.append({"type": "function_call", "call_id": item.call_id,
"name": item.name, "arguments": item.arguments})
items.append({"type": "function_call_output", "call_id": item.call_id,
"output": json.dumps(result)})
final = client.responses.create(model="gpt-6-luna", input=items, tools=tools)
print(final.output_text)
В нашем запуске модель вызвала get_weather с параметром {"city":"Denver"}, а во втором запросе ответила, что в Денвере 54°F и пасмурно. Версия того же цикла для Chat Completions приведена в нашем руководстве по вызову функций.
Потоковая передача
Оба формата поддерживают "stream": true, но события отличаются. Chat Completions отправляет унифицированные строки data:, где каждый новый фрагмент текста находится в choices[0].delta.content. Responses отправляет именованные события: сначала response.created, затем response.output_text.delta с фрагментами текста и в конце response.completed. Если вы разбираете поток самостоятельно, а не через SDK, вам понадобится парсер для конкретного формата.
Что выбрать
- Chat Completions, если у вас уже есть код на нём или вы используете фреймворк, чат-приложение либо платформу без кода. Его поддерживает почти каждый инструмент, и нет причин переписывать рабочий код только ради смены формата.
- Responses, если этого требует инструмент. Главный пример — Codex CLI, который обращается к
/v1/responses; инструкции по настройке приведены в документации Codex CLI и в нашем руководстве по Codex CLI.
В любом случае список моделей и цены одинаковы. Все поля описаны в справочнике API, а актуальные цены указаны на странице моделей.
