跳到內容

代理 API 格式

opencodex 以多種客戶端方言呈現一個本機代理。Codex 客戶端可說 Responses API,OpenAI 相容的 app 可說 Chat Completions,而 Claude Code 可說 Anthropic Messages,而不需要每個上游供應商實作每種格式。

正常轉譯路徑為:

客戶端方言 → 內部 Responses 模型 → 供應商 adapter → 供應商 wire 格式
供應商事件 → 內部 adapter 事件 → 客戶端方言

Responses 表示是橋接的中心。原生相容的路由可跳過部分轉譯並 passthrough 請求,但認證、路由、許可控制與回應安全仍在代理邊界發生。在設定中設定監聽器與許可金鑰;當一個公開模型 id 應在多個目標間選擇時使用組合

客戶端介面 端點 成功的非串流結果 成功的串流或 socket 結果
OpenAI Responses POST /v1/responses Responses JSON Responses SSE,或 WebSocket 上的 Responses JSON text frame
OpenAI Chat Completions POST /v1/chat/completions chat.completion JSON chat.completion.chunk SSE,以 [DONE] 結束
Anthropic Messages POST /v1/messages Anthropic message JSON Anthropic Messages SSE
Anthropic token 計數 POST /v1/messages/count_tokens { "input_tokens": number } 不適用
模型探索 GET /v1/models 三種目錄契約之一 不適用
語音與 Realtime POST /v1/live, POST /v1/realtime/calls 中繼的 call-creation 回應 一個獨立的 sideband WebSocket 雙向中繼 frame
Responses compaction POST /v1/responses/compact 取代歷史 JSON 不適用

這是原生 opencodex data-plane 結構。請求 body 必須是帶有非空 model 的 JSON 物件。input 可為字串或 Responses 項目陣列。

區域 接受的結構
模型與輸入 必填的非空 model;可選字串 input 或項目陣列
訊息項目 userdevelopersystemassistant 訊息;字串內容或適合該角色的型別內容區塊
內容區塊 Text、輸入圖片、輸入檔案、輸出文字、拒絕,以及 reasoning summary/text 區塊(在其父項目允許時)
工具歷史 function_callfunction_call_outputcustom_tool_callcustom_tool_call_output 項目
工具 Function 工具加上鬆散的內建或代管工具項目;tool_choice 接受 autononerequired、具名 function/custom 選擇、代管選擇或 allowed_tools
Reasoning reasoning.effortreasoning.summaryautoconcisedetailednone
接續與快取 previous_response_idstoreprompt_cache_key
生成控制 max_output_tokenstemperaturetop_pstoppresence_penaltyfrequency_penalty
服務與執行 streamservice_tierparallel_tool_callsinstructionsmetadatauser
延伸 Responses 欄位 backgroundincludeprompttexttruncation 對相容路由被接受

未知的項目型別被接受為鬆散的型別項目以向前相容。轉譯的 adapter 僅處理它識別的項目型別,且可能拒絕其供應商無法表示的功能。

stream: true 時,回應為 text/event-stream。橋接發出 Responses 事件如 response.created、output-item 與 text/tool delta,以及恰好一個終端 response.completedresponse.failedresponse.incomplete 事件。正常串流以 data: [DONE] 結束。

stream: false 或無 stream 時,相同的 adapter 事件被收集為一個 Responses JSON 物件。兩種形式都保留所選模型、輸出項目、終端狀態與 usage。

每個終端 Responses usage 物件都包含兩個 detail 物件,即使供應商未回報那些細節:

{
"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。恆存在的 detail 物件是對嚴格 Responses 客戶端的相容性保證;零可能意指「未回報」,不一定是「供應商未執行此類工作」。

websockets 啟用時,客戶端可升級 /v1/responses 而非開啟 HTTP POST。認證與來源許可在 WebSocket 握手期間發生。它們不在每個 frame 內重複。

客戶端發送 JSON text frame:

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

除了 type 之外的一切成為 Responses 請求 body,且代理為該回合強制串流。新的 response.create 取代並取消該 socket 上的前一個回合。 response.processed 被接受為 no-op 確認。無法解析或不相關的 frame 型別被忽略。

伺服器 frame 為 JSON text frame。成功的串流輸出使用會出現在 SSE data: 列中的相同 JSON payload,而無 SSE 封裝或 [DONE]。非串流的內部結果被重新框架為 response.created、零或多個 response.output_item.done frame,然後是終端 frame。錯誤使用此封裝:

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

帶有 generate: false 的暖機 frame 不呼叫上游。它回傳合成的 response.created 後接 response.completed,兩者皆有空的回應 id 且無輸出。

此端點接受帶有必填 model 與非空 messages 陣列的 OpenAI 相容 Chat Completions 請求。它將 system、user、assistant 與 tool 訊息轉譯為內部 Responses 項目;轉譯 function 工具、tool choice、圖片、reasoning effort 與支援的回應格式;執行正常 Responses 路由管線;然後將結果轉譯回來。

結構化輸出是該轉譯的一部分:帶 json_objectjson_schemaresponse_format 被轉發到路由的 openai-chat 模型。在 POST /v1/responses 上,等效請求欄位是 text.format:原生 Responses 路由在原始 Responses body 中保留它,並在模型路由到 openai-chat 供應商時轉譯為 response_format。列在供應商 noStructuredOutputModels 中的模型會在該 chat wire 上省略 response_format;同儕模型保留轉譯。未分類的後端收到該欄位並回傳自己的錯誤,而非由代理猜測其能力。

非串流輸出有 object: "chat.completion"。串流輸出使用帶有 object: "chat.completion.chunk"、choice delta、帶有 finish_reason 的終端 choice 與 data: [DONE] 的 SSE 物件。Tool-call 與 usage 資訊在來源事件帶有它們時被轉譯回來。

由於內部執行路徑基於 Responses,供應商 adapter 可施加較窄的功能集。例如,所選 adapter 無法表示的請求功能以錯誤回傳,而非靜默變更其意義。

這些端點說 Claude Code 與相容客戶端使用的 Anthropic Messages 方言。多數請求被轉譯為 Responses、正常路由,然後轉譯回 Anthropic JSON 或 Anthropic SSE。

原生 Anthropic passthrough 僅在以下全部為真時合格:

  • 原生 passthrough 未在 Claude Code 設定中被停用;
  • 請求的模型以 claudeanthropic 開頭;
  • 請求帶有原生 Anthropic bearer 或 x-api-key 憑證;
  • 在非回環 listener 上,請求還只透過 x-opencodex-api-key 攜帶有效代理許可;且
  • 無設定的別名或模型映射為路由目標聲明該模型 id。

合格的請求以 Anthropic 方言轉發,使原生 beta 標頭、thinking 簽章與訂閱身分保持端到端。否則它走 Responses 往返。

專用許可標頭絕不轉發。Authorizationx-api-key 中的代理許可密鑰也會被移除, 而另一標頭中的真正 Anthropic 憑證會保留。以逗號合併的模糊憑證標頭會 fail closed。

POST /v1/messages/count_tokens 遵循相同的模型解析與 passthrough 決策。原生合格的請求被轉發到 Anthropic 的計數端點。其他請求使用基於 system 內容、訊息與工具的本機檔案式估計並回傳:

{ "input_tokens": 123 }

相同路由服務三個期待不相容目錄封裝的客戶端。除非也存在 client_version,否則 Anthropic flavor 勝出。

契約 觸發 頂層結構 模型 id 行為
Anthropic 模型清單 anthropic-version 標頭或 ?flavor=anthropic,無 client_version { "data": [...] } 含 Anthropic model-info 項目 Claude Code 收到可讀 id;Desktop 可收到其設定檔專屬的別名家族
Codex 目錄 client_version query 參數 { "models": [...] } 原生與路由項目帶有更豐富的 Codex 目錄欄位、可見性、effort、WebSocket 與多代理中繼資料
普通 OpenAI 清單 無觸發 { "object": "list", "data": [...] } 可見的原生 id 為裸 id;路由 id 為別名或 provider/model

POST /v1/live 接受 ChatGPT/Codex App Frameless call-creation 介面。 POST /v1/realtime/calls 接受 OpenAI Realtime call-creation 介面。opencodex 選擇一個合格的 OpenAI 家族路由、為上游認證模式正規化 call-creation 請求,並中繼有界的回應。

在 call 建立後,客戶端可使用任何支援的入站形式加入 sideband WebSocket:

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

代理正規化上游 join URL,然後雙向透明中繼 text 與 binary frame。客戶端協定標頭被保留,而上游認證保持代理擁有。

Compaction 為需要縮短長 Responses 對話的客戶端回傳取代歷史。

路由型別 行為
規範 ChatGPT 或官方 OpenAI 路由 以解析的帳號與模型認證將請求轉發到原生 /responses/compact 端點
其他路由模型 執行一個內部、非串流、無工具的 compaction 回合,帶有 compaction_trigger;需要恰好一個合成的 compaction 項目,其 encrypted_contentocx1: 封裝;將該摘要解碼為 v1 取代歷史

原生 compact 回應以 32 MiB 上限緩衝,包含其宣告的 Content-Length 已超過限制的回應。Compact 專屬失敗包含:

狀態 型別或代碼 意義
400 invalid_request_error 無效 JSON/body 結構或缺失模型
404 invalid_request_error 請求的模型無法被路由
499 client_cancelled 客戶端在中繼或緩衝時取消
502 compact_response_too_large 原生 compact 輸出超過 32 MiB
502 upstream_error 連線、讀取或合成 compaction 回合失敗
502 invalid_response_error 合成回合未產生恰好一個有效、非空的 ocx1: compaction 項目

在僅回送綁定上,data-plane 許可不需要設定的金鑰。在遠端綁定上,請使用下方矩陣。「專屬」意指 X-OpenCodex-API-Key;其他欄意指 Authorization: Bearer ...x-api-key

介面 專屬 Bearer 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 與 sideband join 接受 接受 接受

Responses 家族與 Chat 請求為供應商或 Codex Direct passthrough 保留 Authorization,因此遠端代理金鑰必須使用專屬標頭。Messages 與 Realtime 介面需要更廣的客戶端相容性,因此接受所有三種形式。

錯誤在需要時使用客戶端方言的封裝,但這些狀態/代碼意義是穩定的:

狀態 型別或代碼 意義
401 authentication_error 必填的代理許可憑證缺失或無效
403 origin_rejected Responses/OpenAI data-plane 請求或 WebSocket 升級來自不允許的來源
503 combo_unavailable 所選組合中的每個目標都不可用、在冷卻中、停用或因其他原因不合格
400 unreadable_encrypted_agent_task 加密的 v2 worker task 沒有可消耗它的合格原生 ChatGPT 目標
426 upgrade_required Responses WebSocket 傳輸被停用或升級失敗;請使用 HTTP

Anthropic 來源的失敗以 Anthropic 的錯誤封裝渲染,因此該方言上的來源拒絕是 403 permission_error,而非 OpenAI 風格的 origin_rejected body。

代理將真實的後端密文視為不透明。結構有效的密文被逐位元組保留:opencodex 不解密它、轉譯其內容,或為另一個供應商重新加密它。

某些 agent hook 在歷史上曾將明文控制文字放入 encrypted_content 插槽。為相容性,代理將該明文分離為 text 部分,同時保留任何結構有效的 Fernet run 不變。若 agent_message 在該修復期間失去所有加密部分,它成為普通使用者訊息。若目前的 v2 task 保持真正加密但所選路由目標無法讀取原生 ChatGPT 密文,opencodex 以 unreadable_encrypted_agent_task 失敗,而非發送不可讀的位元組給該供應商。關於 worker task 周圍的客戶端行為,請見子代理介面