OpenAI /v1/chat/completions
POST /v1/chat/completions は cc-router の OpenAI Chat Completions 互換入口 です。Open WebUI、Cherry Studio、Cline、LobeChat のように「OpenAI 互換」インターフェースにしか対応していないツールでも、Base URL を cc-router に向けるだけで、集約済みのすべてのサブスクリプションを利用できます。
対応バージョン:cc-router v5.0.0 以上。3 つの入口の比較は エンドポイント概要 を参照してください。
クイック接続
| 設定項目 | 入力内容 |
|---|---|
| Base URL | http://127.0.0.1:23456/v1(ツールによっては /v1 なしを求められるので、ツールの案内に従って調整) |
| API Key | cc-router 設定ページのトークン。認証を無効にしている場合は任意の空でない値 |
| モデル名 | model-fable / model-opus / model-sonnet / model-haiku、または gpt-5.6 / gpt-5.5 / gpt-5.4 / gpt-5.4-mini などのエイリアス |
ほとんどのツールは GET /v1/models からモデル一覧を自動取得するので、その中から 1 つ選ぶだけです。
Open WebUI:管理者パネル → 設定 → 接続 → OpenAI API の横の + → URL に http://127.0.0.1:23456/v1、キーにトークンを入力します。Open WebUI を Docker で動かしている場合は、127.0.0.1 を host.docker.internal に置き換え、cc-router のリッスンアドレスを「LAN」に切り替えてください。
Cherry Studio:設定 → モデルサービス → 追加 → プロバイダーの種類で OpenAI を選択 → API アドレスに http://127.0.0.1:23456、キーにトークンを入力 → 管理 → モデル一覧を取得します。
OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:23456/v1", api_key="<cc-router token>")
resp = client.chat.completions.create(
model="model-sonnet",
messages=[{"role": "user", "content": "cc-router を一文で説明して"}],
)
print(resp.choices[0].message.content)
プロトコル位置付けと翻訳フロー
クライアント (Chat Completions) cc-router 上流
────────────────────────────── ────────────────────────────────────── ─────────────
POST /v1/chat/completions ─────►│ Chat → Anthropic Messages へ翻訳 │
│ ↓ │
│ ディスパッチ pipeline(/v1/messages と同一)│──► サブ選択 / model 書換
│ ↓ │◄── 上流応答
│ Anthropic JSON / SSE → Chat 形式へ翻訳 │
◄────────────── HTTP レスポンス ──│ │
ディスパッチ pipeline は /v1/messages と完全に共通で、サブスクリプション、仮想モデル、利用上限、セッション固定、失敗時の自動切替がすべて有効です。上流はどの種類の出口(Anthropic / OpenAI / Gemini / OAuth)でもよく、クライアントはそれを意識しません。リクエストログ詳細の「受信エンドポイント」には /v1/chat/completions と表示されます。
リクエスト
POST /v1/chat/completions
Content-Type: application/json
| ヘッダ | 必須 | 説明 |
|---|---|---|
Content-Type: application/json | はい | リクエストボディは JSON 必須 |
Authorization: Bearer <token> または x-api-key | 認証設定による | 認証有効時は必須 |
x-session-id | いいえ | セッション固定用のセッション識別子。後述 |
リクエストフィールド
| フィールド | 挙動 |
|---|---|
model | 必須。仮想モデルのエイリアスルール に従って解決。欠落時は 400 |
messages | 必須かつ空不可。翻訳ルールは次節を参照 |
stream | true で SSE。既定は false |
max_completion_tokens / max_tokens | Anthropic max_tokens に翻訳。前者が優先。どちらもない場合は既定で 4096 |
reasoning_effort | Anthropic thinking.budget_tokens に翻訳(下表参照)。none は thinking を有効にしないことを意味する |
temperature / top_p | 透過。thinking 有効時は透過しない(Anthropic は thinking 時に temperature を 1 にすることを要求するため、上流の既定値に任せる) |
stop | 文字列または配列。stop_sequences に翻訳 |
tools | type: "function" のみ受け付け、Anthropic tool schema に翻訳(parameters → input_schema) |
tool_choice | auto / none / required(→ any)/ 関数指定(→ tool) |
parallel_tool_calls | false のとき disable_parallel_tool_use: true に翻訳 |
user | metadata.user_id に翻訳し、同時にセッション固定のセッション識別子としても使用 |
reasoning_effort のマッピング
reasoning_effort | thinking.budget_tokens |
|---|---|
minimal | 1024 |
low | 2048 |
medium | 8192 |
high / xhigh / max | 16384 |
| その他の値 | 8192 |
thinking が有効で max_tokens が予算以下の場合、cc-router は max_tokens を自動で「予算 + 4096」まで引き上げ、上流の「max_tokens は budget_tokens より大きくなければならない」という要件を満たします。
ツール呼び出しの強制は
reasoning_effortより優先:tool_choiceがrequiredまたは関数指定の場合、cc-router はツール選択を維持し、thinking を破棄します。ツール呼び出しの強制は機能上の要件で、思考の強さは好みにすぎないため、両者が衝突した場合は前者を優先します。
messages の翻訳ルール
| Chat メッセージ | Anthropic への翻訳 |
|---|---|
role: system / developer | 順番どおりにトップレベルの system へ連結 |
role: user、テキスト | text ブロック |
role: user、image_url | image ブロック。data:image/...;base64,... と http(s) URL に対応 |
role: assistant、content | text ブロック |
role: assistant、tool_calls | tool_use ブロック(arguments は正しい JSON であること) |
role: assistant、reasoning_content | 破棄。クライアントは正しい署名を持っていないため、上流へ送り返すと拒否される |
role: tool | tool_result ブロック(tool_call_id 必須)。連続する複数件は 1 つの user メッセージにまとめる |
隣接する同じロールのメッセージは自動で結合され、Anthropic のロール交互の要件を満たします。
非対応・無視されるフィールド
| フィールド | 扱い |
|---|---|
旧形式の functions / function_call | 400 を返す。tools / tool_choice を使用してください |
| 音声・ファイル系の content part | 400 を返す |
n > 1、logprobs、logit_bias、seed、presence_penalty、frequency_penalty | 無視 |
response_format(JSON Schema による強制を含む) | 無視し、エラーにはしない |
stream_options | 無視。ストリーミングレスポンスは常に末尾に usage フレームを 1 つ含む |
非ストリーミングリクエストの例
curl http://127.0.0.1:23456/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"model": "model-sonnet",
"messages": [
{ "role": "system", "content": "You are concise." },
{ "role": "user", "content": "cc-router を一文で説明して" }
]
}' | jq
レスポンス(非ストリーミング)
200 OK、Content-Type: application/json、標準のchat.completionオブジェクトidは上流メッセージの id から変換:msg_abc→chatcmpl-abcmodelはクライアントがリクエストで指定したモデル名をそのまま返す(仮想モデル名でも実モデル名でもない)- thinking の内容は
message.reasoning_contentに入る。DeepSeek の慣例と同じで、主要なクライアントは折りたたんで表示できる
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1790000000,
"model": "model-sonnet",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "...",
"reasoning_content": "..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 128,
"total_tokens": 170,
"prompt_tokens_details": { "cached_tokens": 0 }
}
}
finish_reason のマッピング
Anthropic stop_reason | finish_reason |
|---|---|
end_turn / stop_sequence | stop |
max_tokens | length |
tool_use | tool_calls |
refusal | content_filter |
レスポンスにツール呼び出しが含まれていれば、上流や中継サービスが end_turn を返していても、finish_reason は tool_calls になります(max_tokens / refusal を除く)。クライアントは finish_reason == "tool_calls" でツールを実行するかどうかを安心して判定できます。
usage のマッピング
| Chat フィールド | 計算方法 |
|---|---|
prompt_tokens | input_tokens + cache_creation_input_tokens + cache_read_input_tokens |
completion_tokens | output_tokens |
total_tokens | 両者の合計 |
prompt_tokens_details.cached_tokens | cache_read_input_tokens |
レスポンス(ストリーミング SSE)
200 OK、Content-Type: text/event-stream- 各フレームは
data: {chat.completion.chunk}で、event:行はなく、data: [DONE]で終了
フレームの順序:
| 順序 | 内容 | 元の Anthropic イベント |
|---|---|---|
| 1 | delta: {"role": "assistant", "content": ""} | message_start |
| … | delta: {"content": "..."} | text_delta |
| … | delta: {"reasoning_content": "..."} | thinking_delta |
| … | delta: {"tool_calls": [{index, id, type, function: {name, arguments: ""}}]} | tool_use ブロック開始 |
| … | delta: {"tool_calls": [{index, function: {arguments: "..."}}]} | input_json_delta |
| 最後から 3 番目 | delta: {} + finish_reason | message_delta |
| 最後から 2 番目 | choices: [] + usage | message_stop |
| 最後 | data: [DONE] |
ストリーミングリクエストの例
curl -N http://127.0.0.1:23456/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"model": "model-sonnet",
"stream": true,
"messages": [{ "role": "user", "content": "ping" }]
}'
先頭フレームのエラーは自動リトライ:
/v1/messagesと同様、上流が200を返したのに最初のイベントがエラーだった場合(クォータ枯渇が成功に偽装されるケースなど)、cc-router はそのまま次のサブスクリプションへ切り替えてリトライします。クライアントはそれを意識しません。途中のエラーと切断への備え:ストリームの途中で上流エラーを受け取った場合、cc-router はエラーボディのフレーム +
data: [DONE]を送出します。上流接続が予期せず切れた場合は、finish_reason、usage、data: [DONE]を補って送出するため、クライアントが待ち続けてハングすることはありません。
セッション固定
ディスパッチモードが「セッション固定」の場合、cc-router は次の優先順位でセッションを識別し、同じセッションには常に同じサブスクリプションを使います。
- リクエストボディの
userフィールド(Open WebUI などはユーザーごとに設定します) - リクエストヘッダ
x-session-id - 最初の
userメッセージ内容のハッシュ
自作のクライアントでは、会話ごとに安定した user または x-session-id を渡すと、上流の prompt cache のヒット率が大きく向上します。
エラーレスポンス
/v1/chat/completions で発生するエラーは OpenAI 形式 です。
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "invalid_request_error",
"param": null
}
}
type は OpenAI の慣例に従って分類され、code には調査しやすいよう Anthropic の元のエラー種別が入ります。
Anthropic のエラー種別(code) | OpenAI type |
|---|---|
invalid_request_error / authentication_error / permission_error | invalid_request_error |
rate_limit_error / overloaded_error | rate_limit_error |
| その他 | server_error |
| Status | 発生条件 |
|---|---|
400 | リクエストボディが正しい JSON でない、model がない、messages が空、または翻訳に失敗(旧形式の functions、非対応の content part、tool_calls の arguments が正しい JSON でない など) |
401 | 認証失敗。このときのエラーボディは Anthropic 形式です。認証は入口の handler より前で行われるためです |
500 | cc-router の内部エラー、または上流レスポンスの読み取り・解析に失敗 |
503 | その仮想モデルにサブスクリプションがバインドされていない、またはすべてのサブスクリプションが一時的に利用不可 |
| 上流の status | すべてのサブスクリプションが失敗した場合、最後の上流のステータスコードをそのまま返し、エラーボディは OpenAI 形式に翻訳 |