コンテンツにスキップ

プロキシ API 形式

opencodex は、複数のクライアント方言で 1 つのローカル プロキシを表示します。 Codex クライアントは Responses API を話すことができ、OpenAI 互換アプリは Chat Completions を話すことができ、Claude Code は Anthropic Messages を話すことができます。すべての上流プロバイダーがあらゆる形式を実装する必要はありません。

通常の変換パスは次のとおりです。

client dialect → internal Responses model → provider adapter → provider wire format
provider events → internal adapter events → client dialect

応答表現はブリッジの中心です。ネイティブ互換ルートは、変換の一部をスキップしてリクエストを通過させる可能性がありますが、認証、ルーティング、アドミッション コントロール、および応答の安全性は依然としてプロキシ境界で発生します。 構成 でリスナーとアドミッション キーを構成します。 1 つのパブリック モデル ID を複数のターゲットから選択する必要がある場合は、コンボ を使用します。

認証情報を含むモデル・画像・動画・検索リクエストは、同一オリジンを含む HTTP リダイレクトを自動追跡しません。リダイレクトする別名ではなく、最終的な上流 API URL を設定してください。サーバーはリダイレクト先に認証情報やリクエスト本文を再送しません。各応答処理の既存のエラー処理・中継動作は維持され、native Responses と compact は元の 3xx と Location をクライアントへ返す場合があります。クライアントのリダイレクト動作は、このサーバー転送ポリシーとは別です。

クライアントサーフェス エンドポイント 非ストリームの結果が成功 成功したストリームまたはソケットの結果
OpenAI の応答 POST /v1/responses 応答 JSON 応答 SSE、または WebSocket 上の応答 JSON テキスト フレーム
OpenAI チャットの完了 POST /v1/chat/completions JSON chat.completion chat.completion.chunk SSE で終わる [DONE]
人間的なメッセージ POST /v1/messages 人類 message JSON 人間的メッセージ SSE
人間トークン数 POST /v1/messages/count_tokens { "input_tokens": number } 該当なし
モデルの発見 GET /v1/models カタログまたは明示的な Desktop スナップショット 該当なし
音声とリアルタイム POST /v1/livePOST /v1/realtime/calls 中継されたコール作成応答 別のサイドバンド WebSocket がフレームを両方向に中継します。
応答の圧縮 POST /v1/responses/compact 置換履歴 JSON 該当なし

これは、ネイティブの opencodex データプレーン形状です。リクエスト本文は、空ではない model を持つ JSON オブジェクトである必要があります。 input は文字列または応答項目の配列です。

受け入れられたリクエストフィールド

Section titled “受け入れられたリクエストフィールド”
エリア 許容される形状
モデルと入力 空ではない必須の model。オプションの文字列 input または項目配列
メッセージ項目 userdevelopersystem、および assistant メッセージ。役割に適した文字列コンテンツまたは型付きコンテンツ ブロック
コンテンツブロック テキスト、入力画像、入力ファイル、出力テキスト、拒否、および親項目で許可されている推論の概要/テキスト ブロック
ツールの歴史 function_callfunction_call_outputcustom_tool_call、および custom_tool_call_output アイテム
ツール 関数ツールに加えて、緩い組み込みまたはホストされたツール エントリ。 tool_choice は、autononerequired、名前付き関数/カスタム選択肢、ホストされた選択肢、または allowed_tools を受け入れます。
推論 reasoning.effort および reasoning.summary (autoconcisedetailed、または none)
継続とキャッシュ previous_response_idstore、および prompt_cache_key
生成制御 max_output_tokenstemperaturetop_pstoppresence_penalty、および frequency_penalty
サービスと実行 streamservice_tierparallel_tool_callsinstructionsmetadata、および user
拡張応答フィールド backgroundincludeprompttext、および truncation は互換性のあるルートとして受け入れられます。

未知の項目タイプは、前方互換性のためにルーズタイプの項目として受け入れられます。変換されたアダプターは、認識する項目タイプのみを処理し、プロバイダーが表現できない機能を拒否する場合があります。

stream: true の場合、応答は text/event-stream となります。ブリッジは、response.created、出力項目およびテキスト/ツール デルタ、および 1 つの端末 response.completedresponse.failed、または response.incomplete イベントなどの応答イベントを発行します。通常のストリームは data: [DONE] で終了します。

stream: false を指定するか、stream を指定しないと、同じアダプター イベントが 1 つの Responses JSON オブジェクトに収集されます。どちらの形式でも、選択したモデル、出力項目、端末の状態、使用状況が保存されます。

クライアント向け Responses SSE フレームは、SSE ブロック区切りの前の生バイトで測って 1 フレームあたり 4 MiB に制限されます。HTTP では、区切りなしでこの上限を超えたアップストリーム フレームは、合成 response.failed イベントと続く data: [DONE] でフェイルクローズします。Responses WebSocket ブリッジでは、同じ条件で 502 websocket_protocol_error を送信し、アップストリーム リーダーをキャンセルします。完全な Responses 終端フレームがすでに到着している場合はそれが優先され、その後のサイズ超過または不正なバイトは、完了したターンをトランスポート障害に置き換えず破棄されます。

すべての端末応答使用状況オブジェクトには、プロバイダーが詳細を報告しなかった場合でも、両方の詳細オブジェクトが含まれます。

{
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
"input_tokens_details": { "cached_tokens": 0 },
"output_tokens_details": { "reasoning_tokens": 0 }
}

利用可能な場合、input_tokens_details には cache_write_tokens も含めることができます。常に存在する詳細オブジェクトは、厳密な応答クライアントに対する互換性を保証します。ゼロは「報告されていない」ことを意味する場合がありますが、必ずしも「プロバイダーがそのような作業を実行していない」とは限りません。

応答とリクエストログの関連付け

Section titled “応答とリクエストログの関連付け”

アドミッションを通過したすべての HTTP Responses 応答には、プロキシが生成した ocx-<32 hex> 形式の ID を格納する x-opencodex-request-id ヘッダーが付与されます。この値は、応答をリクエストログおよび使用状況レポート内の対応する行に結び付けるキーです。

プロキシは常にこの値を生成し、呼び出し元が指定した ID やアップストリームが返した ID を上書きします。そのため、このプロキシに固有であり、相関キーとして安全に信頼できます。このヘッダーは Access-Control-Expose-Headers に列挙されているため、ブラウザーの JavaScript からクロスオリジンで読み取れます。カスタムの x- ヘッダーは、実際にワイヤ上に存在していても、そうしなければ response.headers.get() からは見えません。

認証またはオリジンのアドミッションで拒否されたリクエストはこのラッパーに到達せず、ID も付与されません。そのため、ヘッダーがない場合は、リクエストがログに記録される前に拒否されたことを意味します。

同じパスでの WebSocket のアップグレード

Section titled “同じパスでの WebSocket のアップグレード”

websockets が有効な場合、クライアントは HTTP POST を開く代わりに /v1/responses をアップグレードできます。認証とオリジンの許可は、WebSocket ハンドシェイク中に行われます。それらは各フレーム内で繰り返されません。

クライアントは JSON テキスト フレームを送信します。

{
"type": "response.create",
"model": "provider/model",
"input": "Hello",
"tools": [],
"generate": true
}

type を除くすべてが応答リクエストの本文となり、プロキシはそのターンのストリーミングを強制します。新しい response.create が優先され、そのソケットに対する以前のターンがキャンセルされます。 response.processed は no-op 確認応答として受け入れられます。解析できないフレームタイプまたは関連性のないフレームタイプは無視されます。

サーバーフレームはJSONテキストフレームです。ストリーミング出力が成功すると、SSE エンベロープや [DONE] を使用せずに、SSE data: 行に表示されるものと同じ JSON ペイロードが使用されます。非ストリーミングの内部結果は、response.created、0 個以上の response.output_item.done フレーム、そして終端フレームとして再フレーム化されます。エラーでは次のエンベロープが使用されます。

{
"type": "error",
"status": 502,
"error": {
"type": "upstream_error",
"message": "..."
},
"headers": {}
}

generate: false のウォームアップ フレームはアップストリームを呼び出しません。これは、合成 response.created に続いて response.completed を返します。両方とも空の応答 ID を持ち、出力はありません。

このエンドポイントは、必須の model および空ではない messages 配列を含む OpenAI 互換の Chat Completions リクエストを受け入れます。システム、ユーザー、アシスタント、ツールのメッセージを内部の応答アイテムに変換します。機能ツール、ツールの選択、画像、推論負荷、およびサポートされている応答形式を翻訳します。通常の応答ルーティング パイプラインを実行します。次に、結果を変換して戻します。

非ストリーミング出力には object: "chat.completion" があります。ストリーミング出力では、object: "chat.completion.chunk" の SSE オブジェクト、選択デルタ、finish_reason、および data: [DONE] の端末選択を使用します。ツール呼び出しと使用情報は、ソース イベントが伝達する場所に変換されて戻されます。

内部実行パスは応答ベースであるため、プロバイダー アダプターはより狭い機能セットを課すことができます。たとえば、選択したアダプタで表現できないリクエスト機能は、その意味を黙って変更するのではなく、エラーとして返されます。

これらのエンドポイントは、Claude Code および互換性のあるクライアントによって使用される Anthropic Messages 言語を話します。ほとんどのリクエストはレスポンスに変換され、通常どおりルーティングされてから、Anthropic JSON または Anthropic SSE に変換されます。

変換される Messages リクエストでは、推論の再送もリクエスト共通の変換バジェットを使います。 この制限にはエンコード・デコード時のコピー分も含まれます。超過時は translation_buffer_limit を伴う HTTP 413 を返し、署名や不透明な推論データを切り詰めません。 ネイティブ Anthropic パススルーには、別の本文サイズ制限が適用されます。

ネイティブ Anthropic パススルーは、次のすべてが当てはまる場合にのみ適格です。

  • ネイティブ パススルーはクロード コード設定で無効になっていません。
  • 要求されたモデルは claude または anthropic で始まります。
  • リクエストにはネイティブ Anthropic Bearer または x-api-key 資格情報が含まれます。
  • 非ループバック listener では、有効なプロキシ admission を x-opencodex-api-key だけで送ります。そして
  • 構成されたエイリアスまたはモデル マップが、ルーティングされたターゲットのモデル ID を主張しません。

適格なリクエストは Anthropic 方言で転送されるため、ネイティブ ベータ ヘッダー、思考署名、およびサブスクリプション ID がエンドツーエンドで残ります。それ以外の場合は、応答が往復してかかります。

専用 admission ヘッダーは転送されません。Authorization または x-api-key にあるプロキシ admission secret も削除され、別の実際の Anthropic 認証情報は維持されます。カンマで結合された 曖昧な認証ヘッダーは fail closed します。

POST /v1/messages/count_tokens は、同じモデルの解像度とパススルーの決定に従います。ネイティブ適格なリクエストは、Anthropic のカウント エンドポイントに転送されます。他のリクエストは、システム コンテンツ、メッセージ、ツールに対してローカルに文書化された見積もりを使用し、次を返します。

{ "input_tokens": 123 }

解決できない日付形式の Desktop ID は、モデル検出に含まれていない実際のネイティブモデル かもしれません。判断材料が足りず ID を解決できない場合、Messages と count-tokens は固定エラー desktop_model_mapping_unavailableと HTTP 503 を返します。これはモデルが無効だという判定ではありません。 不明な旧ハッシュ別名は引き続き HTTP 400 で拒否します。どちらも日付を除去したり別ルートへ フォールバックしたりしません。既知の ID、登録済みマッピング、正確な modelMap 一致、 認識済みの実ネイティブ ID の処理は変わりません。モデル検出を更新するか接続先ハブの プロファイルを再適用してから試してください。再試行だけで解決する保証はありません。

format=desktop-config を指定しない場合、通常のカタログ契約は次のとおりです。

| — | — | — | — | |人類モデルのリスト | anthropic-version ヘッダーまたは ?flavor=anthropicclient_version なし | Anthropic モデル情報エントリのある { "data": [...] } |クロード コードは読み取り可能な ID を受け取ります。デスクトップはプロファイル固有のエイリアス ファミリを受け取ることができます。 |Codexカタログ | client_version クエリパラメータ | { "models": [...] } |ネイティブおよびルーティングされたエントリには、より豊富な Codex カタログ フィールド、可視性、労力、WebSocket、およびマルチエージェント メタデータが含まれています。 |プレーンな OpenAI リスト |どちらのトリガーもありません | { "object": "list", "data": [...] } |表示されるネイティブ ID は裸です。ルーティング ID はエイリアスまたは provider/model |

GET /v1/models?ids=desktop&format=desktop-config は user-agent に関係なく Desktop スナップショットを明示的に選択します。応答は { "version": 1, "models": [...] } で、 Cache-Control: no-store を含みます。クライアントは Accept: application/jsonanthropic-version: 2023-06-01 と既存のデータ用認証情報を送ります。管理者トークンや プロファイルのアップロードは不要です。項目はハブが発行した Desktop 設定用モデルであり、 Codex カタログの行ではありません。

この形式に ids=cli または client_version を併用すると HTTP 400 になります。形式指定が なければ上記の通常の契約を維持します。Claude が無効なら { "version": 1, "models": [] } を返し、接続中の Desktop apply は利用不可として設定を書き換えません。バージョン 1 ではなく 通常のカタログを返す古いハブは未対応で、ローカル生成 ID に切り替えることはありません。

スナップショットは読み取り専用のモデル一覧であり、キーローテーションやプロファイル送信の API ではありません。Desktop のキー移行・復旧・切断は既存の接続ライフサイクルで処理します。 ローテーションはモデルと選択を保持し、CLI の rotationcommittedrolled_back を 区別します。切断は管理設定を復元するか、確認済み旧プロファイルを標準モードへ戻し、 ユーザーフィールドと後から選んだ有効なプロファイルを保持します。競合や未完了の復旧を完了とは 報告しません。ファイル変更の反映には Desktop の再起動が必要で、切断はハブのキーを自動失効 させません。Desktop ガイドを参照してください。thinking 再送と キャッシュは別件 #3719です。

POST /v1/live とRealtime サイドバンド

Section titled “POST /v1/live とRealtime サイドバンド”

以下のアカウント連携は既存の Codex クライアント向けです。外部 API キーで利用する音声入力と GPT-Live は英語版の音声 API 仕様を参照してください。

Connections > API keys に音声入力とリアルタイム音声の項目があります。データキーは入力欄のメモリにのみ保持されます。文字起こしは選択したファイルを送信し、音声の接続確認はマイクを使わずセッション応答を待ちます。設定済みの表示は接続成功を意味しません。

POST /v1/live は、ChatGPT/Codex アプリのフレームレス通話作成サーフェスを受け入れます。 POST /v1/realtime/calls は、OpenAI Realtime 呼び出し作成サーフェスを受け入れます。 opencodex は、適格な OpenAI ファミリ ルートを選択し、アップストリーム認証モードのコール作成リクエストを正規化し、制限付き応答を中継します。

コールの作成後、クライアントはサポートされている受信フォームを使用してサイドバンド WebSocket に参加できます。

  • /v1/live/{callId}
  • /v1/realtime/calls/{callId}
  • /v1/realtime?call_id={callId}

プロキシはアップストリームの参加 URL を正規化し、テキストとバイナリ フレームを両方向に透過的に中継します。クライアント プロトコル ヘッダーは保持されますが、アップストリーム認証はプロキシが所有したままになります。

圧縮により、長い応答会話を短縮する必要があるクライアントの置換履歴が返されます。

ルートの種類 行動
Canonical ChatGPT または公式 OpenAI ルート 解決されたアカウントとモデル認証を使用して、リクエストをネイティブ /responses/compact エンドポイントに転送します。
その他の配線モデル compaction_trigger を使用して内部、非ストリーミング、ツール不要の圧縮ターンを実行します。 encrypted_contentocx1: エンベロープである合成 compaction アイテムが 1 つだけ必要です。その概要を v1 置換履歴にデコードします。

ネイティブ コンパクト応答は、宣言された Content-Length がすでに制限を超えている応答を含め、最大 32 MiB でバッファリングされます。コンパクト固有の障害には次のようなものがあります。

ステータス タイプまたはコード 意味
400 invalid_request_error 無効な JSON/ボディ形状または欠落しているモデル
404 invalid_request_error 要求されたモデルはルーティングできません。
499 client_cancelled
502 compact_response_too_large ネイティブ コンパクト出力が 32 MiB を超えました
502 upstream_error 接続、読み取り、または合成圧縮ターンの失敗
502 invalid_response_error 合成ターンでは、有効な空でない ocx1: 圧縮項目が 1 つだけ生成されませんでした。

ループバックのみのバインドでは、データ プレーンのアドミッションに設定されたキーは必要ありません。リモート バインドでは、以下のマトリックスを使用します。 「専用」とは X-OpenCodex-API-Key を意味します。他の列は Authorization: Bearer ...x-api-key を意味します。

表面 専用 ベアラー x-api-key
/v1/responses HTTP と WebSocket 承認済み 承認済み 拒否されました
/v1/responses/compact 承認済み 承認済み 拒否されました
/v1/chat/completions 承認済み 承認済み 拒否されました
/v1/messages および /v1/messages/count_tokens 承認済み 承認済み 承認済み
/v1/models 承認済み 承認済み 承認済み
/v1/live/v1/realtime/calls、および側波帯結合 承認済み 承認済み 承認済み

Responses 系列と Chat のリクエストは、専用ヘッダーまたは Bearer フィールドのプロキシキーを受け付けます。ネイティブルートでは選択された保存済み Codex 認証情報が admission bearer を置き換え、他のルートではその bearer を削除します。プロキシキーを upstream の認証情報として使うことはありません。別の provider bearer も渡す場合は、プロキシキーを専用ヘッダーに設定してください。

キーがなく OAuth を使用しない Cursor ルートは、別途指定された呼び出し元 bearer を使用できますが、プロキシ secret や自動補完された ChatGPT main 認証は使用しません。Combo/policy の選択と実際の shadow/thread-spawn ルート変更では、呼び出し元の生の認証情報を新しい対象へ渡しません。正規の OpenAI ルーティングでは、JWT に ChatGPT アカウントの claim が含まれ、明示的なアカウントヘッダーがある場合はその claim と一致するときに限り、内部ルート変更後にプロキシキーではない呼び出し元の単一 bearer を復元できます。 オプションの OpenAI sidecar に呼び出し元の認証を転送するには、単一の JWT とそれに一致する明示的な chatgpt-account-id が必要です。Opaque bearer は、明示的なアカウントヘッダーがあっても、ルート変更をまたいで復元されません。 それ以外の最終対象には自身の設定済み・OAuth・保存済み認証情報が必要で、なければローカルで失敗します。ルート変更のない thread-spawn マーカーだけでは認証情報を削除しません。

設定済みキーのない Cursor への Chat リクエストでは、保存済み main 認証による任意の補完を、OpenAI 補助呼び出しが実際に計画され、canonical Direct の候補が利用可能になるまで延期します。無関係な Cursor リクエストはこの経路で native main を占有せず、プロファイル切り替えを遅らせません。補助認証は起動時と切り替え時の保護に従い、Cursor bearer とは分離されます。Pool およびアカウント指定の補助呼び出しは既存のアカウント選択を維持します。

Claude replay は、その turn が所有権を確保した main 認証だけをメモリ内 snapshot に保持し、最終対象が正規の ChatGPT ルートである場合にのみ復元します。

エラーでは、必要に応じてクライアントダイアレクトのエンベロープが使用されますが、次のステータス/コードの意味は安定しています。

ステータス タイプまたはコード 意味
401 authentication_error 必要なプロキシ アドミッション資格情報が見つからないか無効です。
403 origin_rejected Responses/OpenAI データプレーン リクエストまたは WebSocket アップグレードが、許可されていないオリジンから送信されました。
503 combo_unavailable 選択したコンボ内のすべてのターゲットは使用不可、クールダウン中、無効、またはその他の理由で不適格です。
400 unreadable_encrypted_agent_task 暗号化された v2 ワーカー タスクには、それを処理できる正規の ChatGPT ターゲットも明示的に信頼された Responses ターゲットもありません。
426 upgrade_required 応答 WebSocket トランスポートが無効になっているか、アップグレードが失敗しました。 HTTP を使用する

Anthropic オリジンの失敗は Anthropic のエラー エンベロープでレンダリングされるため、オリジンの拒否は OpenAI スタイルの origin_rejected 本体ではなく、その方言上の 403 permission_error になります。

暗号化されたコンテンツの健全性

Section titled “暗号化されたコンテンツの健全性”

プロキシは、本物のバックエンド暗号文を不透明なものとして扱います。構造的に有効な暗号文はバイト単位で保存されます。opencodex は暗号文を復号したり、その内容を変換したり、別のプロバイダー用に再暗号化したりしません。

一部のエージェント フックはこれまで、プレーンテキストの制御テキストを encrypted_content スロットに配置していました。互換性を確保するために、プロキシは、構造的に有効な Fernet の実行を変更せずに保持しながら、プレーンテキストをテキスト部分に分割します。 agent_message が修復中にすべての暗号化された部分を失った場合、それは通常のユーザー メッセージになります。現在の v2 タスクが完全に暗号化されたままであるが、選択したルーティングされたターゲットがネイティブ ChatGPT 暗号文を読み取ることができない場合、opencodex は読み取り不能なバイトをそのプロバイダーに送信する代わりに unreadable_encrypted_agent_task で失敗します。ワーカー タスクに関するクライアントの動作については、サブエージェントサーフェス を参照してください。