Перейти к основному содержимому

Codex, Claude Code и Gemini CLI через сервер Chat Completions

Эта инструкция подключает один неизменяемый OpenAI-совместимый сервер /v1/chat/completions сразу к трём клиентским протоколам:

Codex (Responses) ───────────┐
Claude Code (Messages) ──────┼─> gpt2giga ─> /v1/chat/completions
Gemini CLI (GenerateContent) ┘

Маршрут имеет статус technical preview. gpt2giga переводит только проверенное чат-подобное подмножество, а запрос с непереносимым смыслом отклоняет до обращения к вышестоящему серверу. Шлюз не выдаёт Chat Completions за полную реализацию Responses, Messages или GenerateContent.

Требования к вышестоящему серверу​

Понадобятся:

  • точный Chat Completions endpoint либо базовый URL API;
  • точный идентификатор модели на сервере;
  • реальные лимиты контекста, входных и выходных токенов;
  • при необходимости bearer-токен;
  • обычный JSON-ответ Chat Completions;
  • либо Chat Completions SSE с завершающим кадром data: [DONE], либо явно включённый буферизованный режим из раздела ниже;
  • function tools и tool calls, если кодовые агенты должны вызывать локальные инструменты.
  • reasoning_effort и текстовый reasoning_content, если рассуждение Codex должно переживать перевод Responses и следующий ход с результатом tool call.

В стандартном SSE-режиме шлюз передаёт stream_options: {"include_usage": true}. В буферизованном режиме он вместо этого отправляет stream: false и использует usage из полного JSON-ответа. Если сервер не вернул usage, gpt2giga не придумывает значения токенов.

1. Опишите upstream​

Создайте providers.yaml вне репозитория:

schema_version: gpt2giga.provider-profiles.v3
profiles:
- profile_id: chat-only-upstream
provider_kind: openai_compatible
base_url: https://inference.example/v1/chat/completions
credential_env: CHAT_UPSTREAM_API_KEY
network_policy_ref: public-openai
tls_policy_ref: system-default
models:
- public_alias: bridge/chat-only
upstream_model: exact-upstream-model-id
capability_profile: chat-only-coding-v1
capabilities:
features:
- roles
- ordered_content_parts
- text
- generation_controls
- reasoning_controls_and_summaries
- function_tools
- tool_choice
- parallel_tool_calls
- tool_results
- stream_deltas
- stream_terminal_events
- stop_reason
- usage
- model_identity
- request_error_classes
- cancellation
- context_token_limits
limits:
context_window: 32768
max_input_tokens: 28672
max_output_tokens: 4096
support_status: technical_preview

Замените endpoint, модель и лимиты. public_alias — имя модели, которое будут использовать все три клиента. В upstream_model оно преобразуется только внутри шлюза.

Если сервер намеренно работает без ключа, удалите credential_env. Когда поле задано, gpt2giga читает указанную переменную при запуске и отправляет её значение как bearer-токен. Клиент не может подменить адрес, внутреннюю модель или учётные данные upstream.

Список features — это список реально проверенных возможностей, а не пожеланий. Добавляйте json_schema_output или image_references только после проверки конкретной модели. Не добавляйте count_tokens: у одного Chat Completions endpoint нет точной операции подсчёта токенов.

Указывайте reasoning_controls_and_summaries, только если модель принимает стандартный верхнеуровневый reasoning_effort и возвращает текстовый reasoning_content. Предпочтителен нативный повтор этого поля в assistant- сообщении с tool call. Если chat template upstream отклоняет такую историю с подтверждённой 5xx-ошибкой, описанной ниже, gpt2giga может продолжить через один встроенный текстовый повтор. Допустимые effort зависят от модели. Например, модель может принимать xhigh, но отклонять max; используйте максимальное значение, проверенное на этом конкретном endpoint.

Можно указать и базу https://inference.example/v1: тогда gpt2giga добавит chat/completions. Полный адрес, заканчивающийся на /chat/completions, используется без изменения.

Буферизованные ответы upstream​

Некоторые OpenAI-совместимые серверы принимают stream: true, но всё равно возвращают один полный Chat Completion с типом application/json. Для такого сервера явно включите отложенный синтез потока в профиле v3:

upstream_stream_mode: buffered

Шлюз отправит upstream-параметр stream: false, проверит полный ответ и выдаст клиенту одну текстовую дельту или по одной полной дельте на function call, после чего передаст завершающее событие и usage. Этого достаточно для обычных циклов с инструментами в Codex, Claude Code и Gemini CLI.

Буферизованный режим не даёт поток токенов от модели. Первое событие с текстом или function call приходит только после завершения полного ответа, поэтому в этом режиме нельзя измерять TTFT модели и инкрементальную отмену. По умолчанию используется строгий sse: gpt2giga не включает буферизацию автоматически, если upstream нарушил заявленный streaming-контракт. Специфичные для провайдера поля не входят в мост без явно заявленной capability. При reasoning_controls_and_summaries поле reasoning_content выдаётся перед ответом или function call как Responses reasoning summary и в настоящем SSE, и в буферизованном режиме.

Локальный upstream​

Для сервера на той же машине используйте явное исключение для loopback:

base_url: http://127.0.0.1:8001/v1/chat/completions
allow_loopback: true
network_policy_ref: loopback-development
upstream_stream_mode: buffered # только если у сервера нет настоящего SSE

Прямые адреса из частных, link-local и metadata-сетей отклоняются. Если сервер доступен только через VPN или частную подсеть, поднимите SSH-туннель или его аналог на loopback и укажите локальный адрес в профиле.

Docker с mitmproxy в reverse mode​

Обычный стек deploy/observability.yaml настраивает forward proxy через HTTP_PROXY и HTTPS_PROXY. OpenAI-compatible adapter намеренно не читает эти переменные окружения. Чтобы запустить мост в Docker и видеть каждый upstream- запрос, используйте отдельный reverse-mode стек:

cp .env.example .env
# Перед запуском задайте GPT2GIGA_API_KEY в .env.

docker compose --env-file .env \
-f deploy/chat-completions-observability.yaml up -d --build

Стек монтирует deploy/providers.chat-completions.example.yaml, где gateway отправляет запросы на http://127.0.0.1:8080/v1/chat/completions. gpt2giga и mitmproxy делят один network namespace, поэтому этот loopback ведёт в mitmproxy. По умолчанию mitmproxy пересылает запрос на Docker host по адресу http://host.docker.internal:29999. При необходимости переопределите оба значения в .env:

MITMPROXY_UPSTREAM_URL=http://host.docker.internal:29999
GPT2GIGA_PROVIDER_CONFIG_FILE=/absolute/path/to/providers.yaml

Пример не заявляет parallel_tool_calls. Добавляйте эту capability только после того, как upstream вернёт два независимых function call в одном ходе assistant и примет результаты обоих инструментов на следующем ходе.

Туннель на host-машине должен уже принимать запросы на порту 29999. Docker не может починить или заменить этот туннель. После запуска:

curl -fsS http://127.0.0.1:8090/ready
curl -fsS \
-H "Authorization: Bearer $GPT2GIGA_API_KEY" \
http://127.0.0.1:8090/v1/models

Интерфейс mitmproxy доступен на http://127.0.0.1:8081. Для остановки используйте down с тем же Compose-файлом.

2. Запустите gpt2giga​

Из рабочей копии репозитория:

export CHAT_UPSTREAM_API_KEY='<upstream-bearer-token>'
export GPT2GIGA_CONFIG=/absolute/path/to/providers.yaml
export GPT2GIGA_ENABLE_API_KEY_AUTH=True
export GPT2GIGA_API_KEY='<local-gateway-key>'

uv run gpt2giga

Для upstream без ключа не задавайте CHAT_UPSTREAM_API_KEY. Если пакет уже установлен, запускайте gpt2giga вместо uv run gpt2giga.

Проверьте готовность и статический алиас модели — эти два запроса не обращаются к inference-серверу:

curl -fsS http://127.0.0.1:8090/ready
curl -fsS \
-H "Authorization: Bearer $GPT2GIGA_API_KEY" \
http://127.0.0.1:8090/v1/models

Затем проверьте реальный перевод:

curl -fsS http://127.0.0.1:8090/v1/responses \
-H "Authorization: Bearer $GPT2GIGA_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"bridge/chat-only","input":"Reply only OK"}'

3. Подключите Codex​

Добавьте провайдера в ~/.codex/config.toml:

[model_providers.gpt2giga_chat]
name = "gpt2giga Chat bridge"
base_url = "http://127.0.0.1:8090/v1"
env_key = "GPT2GIGA_API_KEY"
wire_api = "responses"
requires_openai_auth = false
supports_websockets = false

Создайте отдельный файл профиля ~/.codex/chat-bridge.config.toml:

model = "bridge/chat-only"
model_provider = "gpt2giga_chat"
model_context_window = 32768
model_auto_compact_token_limit = 24576
model_reasoning_summary = "auto"
model_reasoning_effort = "xhigh"
web_search = "disabled"

Экспортируйте локальный ключ и выберите профиль:

export GPT2GIGA_API_KEY='<local-gateway-key>'
codex --profile chat-bridge

Минимальная команда Codex для бенчмарка​

Для герметичного CLI-бенчмарка отключите пользовательские MCP, приложения, плагины и multi-agent, а настройки моста передайте явно:

codex -a never exec --json \
--ignore-user-config \
--strict-config \
--skip-git-repo-check \
--ephemeral \
--sandbox workspace-write \
--disable apps \
--disable plugins \
--disable multi_agent \
--disable apply_patch_freeform \
-m bridge/chat-only \
-c model_provider=gpt2giga_chat \
-c model_providers.gpt2giga_chat.name=gpt2giga_chat \
-c model_providers.gpt2giga_chat.base_url=http://127.0.0.1:8090/v1 \
-c model_providers.gpt2giga_chat.env_key=GPT2GIGA_API_KEY \
-c model_providers.gpt2giga_chat.wire_api=responses \
-c model_providers.gpt2giga_chat.supports_websockets=false \
-c model_context_window=32768 \
-c model_auto_compact_token_limit=24576 \
-c model_reasoning_summary=auto \
-c model_reasoning_effort=xhigh \
-c web_search=disabled \
'Выполни задачу бенчмарка.'

Эту же строку можно передать в runner, например в harness_bench run-cli. Codex CLI 0.146.0 проверен с таким минимальным набором: exec_command, write_stdin, update_plan, request_user_input и view_image. Все они передаются как обычные function tools. В живой двухходовой проверке Codex получил потоковый exec_command, выполнил его, вернул результат и получил финальный ответ модели через upstream Chat Completions.

Codex CLI 0.146.0 отклоняет model_supports_reasoning_summaries при включённом --strict-config, поэтому в закреплённой команде выше этого ключа нет. Эта версия принимает model_reasoning_summary и model_reasoning_effort; для проверенной модели effort достаточно, чтобы получить reasoning_content. После обновления Codex перепроверяйте strict-config ключи: опубликованная справка может описывать более новую версию CLI.

Responses custom/freeform tools намеренно не входят в поддержанное подмножество. В частности, не включайте apply_patch_freeform. Для неизвестного публичного алиаса из примера текущий Codex редактирует файлы через exec_command и не выставляет apply_patch. Если бенчмарк проверяет именно wire-контракт custom apply_patch, нужен нативный Responses upstream или отдельно рассмотренное расширение для custom tools.

Codex работает по Responses API. Шлюз принимает его stateless-envelope, переводит сообщения developer в system для Chat Completions, разворачивает namespace tools перед вызовом upstream и восстанавливает namespace в вернувшихся function calls. Он принимает служебные item id, которые Codex повторяет в истории, и сохраняет идентификатор вызова до следующего function_call_output.

Когда reasoning включён, шлюз передаёт effort из Responses как Chat Completions reasoning_effort. Вернувшийся reasoning_content становится элементом reasoning с одной частью summary_text. Если Codex повторит этот элемент перед function call, gpt2giga восстановит текст как assistant reasoning_content в следующем запросе Chat Completions. Обычная stateless- подсказка include: ["reasoning.encrypted_content"] принимается, но шлюз не утверждает, что создаёт зашифрованное reasoning-состояние OpenAI: для повторной передачи используется текстовый summary.

Это явно обозначенная совместимая проекция текста рассуждения провайдера, а не summary, созданный OpenAI. Upstream из проверенного примера не возвращает пригодное число reasoning-токенов, поэтому gpt2giga оставляет эту детализацию недоступной и не пытается вычислить её из текста.

Сломанный chat template для истории инструментов​

Некоторые inference-серверы принимают первый function call, но падают при отрисовке assistant tool calls и tool results в следующем запросе, например с Failed to apply chat template: Object of type Undefined is not JSON serializable. gpt2giga обрабатывает это внутри OpenAI-compatible адаптера; скрипт mitmproxy и отдельная compatibility-переменная окружения не нужны.

Сначала всегда отправляется исходный запрос в стандартной форме. Только если тот же профиль и модель возвращают 5xx с Failed to apply chat template в detail, а в запросе есть история инструментов, gpt2giga делает один повтор. В нём сохраняются текущие определения инструментов, tool choice, reasoning effort и параметры генерации, а предыдущие assistant reasoning, function calls и tool results переводятся в явный текст. Результаты инструментов помечаются как недоверенные данные. После этого модель может вызвать следующий обычный инструмент или завершить задачу.

Остальные ошибки не повторяются. Не повторяется и SSE-ошибка после первого content chunk от upstream. gpt2giga не выполняет, не переименовывает и не придумывает инструменты, а вторую ошибку возвращает клиенту. Успешное восстановление отмечается как metadata.gpt2giga_chat_template_fallback="tool_history_text_replay"; маркер также есть в начальном и завершённом объектах потока Responses.

Codex запрашивает /v1/models?client_version=... в собственном формате каталога, который включает принадлежащие Codex базовые инструкции и метаданные инструментов. На такой запрос gpt2giga возвращает корректный пустой каталог Codex, не обращаясь к серверу инференса. Для явно выбранной пользовательской модели Codex сохраняет собственные fallback-инструкции и метаданные инструментов, а явные значения context и compaction выше заменяют fallback- лимиты токенов. Обычные OpenAI-клиенты по-прежнему получают статические алиасы в стандартном списке data. При работе через этот мост всегда передавайте -m или задавайте model: пустой каталог Codex не может выбрать клиенту модель по умолчанию.

Поля конфигурации и приоритет файлов профилей описаны в официальной справке Codex.

4. Подключите Claude Code​

Оставьте Claude Code в stateless-подмножестве Messages и синхронизируйте лимиты с профилем провайдера:

export ANTHROPIC_BASE_URL=http://127.0.0.1:8090
export ANTHROPIC_API_KEY="$GPT2GIGA_API_KEY"
export ANTHROPIC_MODEL=bridge/chat-only
export ANTHROPIC_SMALL_FAST_MODEL=bridge/chat-only

export CLAUDE_CODE_MAX_CONTEXT_TOKENS=32768
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=4096
export MAX_THINKING_TOKENS=0
export CLAUDE_CODE_DISABLE_THINKING=1
export DISABLE_PROMPT_CACHING=1
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

claude --model bridge/chat-only

Через ANTHROPIC_API_KEY Claude Code отправляет локальный ключ шлюза в x-api-key. gpt2giga также принимает bearer-аутентификацию.

Thinking и prompt caching отключены, потому что Chat Completions не может сохранить эту семантику Anthropic. Значение CLAUDE_CODE_MAX_OUTPUT_TOKENS не должно превышать capabilities.limits.max_output_tokens: для неизвестной Claude-модели клиент по умолчанию запрашивает существенно больший ответ. Обычные function tools и потоковые Messages events через этот маршрут работают.

Некоторые сценарии Claude Code вызывают /v1/messages/count_tokens. Мост отклоняет такую операцию вместо неточной оценки. Основной чат и вызовы tools не требуют отдельного count-token endpoint, но зависимая от него функция останется недоступна.

Актуальный список клиентских переменных находится в официальной справке Claude Code.

5. Подключите Gemini CLI​

Для неизвестной модели Gemini CLI применяет встроенную конфигурацию chat-base, которая сейчас добавляет topK и thinkingConfig. У этих параметров нет точного представления в Chat Completions, поэтому мост их отклоняет. Нужен минимальный пользовательский алиас с ключом, точно совпадающим с публичным id модели.

Добавьте этот блок в ~/.gemini/settings.json, не удаляя остальные настройки:

{
"security": {
"auth": {
"selectedType": "gemini-api-key"
}
},
"modelConfigs": {
"customAliases": {
"bridge/chat-only": {
"modelConfig": {
"model": "bridge/chat-only",
"generateContentConfig": {
"temperature": 1,
"topP": 0.95,
"maxOutputTokens": 4096
}
}
}
}
}
}

Не наследуйтесь от chat-base и не добавляйте topK либо thinkingConfig для этого маршрута. Ключ алиаса должен оставаться bridge/chat-only: после вызова инструмента Gemini CLI продолжает работу с разрешённым id модели, поэтому алиас с другим именем повлияет только на первый запрос.

Запускайте Gemini CLI с публичным id модели:

export GOOGLE_GEMINI_BASE_URL=http://127.0.0.1:8090
export GEMINI_API_KEY="$GPT2GIGA_API_KEY"
export GEMINI_MODEL=bridge/chat-only
export OTEL_SDK_DISABLED=true

gemini --model bridge/chat-only

Gemini передаёт GEMINI_API_KEY в Google-совместимой форме, которую шлюз принимает как свой локальный API key. Профиль провайдера затем связывает публичный id с точной моделью upstream. Синтетический маркер Gemini CLI skip_thought_signature_validator принимается только как клиентский no-op; настоящая thought signature остаётся неподдержанной.

Если задан GEMINI_CLI_HOME, Gemini CLI считает его корнем домашней папки. Глобальные настройки должны находиться в $GEMINI_CLI_HOME/.gemini/settings.json, а не прямо в $GEMINI_CLI_HOME.

Если алиас хранится в проектном .gemini/settings.json, сначала доверьте рабочую папку клиенту. Для headless-запуска задайте GEMINI_CLI_TRUST_WORKSPACE=true до старта Gemini CLI, чтобы проектные настройки были загружены.

Формат описан в официальной справке Gemini CLI по model configuration.

Поддержанное подмножество и ограничения​

Мост поддерживает текстовые роли, общие параметры генерации, function tools и их результаты, tool choice там, где целевой протокол умеет его выразить, потоковые ответы, terminal stop reasons, usage при наличии в upstream, отмену, заявленные лимиты контекста и включаемые через профиль Responses reasoning controls с повторной передачей summary.

Поддержан полный stateless-цикл инструмента: клиент передаёт определения функций, получает потоковый или обычный вызов, выполняет его локально и в следующем запросе повторяет вызов вместе с результатом. Мост сохраняет call id и переводит следующий ход обратно в Chat Completions. Сценарий проверен для Responses, Anthropic Messages и Gemini GenerateContent.

Если OpenAI-compatible upstream возвращает для этой истории подтверждённую 5xx-ошибку chat template, адаптер выполняет описанный выше однократный текстовый повтор. Восстановление общее для фасадов Responses, Messages и GenerateContent, потому что выполняется после их перевода в один нормализованный путь Chat Completions.

Мост не эмулирует:

  • состояние Responses: previous_response_id, conversations, background jobs и долговременный store;
  • нативное зашифрованное reasoning-состояние, независимые от провайдера thinking controls и точный учёт reasoning-токенов;
  • семантику prompt cache и точный cached-token accounting;
  • hosted web search, computer use, code execution и provider-native tools;
  • Responses custom/freeform tools, например apply_patch;
  • files, audio и неподдерживаемые мультимодальные данные;
  • точный подсчёт токенов;
  • Gemini safety settings, cached content, topK и thinking configuration.

Проверенные служебные no-op поля клиентов принимаются, если они не меняют переведённый запрос. Семантическое поле вне заявленного подмножества возвращает unsupported_semantic до обращения к upstream.

Диагностика​

СимптомЧто делать
unknown_model_aliasИспользуйте точный public_alias с тем же регистром и знаками.
credential_unavailable при запускеЗадайте переменную из credential_env или удалите поле для намеренно открытого upstream.
unsupported_semanticКлиент запросил смысл, который нельзя сохранить в Chat Completions. Отключите эту функцию; не добавляйте capability без реальной проверки upstream.
Отказ по токенным лимитамСинхронизируйте context/output клиента с точными значениями профиля.
В запросе Gemini есть topK или thinkingConfigЗадайте минимальный пользовательский алиас под точным публичным id bridge/chat-only: алиас с другим именем действует только на первый запрос tool-loop.
Claude вызывает count_tokensДля upstream только с Chat Completions эта операция намеренно не поддерживается.
Codex предупреждает о метаданных моделиОставьте явные context/compaction значения в профиле Codex; обычные запросы продолжат работу.
Upstream отклоняет reasoning_effort=maxНабор effort зависит от модели. Используйте максимальное проверенное значение, например xhigh, и привязывайте capability profile к этой модели.
В metadata ответа есть gpt2giga_chat_template_fallback=tool_history_text_replayUpstream отклонил нативную историю инструментов, а однократный встроенный текстовый повтор сработал. Слой совместимости mitmproxy не участвовал.
Клиент получил chat_template_application_failedВ запросе не было истории для повтора или единственная fallback-попытка тоже завершилась ошибкой. Исправьте или замените chat template upstream; повторной попытки не будет.
provider_protocol_error в потокеПроверьте корректный Chat Completions SSE, terminal choice, порядок optional usage и завершающий [DONE].

Полная схема находится в разделе «Профили провайдеров и алиасы моделей», а правила семантического допуска — в разделе «Совместимость провайдеров».