OpenAI 互換 API:/v1/chat/completions と /v1/responses の実装戦略
この OpenAI 互換 API を使用して、既存の OpenAI SDK/クライアントを Antigravity Tools ローカルゲートウェイに直接接続します。重点的に /v1/chat/completions と /v1/responses を動作させ、レスポンスヘッダを使用して素早くトラブルシューティングする方法を学びます。
学習後、できること
- OpenAI SDK(または curl)を使用して Antigravity Tools ローカルゲートウェイに直接接続する
/v1/chat/completions(stream: trueを含む)と/v1/responsesを動作させる/v1/modelsのモデルリストと、レスポンスヘッダのX-Mapped-Modelを理解する- 401/404/429 が発生した場合、どこを先に確認すべきかを知る
現在の課題
多くのクライアント/SDK は OpenAI のインターフェース形状しか認識しません:固定された URL、固定された JSON フィールド、固定された SSE ストリーミング形式。 Antigravity Tools の目標は、クライアントを変更することではなく、クライアントに「自分は OpenAI を呼び出している」と思わせ、実際にはリクエストを内部上流呼び出しに変換し、結果を OpenAI 形式に戻すことです。
いつこの手法を使うか
- OpenAI のみをサポートするツール(IDE プラグイン、スクリプト、Bot、SDK)を多数持っており、それぞれに新しい統合を書きたくない場合
base_urlを使用してリクエストをローカル(または LAN)ゲートウェイに送信し、ゲートウェイでアカウントスケジューリング、再試行、監視を行いたい場合
🎒 始める前の準備
前提条件
- Antigravity Tools の "API Proxy" ページでリバースプロキシサービスを起動済み、ポートを記録(例:
8045) - 少なくとも1つの利用可能なアカウントを追加済み、そうしないとリバースプロキシが上流 token を取得できない
認証はどう持たせますか?
proxy.auth_mode を有効にし proxy.api_key を設定した場合、リクエストに API Key を含める必要があります。
Antigravity Tools のミドルウェアは Authorization を優先的に読み取り、x-api-key、x-goog-api-key も互換します。(実装は src-tauri/src/proxy/middleware/auth.rs を参照)
OpenAI 互換 API とは?
OpenAI 互換 API は、「OpenAI のように見える」HTTP ルートと JSON/SSE プロトコルのセットです。クライアントは OpenAI のリクエスト形式でローカルゲートウェイに送信し、ゲートウェイはリクエストを内部上流呼び出しに変換し、上流レスポンスを OpenAI レスポンス構造に変換して返します。これにより、既存の OpenAI SDK は基本的に変更なしで使用できます。
互換エンドポイント概要(この授業関連)
| エンドポイント | 用途 | コード証拠 |
|---|---|---|
POST /v1/chat/completions | Chat Completions(ストリーミングを含む) | src-tauri/src/proxy/server.rs ルート登録;src-tauri/src/proxy/handlers/openai.rs |
POST /v1/completions | Legacy Completions(同じハンドラーを再利用) | src-tauri/src/proxy/server.rs ルート登録 |
POST /v1/responses | Responses/Codex CLI 互換(同じハンドラーを再利用) | src-tauri/src/proxy/server.rs ルート登録(コメント:Codex CLI 互換) |
GET /v1/models | モデルリストを返す(カスタムマッピング + 動的生成を含む) | src-tauri/src/proxy/handlers/openai.rs + src-tauri/src/proxy/common/model_mapping.rs |
実践してみましょう
ステップ1:curl でサービスが動作していることを確認する(/healthz + /v1/models)
なぜ 「サービスが起動していない/ポートが間違っている/ファイアウォールにブロックされている」などの低レベルな問題を先に排除します。
# 1) ヘルスチェック
curl -s http://127.0.0.1:8045/healthz
# 2) モデルリストを取得
curl -s http://127.0.0.1:8045/v1/models次のように見えるはずです:/healthz は {"status":"ok"} のような JSON を返す;/v1/models は {"object":"list","data":[...]} を返す。
ステップ2:OpenAI Python SDK を使用して /v1/chat/completions を呼び出す
なぜ このステップは「OpenAI SDK → ローカルゲートウェイ → 上流 → OpenAI レスポンス変換」のチェーン全体が通っていることを証明します。
import openai
client = openai.OpenAI(
api_key="sk-antigravity",
base_url="http://127.0.0.1:8045/v1",
)
response = client.chat.completions.create(
model="gemini-3-flash",
messages=[{"role": "user", "content": "こんにちは、自己紹介してください"}],
)
print(response.choices[0].message.content)次のように見えるはずです:ターミナルにモデル返信テキストが出力されます。
ステップ3:stream を有効にし、SSE ストリーミング返却を確認する
なぜ 多くのクライアントは OpenAI の SSE プロトコル(Content-Type: text/event-stream)に依存します。このステップはストリーミングチェーンとイベント形式が使用可能であることを確認します。
curl -N http://127.0.0.1:8045/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"stream": true,
"messages": [
{"role": "user", "content": "ローカルリバースプロキシゲートウェイを3文で説明してください"}
]
}'次のように見えるはずです:ターミナルが継続的に data: { ... } で始まる行を出力し、data: [DONE] で終わります。
ステップ4:/v1/responses を使用して(Codex/Responses スタイル)1つのリクエストを動作させる
なぜ 一部のツールは /v1/responses を使用するか、リクエストボディで instructions、input などのフィールドを使用します。このプロジェクトはこの種のリクエストを messages に「正規化」し、同じ変換ロジックを再利用します。(ハンドラーは src-tauri/src/proxy/handlers/openai.rs を参照)
curl -s http://127.0.0.1:8045/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-flash",
"instructions": "あなたは厳格なコードレビュアーです。",
"input": "次のコードの最も可能性の高いバグを指摘してください:\n\nfunction add(a, b) { return a - b }"
}'次のように見えるはずです:レスポンスボディは OpenAI スタイルのレスポンスオブジェクトです(このプロジェクトは Gemini レスポンスを OpenAI choices[].message.content に変換します)。
ステップ5:モデルルーティングが有効になっていることを確認する(X-Mapped-Model レスポンスヘッダを確認)
なぜ クライアントで記述した model は必ずしも実際に呼び出される「物理モデル」ではありません。ゲートウェイはまずモデルマッピングを行い(カスタムマッピング/ワイルドカードを含む、モデルルーティング:カスタムマッピング、ワイルドカード優先度とプリセット戦略 を参照)、最終結果をレスポンスヘッダに配置し、トラブルシューティングを容易にします。
curl -i http://127.0.0.1:8045/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "こんにちは"}]
}'次のように見えるはずです:レスポンスヘッダに X-Mapped-Model: ... が含まれます(例:gemini-2.5-flash にマッピング)、また X-Account-Email: ... が含まれる可能性があります。
チェックポイント ✅
GET /healthzが{"status":"ok"}を返す(または同等の JSON)GET /v1/modelsがobject=listを返し、dataは配列/v1/chat/completions非ストリーミングリクエストでchoices[0].message.contentを取得できるstream: trueの場合、SSE を受信し、[DONE]で終了curl -iでX-Mapped-Modelレスポンスヘッダを見ることができる
よくある落とし穴
1) Base URL の書き間違いによる 404(最も一般的)
- OpenAI SDK の例では、
base_urlは/v1で終わる必要があります(プロジェクト README の Python 例を参照)。 - 一部のクライアントは「パスを重ねる」可能性があります。例:README で明示的に言及されていますが、Kilo Code は OpenAI モードで
/v1/chat/completions/responsesこの種の非標準パスを生成し、404 をトリガーする可能性があります。
2) 401:上流がダウンしたのではなく、key を持っていないか、モードが間違っている
認証戦略の「有効モード」が off ではない場合、ミドルウェアはリクエストヘッダを検証します:Authorization: Bearer <proxy.api_key>、x-api-key、x-goog-api-key も互換します。(実装は src-tauri/src/proxy/middleware/auth.rs を参照)
認証モードヒント
auth_mode = auto の場合、allow_lan_access に基づいて自動的に決定されます:
allow_lan_access = true→ 有効モードはall_except_health(/healthzを除きすべて認証が必要)allow_lan_access = false→ 有効モードはoff(ローカルアクセスは認証不要)
3) 429/503/529:プロキシは再試行 + アカウントローテーションを行いますが、「プール枯渇」の可能性もあります
OpenAI ハンドラーは最大3回の試行を内蔵し(アカウントプールのサイズにも制限)、一部のエラーが発生すると待機/アカウントローテーションして再試行します。(実装は src-tauri/src/proxy/handlers/openai.rs を参照)
この授業のまとめ
/v1/chat/completionsは最も汎用的な接続ポイントで、stream: trueは SSE を使用/v1/responsesと/v1/completionsは同じ互換ハンドラーを使用し、コアはリクエストをmessagesに正規化することX-Mapped-Modelは「クライアントモデル名 → 最終物理モデル」のマッピング結果を確認するのに役立つ
次の授業の予告
次の授業では、Anthropic 互換 API:/v1/messages と Claude Code の重要な契約(対応章:
platforms-anthropic)を続けて見ていきます。
付録:ソースコード参照
クリックしてソースコードの場所を展開
更新日時:2026-01-23
| 機能 | ファイルパス | 行番号 |
|---|---|---|
| OpenAI ルート登録(/v1/responses を含む) | src-tauri/src/proxy/server.rs | 120-194 |
| Chat Completions ハンドラー(Responses 形式検出を含む) | src-tauri/src/proxy/handlers/openai.rs | 70-462 |
| /v1/completions と /v1/responses ハンドラー(Codex/Responses 正規化 + 再試行/ローテーション) | src-tauri/src/proxy/handlers/openai.rs | 464-1080 |
| /v1/models の返り(動的モデルリスト) | src-tauri/src/proxy/handlers/openai.rs | 1082-1102 |
| OpenAI リクエストデータ構造(messages/instructions/input/size/quality) | src-tauri/src/proxy/mappers/openai/models.rs | 7-38 |
| --- | --- | --- |
| --- | --- | --- |
| モデルマッピングとワイルドカード優先度(厳密 > ワイルドカード > デフォルト) | src-tauri/src/proxy/common/model_mapping.rs | 180-228 |
| --- | --- | --- |
重要な定数:
MAX_RETRY_ATTEMPTS = 3:OpenAI プロトコル最大試行回数(ローテーションを含む)(src-tauri/src/proxy/handlers/openai.rsを参照)
重要な関数:
transform_openai_request(...):OpenAI リクエストボディを内部上流リクエストに変換(src-tauri/src/proxy/mappers/openai/request.rsを参照)transform_openai_response(...):上流レスポンスを OpenAIchoices/usageに変換(src-tauri/src/proxy/mappers/openai/response.rsを参照)