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 URLhttp://127.0.0.1:23456/v1(ツールによっては /v1 なしを求められるので、ツールの案内に従って調整)
API Keycc-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必須かつ空不可。翻訳ルールは次節を参照
streamtrue で SSE。既定は false
max_completion_tokens / max_tokensAnthropic max_tokens に翻訳。前者が優先。どちらもない場合は既定で 4096
reasoning_effortAnthropic thinking.budget_tokens に翻訳(下表参照)。none は thinking を有効にしないことを意味する
temperature / top_p透過。thinking 有効時は透過しない(Anthropic は thinking 時に temperature を 1 にすることを要求するため、上流の既定値に任せる)
stop文字列または配列。stop_sequences に翻訳
toolstype: "function" のみ受け付け、Anthropic tool schema に翻訳(parameters → input_schema)
tool_choiceauto / none / required(→ any)/ 関数指定(→ tool)
parallel_tool_callsfalse のとき disable_parallel_tool_use: true に翻訳
usermetadata.user_id に翻訳し、同時にセッション固定のセッション識別子としても使用

reasoning_effort のマッピング

reasoning_effortthinking.budget_tokens
minimal1024
low2048
medium8192
high / xhigh / max16384
その他の値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_urlimage ブロック。data:image/...;base64,... と http(s) URL に対応
role: assistant、contenttext ブロック
role: assistant、tool_callstool_use ブロック(arguments は正しい JSON であること)
role: assistant、reasoning_content破棄。クライアントは正しい署名を持っていないため、上流へ送り返すと拒否される
role: tooltool_result ブロック(tool_call_id 必須)。連続する複数件は 1 つの user メッセージにまとめる

隣接する同じロールのメッセージは自動で結合され、Anthropic のロール交互の要件を満たします。

非対応・無視されるフィールド

フィールド扱い
旧形式の functions / function_call400 を返す。tools / tool_choice を使用してください
音声・ファイル系の content part400 を返す
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-abc
  • model はクライアントがリクエストで指定したモデル名をそのまま返す(仮想モデル名でも実モデル名でもない)
  • 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_reasonfinish_reason
end_turn / stop_sequencestop
max_tokenslength
tool_usetool_calls
refusalcontent_filter

レスポンスにツール呼び出しが含まれていれば、上流や中継サービスが end_turn を返していても、finish_reason は tool_calls になります(max_tokens / refusal を除く)。クライアントは finish_reason == "tool_calls" でツールを実行するかどうかを安心して判定できます。

usage のマッピング

Chat フィールド計算方法
prompt_tokensinput_tokens + cache_creation_input_tokens + cache_read_input_tokens
completion_tokensoutput_tokens
total_tokens両者の合計
prompt_tokens_details.cached_tokenscache_read_input_tokens

レスポンス(ストリーミング SSE)

  • 200 OK、Content-Type: text/event-stream
  • 各フレームは data: {chat.completion.chunk} で、event: 行はなく、data: [DONE] で終了

フレームの順序:

順序内容元の Anthropic イベント
1delta: {"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_reasonmessage_delta
最後から 2 番目choices: [] + usagemessage_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 は次の優先順位でセッションを識別し、同じセッションには常に同じサブスクリプションを使います。

  1. リクエストボディの user フィールド(Open WebUI などはユーザーごとに設定します)
  2. リクエストヘッダ x-session-id
  3. 最初の 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_errorinvalid_request_error
rate_limit_error / overloaded_errorrate_limit_error
その他server_error
Status発生条件
400リクエストボディが正しい JSON でない、model がない、messages が空、または翻訳に失敗(旧形式の functions、非対応の content part、tool_calls の arguments が正しい JSON でない など)
401認証失敗。このときのエラーボディは Anthropic 形式です。認証は入口の handler より前で行われるためです
500cc-router の内部エラー、または上流レスポンスの読み取り・解析に失敗
503その仮想モデルにサブスクリプションがバインドされていない、またはすべてのサブスクリプションが一時的に利用不可
上流の statusすべてのサブスクリプションが失敗した場合、最後の上流のステータスコードをそのまま返し、エラーボディは OpenAI 形式に翻訳