画像生成 API 統合:OpenAI 互換パターン
OpenAI 互換エンドポイントで画像生成を統合し、編集を扱い、n とサイズを安全に制限し、gpt-image-2 の一時メディアを正しく永続化します。
画像生成 API 統合とは、製品コードから安定した画像エンドポイント—通常は POST /v1/images/generations、多くの場合は POST /v1/images/edits も—を呼び出しながら、モデル選択、資格情報、クォータ、メディアストレージを各クライアントの外へ出すことです。OpenAI 互換ゲートウェイでは、アプリケーションは prompt と任意の model、n、size、quality、さらに output_format などの GPT Image フィールドを送信し、base64 ペイロードをデコードします。従来の URL 応答は、モデルがまだ対応している場合に限って扱います。このガイドでは、プロバイダ価格や品質スコアを作り出さず、本リポジトリがすでに公開しているゲートウェイルートを使用した本番向け統合経路を示します。
GPT Image の最小かつ正しい形式
一つの認証済みベース URL、ゲートウェイが発行した一つの Bearer token、現在の OpenAI GPT Image 契約を使用します。公式ドキュメントでは gpt-image-2 が現在の主要画像モデルとして示され、GPT Image の生成結果は data[].b64_json を返します。response_format パラメータは従来の DALL·E モデル向けであり、GPT Image 向けではありません。
curl -sS https://YOUR_GATEWAY/v1/images/generations \
-H "Authorization: Bearer $GATEWAY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "A product photo of a ceramic mug on a wooden table, soft window light",
"n": 1,
"size": "1024x1024",
"output_format": "png"
}' | jq -r '.data[0].b64_json' | base64 --decode > mug.png
応答は永続的な製品資産ではなく、一時メディアと課金メタデータとして扱ってください。従来モデルや OpenAI 以外のアダプターが返す一時 URL の失効後も画像が必要なら、管理下のオブジェクトストレージへ直ちにダウンロードするか、ユーザー所有権で認可される個人画像ワークベンチの永続化経路を使用します。
モデル固有のフィールドは、最新の OpenAI 画像生成ガイド と Images API リファレンス で必ず再確認してください。Google は Gemini のネイティブ画像生成を ai.google.dev で別途説明しています。ゲートウェイはこれらのドキュメントを置き換えず、アクセスを正規化するものです。
複数 SDK ではなくゲートウェイ経由で統合する理由
製品チームが画像統合で失敗する原因は、通常モデル品質ではなく運用です。
- 各サービスが異なるプロバイダキーを保持する
- prompt 実験が予算を迂回する
- 一時 CDN URL をデータベースへ保存し、後からリンク切れになる
- 画像の
nまたは解像度が、検証なしでコストを乗算する - 個人サンドボックスとチームプロジェクトが一つのキーを共有し、コスト帰属が崩れる
マルチプロバイダ LLM ゲートウェイ と OpenAI 互換 API ゲートウェイ で詳しく説明するパターンでは、クライアントを安定させたまま、運用者が安定した別名をチャネルへ対応付け、クォータを適用し、利用ログを書きます。メディアでは、同じコントロールプレーンが「後からこの資産をダウンロードできるのは誰か」にも答える必要があります。
重要なエンドポイントとリクエストフィールド
生成
POST /v1/images/generations は text-to-image の入口です。本リポジトリの画像リクエスト DTO に一般的に現れるフィールドは次のとおりです。
| フィールド | GPT Image / ゲートウェイでの役割 |
|---|---|
model | 安定した別名 gpt-image-2、または対応付けた別名を優先する。古い gpt-image-1* ID は、OpenAI の gpt-image-2 への非推奨移行経路にある |
prompt | 必須のテキスト指示 |
n | 任意の個数。ポインター型は JSON 上の存在を保持するが、このゲートウェイは課金前に n=0 または省略を 1 へ正規化する |
size | 1024x1024、1536x1024、1024x1536 などの解像度文字列。GPT Image 2 はより広い WIDTHxHEIGHT 規則も説明している |
quality | 対応時に使用するプロバイダ固有の品質トークン |
output_format | GPT Image 系の png / jpeg / webp |
output_compression | 対応形式向けの圧縮率 |
response_format | 従来の DALL·E 専用(url または b64_json)。常に base64 を返す GPT Image では非対応 |
background, moderation, stream | アダプターが対応する場合のモデル系拡張 |
user | 不正利用追跡のためのエンドユーザータグ |
すべてのフィールドがすべてのモデルで機能すると仮定してはいけません。実際に環境で有効化したモデルと、プロバイダの最新ドキュメントに対して検証してください。
編集
POST /v1/images/edits は編集 / inpaint のサーフェスです。統合では通常、元画像、任意のマスク、prompt を multipart form フィールドとして送信します。製品コードでは次を行います。
- ユーザーのアップロードをサイズ制限付き一時ストレージへ受け入れる
- ゲートウェイを呼ぶ前に MIME type を正規化する
- 生成と同じ認証と課金所有者を維持する
- 生の画像バイトをアプリケーションログへ決して記録しない
製品が「より厳密な prompt で再生成」するだけなら、生成エンドポイントだけで十分な場合があります。構図やブランド資産を維持する必要があるときに編集を追加してください。
課金の安全性も統合の一部
画像生成は乗数の多い API です。悪意ある、または不具合のあるクライアントが n を極端な値にすると、一つの HTTP リクエストが会計事故になります。このコードベースでは次の保護があります。
- クォータ計算前のリクエスト検証で
MaxImageN(128)を超えるnを拒否する nの省略または0は1へ正規化されるため、ポインターの存在を保持することと画像数ゼロを受け入れることは同じではない- 事前消費と精算の経路でも、負または飽和した課金を拒否する必要がある
- 品質 / サイズ比率は検証後にのみ乗数となる
製品側の規則:
- UI の
nは1をデフォルトにする - ユーザー入力の
nを、プラットフォーム最大値より低い上限なしで受け入れない - 複数バリエーション生成前に推定コストを表示する
- 事前消費に失敗したら UX を失敗させ、別ウォレットに対して楽観的に queue しない
TypeScript:生成、デコード、永続化
type ImageGenResponse = {
data: Array<{ b64_json?: string; url?: string; revised_prompt?: string }>;
};
async function generatePng(prompt: string, model = "gpt-image-2") {
const res = await fetch(`${process.env.GATEWAY_BASE}/v1/images/generations`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GATEWAY_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model,
prompt,
n: 1,
size: "1024x1024",
output_format: "png",
}),
});
if (!res.ok) throw new Error(await res.text());
const body = (await res.json()) as ImageGenResponse;
const b64 = body.data?.[0]?.b64_json;
if (!b64) {
// Legacy/provider-adapter path only: some non-GPT models may still return url.
if (body.data?.[0]?.url) {
return { kind: "url" as const, url: body.data[0].url, revised: body.data[0].revised_prompt };
}
throw new Error("no image payload");
}
const bytes = Buffer.from(b64, "base64");
// upload bytes to object storage under the billing owner's key prefix
return { kind: "bytes" as const, bytes, revised: body.data[0].revised_prompt };
}
Python:明示的なデコードを行う同じ契約
import base64
import os
from pathlib import Path
import requests
def generate_and_store(prompt: str, model: str = "gpt-image-2") -> Path:
r = requests.post(
f"{os.environ['GATEWAY_BASE']}/v1/images/generations",
headers={"Authorization": f"Bearer {os.environ['GATEWAY_KEY']}"},
json={
"model": model,
"prompt": prompt,
"n": 1,
"size": "1024x1024",
"output_format": "webp",
"output_compression": 80,
},
timeout=120,
)
r.raise_for_status()
b64 = r.json()["data"][0]["b64_json"]
out = Path("/tmp/out.webp")
out.write_bytes(base64.b64decode(b64))
return out
ストレージを管理し、一往復で処理したい場合は GPT Image のデフォルトである b64_json を優先してください。url は従来モデルまたはプロバイダアダプターの挙動としてのみ説明します。URL が無期限に有効だと仮定してはいけません。
個人画像ワークベンチとチーム動画ワークベンチ
本リポジトリの画像ワークベンチは、個人 / セッション / トークンのスコープです。
- UI:画像ワークベンチ
/api/user/image-workbench/*以下のユーザー API(プリセット、テンプレート、実行、資産、生成、編集)- 生成 / 編集 / ダウンロード向けの
/api/image-workbench/*以下の API キー用ルート /api/image-workbench/admin/*以下の管理者向けテンプレートツール- オブジェクトキーと履歴はユーザー所有権を使用する
現在のツリーには、チームスコープの /api/teams/:team_id/image-workbench ルートはありません。fork で追加していない限り、チーム画像履歴やプロジェクトスコープの画像資産が存在すると説明しないでください。
対照的に、動画ワークベンチ には /api/teams/:team_id/video-workbench 以下のチーム / プロジェクトルートがあります。製品担当者が「動画と同じ共有画像履歴」を求める場合、それは機能差であり、現在の画像ワークベンチがすでに提供しているものではありません。生の /v1/images/* に対するマルチテナント課金は引き続きトークンの課金所有者に従います。マルチテナント LLM ゲートウェイアーキテクチャ も参照してください。
運用者やデザイナーが対話的な履歴とプリセットを必要とする場合はワークベンチを使用します。バックエンドサービスがプログラムから資産を生成する場合は、生の /v1/images/* を使用します。後から帰属を確認できるよう、両経路で同じモデル別名と利用ログフィールドを共有してください。
メディアライフサイクルのチェックリスト
- プロバイダキーではなく、スコープ付きゲートウェイキーで送信する。
- 送信前に
n(ここでは0が1になる)、サイズ、prompt 長を検証する。 - サポート用にリクエスト ID / 利用ログ ID を記録する。
b64_jsonをデコードするか、従来 URL を一度だけ取得し、保持ポリシーに従ってオブジェクトストレージキーを永続化する。- 後続ダウンロードを所有者で認可する。画像ワークベンチ資産ではユーザー、自ら保存した API ジョブでは課金所有者を使う。
- 製品ポリシーが要求する場合、資産を失効または再モデレーションする。
- 監視スタックでモデル別の失敗率とコストを観測する。
手順 4 を省略すると、製品の不具合は API エラーではなく、数週間後の壊れたサムネイルとして現れます。
Prompt と製品実装の注意点
- アプリケーション層でブランド規則などのシステム意図とユーザー promptを分離してください。Images API は通常一つの
prompt文字列を使うため、慎重に合成します。 - プロバイダが
revised_promptを返す場合があるため、UI の下書きだけでなく実際に送信した最終 prompt を保存します。 - ユーザーごとの同時画像ジョブ数を制限してください。retry により意図せずファンアウトしやすいエンドポイントです。
- スタックが対応する場合、HTTP 429/5xx を上限付き retry と冪等なクライアントキーで処理します(LLM API の retry と冪等性)。
- マルチモデル実験では、サイズ / 品質対応を確認した後に
model別名だけを変更し、クライアント全体を分岐させないでください。 - デフォルト例には
gpt-image-2を優先し、チャネルが古いモデルをまだ公開している場合だけ旧別名を明示的に対応付けます。
一般的な統合失敗
| 症状 | 考えられる原因 | 修正 |
|---|---|---|
| 「成功」後も画像が空 | GPT Image で url を探している | b64_json を読み取りデコードする |
| クォータが急激に消費される | 上限のない n または retry storm | n を制限し、retry を重複排除する |
n=0 を「dry run」と想定した | ゲートウェイが 0 → 1 に正規化する | ゼロを送らず、呼び出し自体を省略する |
| 個人アカウントでは動くがチームキーで失敗 | キーにプロジェクト権限がない、またはモデルグループが誤っている | チームトークン権限を修正し、画像ワークベンチがチームスコープだと仮定しない |
| 編集エンドポイントが 400 | multipart フィールド名またはマスクサイズが誤っている | プロバイダ / ゲートウェイの form 契約に合わせる |
| モデルが見つからない | チャネルで別名が未対応付け | チャネル設定で安定した別名 → 上流 ID を対応付ける |
関連記事
出典
- OpenAI 画像生成ガイド
- OpenAI Images API リファレンス
- OpenAI の非推奨情報(旧 GPT Image →
gpt-image-2) - Google Gemini 画像生成
- OWASP Top 10 for LLM Applications
失敗に関する Q&A
OpenAI、Gemini、その他を三つの SDK で呼ぶべきですか?
製品が一つのクライアント契約だけを必要とするなら不要です。生成 / 編集には OpenAI 互換ゲートウェイ経路を優先し、ゲートウェイがまだ対応付けていない機能だけネイティブ SDK を使用してください。
response_format=url は本番データベースで安全ですか?
GPT Image では非対応で常に base64 を返すため、誤ったデフォルトです。まだ URL を返す従来モデルでは、一時的な参照としてのみ扱い、長期掲載する製品ページへ載せる前に再ホストしてください。
n の安全なデフォルト値は何ですか?
本番 UX では 1 から始めます。製品が複数バリエーションを明示的に必要とする場合だけ増やし、製品上限をプラットフォーム上限(このコードでは 128)より低く設定してください。no-op を期待して n=0 を送ってはいけません。
画像リクエストに streaming は必要ですか?
通常は不要です。アダプターと UI が部分プレビューから利益を得る場合だけ有効にし、それ以外は単純なリクエスト / 応答経路を維持してください。
資産を漏らさずにチームで画像履歴を共有するには?
現在のゲートウェイでは、画像ワークベンチ履歴は個人スコープです。共有ライブラリには、保存したバイトの上にアプリケーション層の共有機能を構築するか、チームプロジェクトキーを /v1/images/* の課金だけに使用し、永続化オブジェクトには独自 ACL を実装してください。
最初の本番接続
一つのバックエンドジョブを POST /v1/images/generations へ接続し、model=gpt-image-2、n=1、output_format=png、base64 デコード、即時のオブジェクトストレージアップロードを設定します。利用ログに期待する課金所有者が表示されることを確認し、チャネル設定でサイズ / 品質互換性を検証してから 2 番目のモデル別名を追加してください。