Как работает шлюз
Один хост нативно обслуживает API Anthropic, OpenAI и Gemini. Конечные точки, базовые URL, допустимые id моделей, заголовки аутентификации и то, что Half Off Dev изменяет.
Смысл Half Off Dev в том, что вы сохраняете уже написанный код. Claude, GPT и Gemini обслуживаются каждый в собственном формате своего вендора, на одном хосте, за половину прайсовой цены. Ваш SDK отправляет тот же JSON, что и вендору; меняются только базовый URL и ключ.
Прослойки-переводчика посередине нет. Запрос в стиле Anthropic передаётся каналу, который говорит на Anthropic, запрос в стиле OpenAI — каналу, который говорит на OpenAI, и так далее. За исключением двух небольших правок, тело приходит так, как вы его написали, поэтому возможности вендоров вроде thinking, cache_control, tools или generationConfig работают ровно так, как описано в справочнике каждого вендора.
Конечные точки вкратце#
Передайте SDK базовый URL из этой таблицы и позвольте ему построить путь.
| Формат протокола | Пути | Базовый URL для SDK | Руководство |
|---|---|---|---|
| Anthropic Messages | POST /v1/messages, POST /v1/messages/count_tokens | https://halfoff.dev | Anthropic Messages |
| OpenAI Chat Completions | POST /v1/chat/completions | https://halfoff.dev/v1 | OpenAI |
| OpenAI Responses (только GPT) | POST /v1/responses | https://halfoff.dev/v1 | Responses API |
| Gemini | POST /v1beta/models/{model}:generateContent, :streamGenerateContent, :countTokens | https://halfoff.dev | Gemini |
| Каталог моделей | GET /v1/models, GET /v1beta/models | Цены |
Всё, чего нет в этом списке, не поддерживается; см. что вызвать нельзя.
Передача ключа#
Каждая конечная точка ищет ключ в четырёх местах, так что то, что SDK делает по умолчанию, просто работает:
| Место | Типичный отправитель |
|---|---|
Authorization: Bearer ho-... | OpenAI SDK, Claude Code через ANTHROPIC_AUTH_TOKEN, обычные HTTP-клиенты |
x-api-key: ho-... | Anthropic SDK |
x-goog-api-key: ho-... | Google Gen AI SDK, Gemini CLI |
?key=ho-... | Фрагменты REST для Gemini |
Две конечные точки каталога публичны и ключа не требуют. Создание, ограничение и ротация ключей описаны в разделе Аутентификация.
Нативный формат для каждого семейства моделей#
Каждое семейство спроектировано вокруг одного API. Обращаясь к нему там, вы получаете все возможности вендора.
| Семейство | Нативный формат | Также принимается сегодня |
|---|---|---|
Claude (claude-*) | Anthropic Messages | OpenAI Chat Completions, Gemini |
GPT (gpt-*) | OpenAI Chat Completions, OpenAI Responses | Anthropic Messages, Gemini |
Gemini (gemini-*) | Gemini | OpenAI Chat Completions, Anthropic Messages |
Использование ненативного формата означает, что канал преобразует ваш запрос для модели, и параметры, специфичные для вендора, по пути могут быть отброшены или отклонены. cache_control и thinking у Anthropic, reasoning_effort у OpenAI и thinkingConfig у Gemini гарантированно работают только в родном формате модели.
Какие форматы принимает модель, зависит от обслуживающих её каналов, поэтому список может меняться. Маркетплейс моделей показывает актуальные форматы для каждой модели. Если ни один канал не принимает ваш формат, вы получите 404 с сообщением вроде Model 'gpt-6.1-sol' is not served in gemini format in any group.
Id моделей#
Канонические id — те, что в GET /v1/models, например claude-opus-5.5, gpt-6.1-sol или gemini-3-flash. Инструменты, которые пишут id по-своему, всё равно попадают на нужную модель, потому что шлюз нормализует полученное:
| Отправлено | Трактуется как |
|---|---|
Claude-Opus-5-5 | claude-opus-5.5 |
anthropic/claude-opus-5.5 | claude-opus-5.5 |
claude-haiku-4-5-20251001 | claude-haiku-4.5 |
gemini-3.1-pro-preview | gemini-3.1-pro |
models/gemini-3-flash | gemini-3-flash |
Нормализация приводит id к нижнему регистру, отбрасывает ведущие vendor/ или models/, считает . и - одним и тем же символом и убирает завершающую дату (-20251001), -preview или -latest. Биллинг, логи и списки разрешённых моделей ключа всегда записывают канонический id.
Id, который ни с чем не сопоставился, возвращает 404: Model '<id>' is not available. See GET /v1/models.
Путь одного запроса#
- Проверка ключа. Неактивный или просроченный ключ получает
401. - Поиск модели. Id нормализуется, как описано выше, и сравнивается со списком разрешённых моделей ключа; модель вне списка получает
403. - Проверка баланса. Баланс, равный нулю или ниже, либо ключ, исчерпавший лимит расходов, получает
402. - Лимиты ключа. Действуют ограничения ключа на число запросов в минуту и на параллелизм (
429). Подробности в разделе Лимиты запросов и переключение. - Выбор канала. Уже идущий разговор возвращается на канал, где лежит его кеш промпта; новый попадает на самый здоровый канал своей группы каналов. См. Кеширование промптов.
- Вызов модели. Канал, отказавший до какого-либо вывода, заменяется другим, всего до трёх попыток. Потоки удерживаются до первого токена, так что замена незаметна. См. Потоковая передача.
- Учёт. Число токенов в ответе определяет списание по половине прайсовой цены, и оно уходит с баланса в течение нескольких секунд. См. Цены.
Что шлюз изменяет#
В тело вносятся две правки, и больше ничего:
model. Шлюз записывает id в написании, которое ожидает обслуживающий канал. В результате полеmodelв ответе может иметь форму вендора, напримерclaude-opus-5-5.- Usage в потоках OpenAI. Каждый потоковый запрос Chat Completions получает
stream_options.include_usage: true, чтобы вызов можно было тарифицировать. Поэтому ваш поток заканчивается одним дополнительным фрагментом, у которого массивchoicesпуст и который несётusage. Убедитесь, что ваш цикл терпимо относится к пустомуchoices.
Отправленные вами заголовки отбрасываются, за тремя исключениями, которые пересылаются: anthropic-version, anthropic-beta и openai-beta. Если anthropic-version отсутствует, подставляется 2023-06-01. На путях Gemini параметры запроса вроде alt=sse проходят, а key удаляется.
Тела ответов возвращаются байт в байт. content-type и cache-control сохраняются, а шлюз добавляет собственные заголовки; id запросов вендоров и заголовки лимитов вендоров не передаются.
Если модель отклоняет запрос как некорректный (400, 404, 413, 422), вы получаете этот статус и причину в формате ошибок того API, к которому обращались. Любой другой сбой сначала повторяется на другом канале. Подробнее в разделах Лимиты запросов и переключение и Ошибки.
Заголовки в каждом ответе#
| Заголовок | Содержимое |
|---|---|
x-halfoff-request-id | Id этого вызова, вроде req_6f1c.... Это ключ к записи в Логах; приводите его, когда пишете в поддержку. |
x-halfoff-group | Группа каналов, которая ответила, например gpt-value. |
x-halfoff-channel | Метка канала этой группы в URL-кодировке, например Pooled%20capacity. |
x-halfoff-queued-ms | Присутствует, только если вызов ждал освобождения мощности: время ожидания в миллисекундах. |
Первые три перечислены в access-control-expose-headers, поэтому клиентский JavaScript может их читать.
Что вызвать нельзя#
Конечные точки из таблицы выше — вся поверхность для моделей. В частности, не обслуживаются:
- OpenAI: сохранённые ответы (
/v1/responses/{id}), устаревшие Completions, эмбеддинги, аудио, файлы и пакеты. Сам Responses API работает только для моделей GPT. - Anthropic: Message Batches, Files и получение одной модели по id.
- Gemini:
cachedContents(явное кеширование контекста), загрузка файлов,embedContentи Live API.
О подключении конкретного клиента см. Интеграции.