Responses API
POST /v1/responses принимает запросы в формате OpenAI Responses API. Base URL — https://go2llm.tech/v1, авторизация — Authorization: Bearer YOUR_API_KEY.
Внутри это адаптер протокола поверх chat.completions, а не отдельный маршрут биллинга. Из этого следуют два правила, которые лучше узнать здесь, чем по 400:
- Обслуживаются ровно те же модели, что на
POST /v1/chat/completions. Модель, у которой вGET /v1/modelsвendpointsтолькоmessages, через/v1/responsesнедоступна и вернёт400 model_protocol_mismatch. responsesне появляется в полеendpointsкаталога.endpointsперечисляет, что продаётся на модели, а не то, как с нами можно разговаривать. Не ищите тамresponses— ориентируйтесь наchat.completions.
Резерв, фактическое списание, лимит RPM и usage.cost_usd — те же, что на chat.completions. Отдельного тарифа у Responses нет.
Быстрая проверка
curl https://go2llm.tech/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "gpt-5.6-luna",
"input": "Ответь одним словом: работает",
"max_output_tokens": 32
}'
Ответ:
{
"id": "resp_...",
"object": "response",
"status": "completed",
"model": "gpt-5.6-luna",
"output": [
{
"type": "message",
"id": "msg_...",
"role": "assistant",
"status": "completed",
"content": [{ "type": "output_text", "text": "Работает", "annotations": [] }]
}
],
"usage": {
"input_tokens": 14,
"output_tokens": 3,
"total_tokens": 17,
"input_tokens_details": { "cached_tokens": 0 },
"cost_usd": 0.0000013
}
}
usage.cost_usd — фактическое списание в USD, то же поле и в том же смысле, что на остальных маршрутах. Если ответ упёрся в max_output_tokens, status будет incomplete, а причина — в incomplete_details.reason.
Что поддержано
| Поле | Поведение |
|---|---|
input строкой | превращается в одно сообщение пользователя |
input массивом | элементы message с input_text, input_image, output_text |
instructions | системная инструкция, ставится перед input |
tools, tool_choice | плоская форма Responses: {"type":"function","name",...} |
function_call / function_call_output | цикл вызова инструмента внутри input |
max_output_tokens | ограничение длины ответа |
stream | SSE с именованными событиями |
parallel_tool_calls | пробрасывается как есть |
response_format, text.format | структурированный вывод |
Что отклоняется и почему
Шим stateless: шлюз не хранит историю ответов. Хранить её значит завести персистентное хранилище с собственным TTL и приватностью ради того, чем агентные клиенты и так не пользуются — они присылают полную историю каждый ход. Поэтому вместо молчаливой поломки на втором ходу приходит названный отказ:
| Поле | Ответ |
|---|---|
previous_response_id | 400 stateful_responses_unsupported |
store: true | 400 stateful_responses_unsupported |
background: true | 400 — фоновых задач нет |
curl -i https://go2llm.tech/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{"model":"gpt-5.6-luna","input":"дальше","previous_response_id":"resp_abc"}'
{
"error": {
"type": "invalid_request_error",
"code": "stateful_responses_unsupported",
"message": "previous_response_id is not supported: this gateway stores no response state; send the full history in `input`",
"request_id": "gw_..."
}
}
Лечится одной правкой на стороне клиента: не передавать previous_response_id, а складывать предыдущие элементы output обратно в input следующего запроса. store в теле можно просто не отправлять или слать false.
Python — OpenAI SDK, без потока
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["GO2LLM_API_KEY"],
base_url="https://go2llm.tech/v1",
)
response = client.responses.create(
model="gpt-5.6-luna",
instructions="Отвечай одной строкой, без вступлений.",
input="Назови три способа ускорить холодный старт контейнера",
max_output_tokens=300,
)
print(response.output_text)
print(response.usage) # у шлюза там ещё cost_usd
Python — поток
Responses-поток состоит из именованных SSE-событий, а не безымянных data:-кадров. Текст собирается из response.output_text.delta, конец — response.completed.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["GO2LLM_API_KEY"],
base_url="https://go2llm.tech/v1",
)
stream = client.responses.create(
model="gpt-5.6-luna",
input="Напиши SQL-запрос, считающий DAU по таблице events",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
print()
print(event.response.usage)
Порядок событий в текстовом ответе: response.created → response.in_progress → response.output_item.added → response.content_part.added → response.output_text.delta (много) → response.output_text.done → response.content_part.done → response.output_item.done → response.completed. У tool call вместо текстовых приходят response.function_call_arguments.delta и .done.
После первого валидного SSE-фрагмента шлюз не переключает маршрут и не повторяет запрос автоматически — это общее правило, а не особенность Responses. Клиенту тоже не стоит ретраить начатый поток вслепую.
Python — вызов инструмента
В Responses описание инструмента плоское: name, description и parameters лежат прямо в объекте, без вложенного function.
import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["GO2LLM_API_KEY"],
base_url="https://go2llm.tech/v1",
)
tools = [
{
"type": "function",
"name": "get_order_status",
"description": "Возвращает статус заказа по его номеру",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
}
]
def get_order_status(order_id: str) -> dict:
# здесь ходит ваш код, не модель
return {"order_id": order_id, "status": "shipped", "eta_days": 2}
conversation = [
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Что с заказом A-1043?"}],
}
]
first = client.responses.create(
model="gpt-5.6-luna",
input=conversation,
tools=tools,
)
for item in first.output:
if item.type == "function_call":
result = get_order_status(**json.loads(item.arguments))
conversation.append(
{
"type": "function_call",
"call_id": item.call_id,
"name": item.name,
"arguments": item.arguments,
}
)
conversation.append(
{
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result, ensure_ascii=False),
}
)
second = client.responses.create(
model="gpt-5.6-luna",
input=conversation,
tools=tools,
)
print(second.output_text)
Инструмент выполняет ваше приложение; модель только просит его вызвать. Оба запроса тарифицируются отдельно, каждый со своим usage.cost_usd.
TypeScript
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.GO2LLM_API_KEY,
baseURL: "https://go2llm.tech/v1",
});
const response = await client.responses.create({
model: "gpt-5.6-luna",
input: "Ответь: OK",
max_output_tokens: 32,
});
console.log(response.output_text);
Клиенты
/v1/responses опубликован и проверен живым прогоном Codex CLI 0.151.0 2026-09-14 на модели qwen3-8-flash — статус и настройка описаны на странице инструментов для кода. Cursor живым запросом мы не проверяли и статус по нему не заявляем.
Ошибки маршрута и заголовок x-go2llm-request-id — на странице Ошибки API, полная таблица маршрутов — в Маршрутах API, цены по моделям — в каталоге и на странице цен.