Engineering

OpenAI / Anthropic SDK から SchneeAI への移行

クライアント側の移行はベース URL 1つと認証ヘッダ 1つだけ。Python・TypeScript・curl での実際の diff と、現場で起きる落とし穴を扱います。

SchneeAI2026-07-1413 分読了

OpenAI SDK の chat.completions.create(...)、あるいは Anthropic SDK の client.messages.create(...) を backend から呼んでいるなら、SchneeAI への移行は意図的に小さく作ってあります。クライアント側で変更するのはベース URL と認証ヘッダだけ。リクエストボディ (model、messages、tools、temperature) はそのまま、レスポンス shape も同じです。

これは設計です。SchneeAI の Gateway は wire level で OpenAI 互換です。同じ endpoint shape (POST /v1/chat/completions)、同じ field 名、同じ streaming SSE 形式。SDK は SchneeAI が存在することを知りません。

本記事では言語別に実際の diff を歩き、現場で起きる落とし穴を扱います。移行するかどうか自体をまだ検討中なら、先に SchneeAI vs LiteLLM を読んでください。本記事は移行を決めた前提で書いています。

Design-partner phase. 公開の api.schneeai.com endpoint は general availability の時に有効化されます。本記事の例を今すぐ実行したい場合は [email protected] まで design-partner access をご申請ください。partner 専用の endpoint と key を発行します。例の中の https://api.schneeai.com は partner endpoint で置き換えてください。

1. 実際に変わるもの

2つ。任意でもう1つ:

  1. ベース URL: https://api.openai.com/v1https://api.schneeai.com/v1
  2. 認証ヘッダ: Authorization: Bearer sk-...Authorization: Bearer <schneeai-key>
  3. モデル (任意): "gpt-4o""auto" (もしくは Model Directory の任意の alias)

リクエストボディは変更不要です。messages、tools、temperature、top_p、max_tokens、stream、stop、response_format — OpenAI API が受け付ける全 field を、SchneeAI は同じ path で同じ意味で受け付けます。

これが設計思想です。統合の surface は SchneeAI ではなくあなたの SDK です。我々は OpenAI の wire shape を晒しているので、OpenAI 互換クライアントのエコシステム全体 — Python / TypeScript の公式 SDK、LangChain の OpenAI adapter、AutoGen、Continue、Aider、OpenWebUI — がソースコード変更なしに SchneeAI に対して動きます。

2. Before / After: 実際の diff

Python — OpenAI SDK

# 変更前 — OpenAI を直接呼び出し
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["OPENAI_API_KEY"],
    # base_url のデフォルトは https://api.openai.com/v1
)

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
# 変更後 — SchneeAI 経由
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["SCHNEEAI_API_KEY"],
    base_url="https://api.schneeai.com/v1",
)

resp = client.chat.completions.create(
    model="auto",  # もしくは "gpt-4o-mini", "claude-haiku-4-5" などに固定
    messages=[{"role": "user", "content": "Hello"}],
)

# レスポンス shape は不変
print(resp.choices[0].message.content)

# SchneeAI の拡張 field は同じ場所に並ぶ
print(resp.model)        # "auto→gpt-4o-mini" — router が選んだ実体
print(resp.request_id)   # support 用の trace handle

同じ import。同じ constructor。同じ method。変わるのは引数2つ。移行 review は30秒の diff です。

TypeScript — openai-node

// 変更前
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
});

const resp = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }],
});
// 変更後
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.SCHNEEAI_API_KEY!,
  baseURL: "https://api.schneeai.com/v1",
});

const resp = await client.chat.completions.create({
  model: "auto",
  messages: [{ role: "user", content: "Hello" }],
});

curl

# 変更前 — OpenAI を直接呼び出し
curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
# 変更後 — SchneeAI 経由
curl https://api.schneeai.com/v1/chat/completions \
  -H "Authorization: Bearer $SCHNEEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

レスポンス shape

レスポンスは OpenAI shape です。SchneeAI の拡張は置き換えではなく、同じ場所に並びます:

{
  "id": "chatcmpl-...",
  "choices": [{
    "message": { "role": "assistant", "content": "Hi there." }
  }],
  "usage": { "prompt_tokens": 8, "completion_tokens": 4, "total_tokens": 12 },
  "model": "auto→gpt-4o-mini",
  "request_id": "req_01HQ..."
}

resp.choices[0].message.content を読んでいる既存コードはそのまま動きます。resp.model を読んでいるコードは、literal な gpt-4o-mini ではなく auto→... を見ることになります — router が選んだ実体が分かるという意味で便利ですが、model 文字列を厳密に比較する assertion があれば要注意です。

3. 認証: Bearer ヘッダ、SchneeAI credential

ヘッダ shape は OpenAI 直接呼び出しと同一です: Authorization: Bearer ...。変わるのはヘッダに入れる中身です — OpenAI 発行の sk-... key ではなく、あなたの service に紐づいた SchneeAI 発行の credential を送ります。

design-partner 段階では、登録時に発行する partner API key がその credential です。general availability では、service credential から Ory Hydra 経由で mint した短寿命 JWT になります — 同じ Bearer ヘッダ、長寿命 key ではなく署名付き token。token 取得の全体は Tutorial に譲ります。

移行において重要な点: ヘッダ shape は変わらない ということです。既存コードが OpenAI key を OPENAI_API_KEY に保存しているなら、最も素直な移行は SCHNEEAI_API_KEY を並べて追加し、SDK の init をそちらを読むように切り替え、traffic の切り替えが終わったら OpenAI 側の env var を消すことです。

4. モデルエイリアス: auto、もしくは固定

"model" field は3種類の値を受け付けます:

  • "auto" — SchneeAI の router が、あなたの routing policy (latency target、残 budget、feature tag) に基づいて選びます。
  • SchneeAI の alias — "gpt-4o""claude-sonnet-4-5""gemini-2-5-flash""deepseek-v3" など (一覧は Model Directory)。
  • provider 固有名 — SchneeAI が mapping して転送します。

現実的な移行 path: 移行期間中は既存の model を固定し、router を信頼できたら新 traffic から "auto" に切り替えます。

"auto" を固定より優れる理由は、provider は月次で新 model を出し、pricing は変動し、hardcode した "gpt-4o" は費用対効果の期限を越えて残り続けるからです。routing policy の変更は config の1行編集、コード変更は deploy を伴います。

5. 切り替えた後に無料で手に入るもの

traffic が SchneeAI を経由するようになると、8 hop の Gateway path を、コードを1行も書かずに引き継ぎます:

  • 呼び出し前の PII scanning — 17 カテゴリ。prompt に混じった API key は provider に届きません。
  • Budget enforcement — service / tenant / feature 単位の cap。over-budget な呼び出しは provider に課金される前に code: budget_exceeded 付きの 429 で戻ります。
  • 暗号化された raw 保持 — 全 prompt / response が Vault に置かれます (rest で AES-256 暗号化)。incident response のために query 可能。
  • 構造化 usage — service / tenant / user / model / token / cost (micro-USD) を GET /v1/usage で照会可能。
  • Audit trail — 全 request、policy 変更、運用操作の append-only 記録。
  • Provider 抽象化 — OpenAI が障害でも、routing policy が code 変更なしに Anthropic や DeepSeek に failover できます。

これらは、production で AI を運用し始めて2-3四半期目にほとんどの team が作る layer です。SchneeAI の価値は、call site を変えずにこれらを day one で引き継げることです。

6. 移行時の注意点

移行は予測可能な箇所で失敗します。よく見る5つを上げます:

Provider URL の hardcode

OpenAI SDK は base_url を省略可能です (デフォルトが https://api.openai.com/v1 だから)。SDK を迂回して URL を直書きしているコードは見落としやすいです:

# 壊れている — SDK を迂回して OpenAI を直接叩く
res = requests.post(
    "https://api.openai.com/v1/chat/completions",  # hardcoded
    headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
    json={"model": "gpt-4o", "messages": messages},
)
# 修正 — SchneeAI 経由
res = requests.post(
    "https://api.schneeai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {SCHNEEAI_API_KEY}"},
    json={"model": "auto", "messages": messages},
)

移行完了を宣言する前に、codebase 全体を api.openai.comapi.anthropic.comgenerativelanguage.googleapis.com で grep してください。

Buffer する streaming SSE client

OpenAI SDK の streaming mode は SchneeAI に対してもそのまま動きます。hand-rolled の SSE parser は、上流が完全な event を buffer していることを前提にしがちです — SchneeAI は provider の SSE を到着順に流すので、partial-event の buffer 扱いが重要です。truncation が見えたら、reader 側の buffer handling を確認してください。

Tool-calling の shape parity

SchneeAI は tools array を provider にそのまま転送します。OpenAI の tool-call shape (choices[0].message.tool_calls) も戻りで保存されます。response から tool call を読むコードはそのまま動きます — ただし provider 固有の quirk (Anthropic の tool-use は edge case で異なる) に依存している場合は要確認です。

SDK の version pinning

OpenAI Python SDK v1.x と TypeScript SDK v4.x が、我々が test している version です。古い version (Python 0.x、TypeScript 3.x) は異なる shape を使い、code 変更なしでは動きません。legacy SDK を使っている場合は、移行に合わせて upgrade してください — one-time の cost です。

Anthropic SDK は OpenAI shape ではない

Anthropic SDK (TypeScript は @anthropic-ai/sdk、Python は anthropic) は異なるリクエスト shape を使います — messages.createmax_tokens が必須、system の扱いが異なる、tool 形式が異なる、など。SchneeAI は Anthropic SDK の shape を変えないので、それを SchneeAI に向けることはできません。

Anthropic SDK からの移行の場合、2つの path があります:

  1. SchneeAI に対して OpenAI SDK に切り替える — 推奨。SchneeAI が routing する全 provider (Anthropic model を含む) に、OpenAI shape の ergonomics でアクセスできます。
  2. SDK を持たず requests.post / fetchPOST /v1/chat/completions に向ける — SDK 依存をゼロにしたい場合。

Anthropic model (claude-sonnet-4-5claude-haiku-4-5) は SchneeAI 上で同じ alias で利用可能です。OpenAI shape に切り替えても model への access は失われません。

7. 移行チェックリスト

traffic を切り替える前の pre-flight です:

  • secret manager に SCHNEEAI_API_KEY を追加。OPENAI_API_KEY (あるいは ANTHROPIC_API_KEY) は rollback 用に残す。
  • 全 SDK init を、SchneeAI endpoint から base_url / baseURL を読むように更新。
  • api.openai.comapi.anthropic.comgenerativelanguage.googleapis.com で codebase を grep — test fixture と documentation だけが hit する状態に。
  • Smoke test: 本番 shape の request 1件を SchneeAI 経由で送り、resp.choices[0].message.content が正しく parse できるか確認。
  • resp.modelauto→... (あるいは固定した alias) になっているか — router が path にいることの確認。
  • resp.request_id が空でないか — support 用の trace handle。
  • Streaming: stream: true の request が、想定 latency 内に SSE chunk を返すか確認。
  • Retry: 再送時には Idempotency-Key header (UUIDv4) を付けて、重複呼び出しが二重課金されないようにする。Error Handling 参照。
  • まず低リスクの feature (内製 tooling、batch job) を切り替え。GET /v1/usage を1日観察。
  • 残りの feature を切り替え。
  • rollback window が閉じたら、OPENAI_API_KEY / ANTHROPIC_API_KEY を secret manager から削除。

error model の全体 (retry 規則、idempotency、status code の意味) は Error Handling を参照してください。

これで手に入るもの

OpenAI 互換 API の約束は、移行が rewrite ではなく configuration 変更で済むということです。call site についてはこれは本当にその通りです — SDK init の引数2つ、env var 1つ。

その向こう側で手に入るのは、自力で作るのが難しい部分です — governance、billing、audit、PII scanning、暗号化保持、そして「どの model を使うか」を code 変更ではなく policy 決定にする routing layer。Gateway architecture の walk-through が各 hop の詳細を扱っています。

実際の workload で移行を試してみたい場合は、Tutorial に curl・Python・TypeScript の runnable な例があります — また design-partner access のご申請は メール で受け付けています。