Документация API go2llm

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ограничение длины ответа
streamSSE с именованными событиями
parallel_tool_callsпробрасывается как есть
response_format, text.formatструктурированный вывод

Что отклоняется и почему

Шим stateless: шлюз не хранит историю ответов. Хранить её значит завести персистентное хранилище с собственным TTL и приватностью ради того, чем агентные клиенты и так не пользуются — они присылают полную историю каждый ход. Поэтому вместо молчаливой поломки на втором ходу приходит названный отказ:

ПолеОтвет
previous_response_id400 stateful_responses_unsupported
store: true400 stateful_responses_unsupported
background: true400 — фоновых задач нет
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, цены по моделям — в каталоге и на странице цен.