Формат Responses
POST /v1/responses принимает запросы в формате Responses.
Сравнение трёх форматов: обзор роутера. Как подключить клиент и какой метод SDK звать: OpenAI-совместимые клиенты.
Первый запрос
Заголовок раздела «Первый запрос»curl https://api.waibee.com/v1/responses \ -H "Authorization: Bearer $WAIBEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-5.1-codex", "input": "Скажи привет", "max_output_tokens": 64 }'Через официальный SDK OpenAI:
from openai import OpenAI
client = OpenAI(api_key="sk_live_...", base_url="https://api.waibee.com/v1")
response = client.responses.create( model="openai/gpt-5.1-codex", input="Скажи привет", max_output_tokens=64,)print(response.output_text)model это идентификатор из каталога моделей, тот же, что на остальных форматах. Доступны все модели каталога, включая Claude и Gemini.
Элементы вместо сообщений
Заголовок раздела «Элементы вместо сообщений»Chat Completions оперирует списком сообщений. Responses оперирует элементами: сообщение, вызов функции, результат вызова, рассуждение. У каждого свой type, свой id и свой status.
{ "model": "openai/gpt-5.1-codex", "input": [ {"type": "message", "role": "user", "content": "Что в файле README?"} ], "instructions": "Отвечай кратко."}instructions играет роль системного промпта и лежит на верхнем уровне, а не внутри списка.
Ответ приходит списком output из тех же элементов:
{ "id": "resp_0fe6078d9f604fdc8768e83bec097248", "status": "completed", "output": [ { "type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Это README проекта."}] } ], "usage": {"input_tokens": 14, "output_tokens": 6, "total_tokens": 20}}Идентификатор ответа выдаёт роутер. Идентификаторы элементов внутри output приходят от модели, и их можно отправлять обратно.
Многоходовой диалог
Заголовок раздела «Многоходовой диалог»Роутер не хранит переписку. Каждый следующий запрос несёт весь диалог целиком в input: то, что вы отправляли раньше, плюс элементы из output прошлого ответа, плюс новый ход.
{ "model": "openai/gpt-5.1-codex", "input": [ {"type": "message", "role": "user", "content": "Что в файле README?"}, {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Это README проекта."}]}, {"type": "message", "role": "user", "content": "А в CHANGELOG?"} ]}Отсюда два практических следствия.
Вход растёт с каждым ходом, и длинная сессия оплачивается как череда всё более крупных запросов. Смягчает это кеширование промпта: неизменившееся начало диалога считается по сниженной цене, и доля кеша видна в usage.input_tokens_details.cached_tokens.
Поля, которые просят сервер помнить диалог за вас, отклоняются. Список ниже, в разделе «Чего роутер не делает».
Вызов инструментов
Заголовок раздела «Вызов инструментов»Инструменты объявляются в tools, а цикл вызова живёт в тех же элементах input.
{ "model": "openai/gpt-5.1-codex", "input": [{"type": "message", "role": "user", "content": "Какая погода в Лимасоле?"}], "tools": [ { "type": "function", "name": "get_weather", "description": "Погода в городе", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } ]}Модель отвечает элементом function_call:
{"type": "function_call", "name": "get_weather", "arguments": "{\"city\":\"Limassol\"}", "call_id": "call_a1"}Вы выполняете вызов сами и отправляете результат следующим запросом, дописав в input и сам function_call, и ответ на него:
{ "input": [ {"type": "message", "role": "user", "content": "Какая погода в Лимасоле?"}, {"type": "function_call", "name": "get_weather", "arguments": "{\"city\":\"Limassol\"}", "call_id": "call_a1"}, {"type": "function_call_output", "call_id": "call_a1", "output": "+28, ясно"} ]}call_id связывает вызов с результатом. Круг повторяется, пока модель не ответит сообщением.
Как то же самое устроено на остальных форматах: Вызов инструментов.
Веб-поиск
Заголовок раздела «Веб-поиск»Встроенный тул {"type": "web_search"} выполняет роутер. Модели он отдаётся как обычная функция, поиск делается на нашей стороне, а результат возвращается модели следующим ходом. Внутренние ходы наружу не уходят: вы получаете готовый ответ и рядом с ним элемент waibee:web_search с запросом и списком источников.
{ "type": "waibee:web_search", "id": "ws_call_abc", "status": "completed", "action": { "type": "search", "query": "latest python release", "sources": [{ "type": "url", "url": "https://www.python.org/downloads/" }] }}Префикс waibee: наш, спецификация формата это разрешает. Элемент присылать обратно следующим ходом не нужно, роутер снимет его сам.
Лимиты, стоимость и поведение при недоступном поиске одинаковы на всех форматах, см. Веб-поиск.
Стриминг
Заголовок раздела «Стриминг»С "stream": true ответ приходит именованными событиями:
event: response.createddata: {"type":"response.created","response":{...}}
event: response.output_text.deltadata: {"type":"response.output_text.delta","delta":"При"}
event: response.completeddata: {"type":"response.completed","response":{...,"usage":{...}}}
data: [DONE]Поток открывается событием response.created и закрывается одним из response.completed, response.incomplete или response.failed, после чего приходит data: [DONE]. response.incomplete это обычное окончание, когда генерация упёрлась в max_output_tokens.
Порядок промежуточных событий зависит от модели, и незнакомые типы событий надо пропускать, а не считать ошибкой.
Стриминг на остальных форматах: Стриминг.
Чего роутер не делает
Заголовок раздела «Чего роутер не делает»Формат умеет хранить историю диалога на стороне сервера. Роутер этого не делает, поэтому четыре поля отклоняются с кодом feature_not_supported и HTTP 400:
| Поле | Когда отклоняется |
|---|---|
store |
значение true |
previous_response_id |
задано |
conversation |
задано |
background |
значение true |
{ "error": { "type": "invalid_request_error", "code": "feature_not_supported", "message": "'store' is not supported: this endpoint is stateless. Send the whole conversation in `input`.", "param": "store" }}Поле param называет параметр, из-за которого запрос отклонён. "store": false и отсутствие остальных полей это обычная форма запроса, она работает.
Чтение и удаление сохранённого ответа (GET и DELETE по /v1/responses/{id}), отмена запроса и фоновое выполнение недоступны по той же причине: всё это опирается на серверное состояние.
Плагины
Заголовок раздела «Плагины»DLP работает здесь так же, как на двух других форматах: конфигурация передаётся в поле plugins тела запроса, а правила, назначенные администратором на ваш ключ, применяются в любом случае.
Отличаются два имени полей, потому что их иначе зовёт сам формат:
| В примерах на странице плагинов | Здесь |
|---|---|
messages |
input |
системный промпт внутри messages |
instructions |
Находка называет то поле, которое вы отправили: input[0].content или instructions. При блокировке находки приходят в error.details.dlp.
Формат ошибок
Заголовок раздела «Формат ошибок»Ошибки приходят в формате самого Responses, чтобы клиентские SDK разбирали их штатно:
{ "error": { "type": "invalid_request_error", "code": "validation_error", "message": "..." }}На стриминге ошибка приходит событием error с телом той же формы, после чего поток закрывается data: [DONE].
Когда запрос отклоняет сама модель, в message приходит её объяснение: чего в теле не хватает или что в нём лишнее. Если объяснение касается не вашего запроса, а нашей стороны, вместо него приходит общая строка.
Коды и статусы общие для всех форматов: Ошибки.
Интеграции
Заголовок раздела «Интеграции»- Codex CLI: подключение агента OpenAI