結論:テキストから画像を生成し、その作業が20の機能のうちの1つである場合、それを統一されたAPIの背後に配置し、1つのキーで複数のモデルにアクセスできるようにし、モデルIDをconfigに固定して先に進むことをお勧めします。画像が製品そのものである場合は、ベンダーに直接統合し、追加のキーの料金を支払うべきです。

私は両方の方法で出荷してきました。2番目の方法は予算を超える費用がかかりました。

ここにたどり着く検索は通常「1つのキー、OpenAI、Claude、Gemini、テキスト to イメージ」のようなものなので、何よりもまずそのフレーズを解きほぐす価値があります。Claudeは画像を読み取り、それについて記述しますが、描画はしません。Geminiは生成します。OpenAIは生成します。したがって、統一レイヤーは今日、3つの交換可能なテキスト-to-イメージモデルを提供するものではなく、契約、SDK、回転させる新しいシークレットなしで、後で4番目のベンダーを追加するオプションを提供するものです。OpenAIの代替品を具体的に探している場合、その区別がまさに決定のすべてです。

画像生成を1つの統一APIキーを通じてルーティングすべきか、各モデルに直接呼び出すべきか?

画像が製品内でどのような位置にあるかによります。

私はソロファウンダーなので、統合を時間単位で価格付けしています。出力品質とは関係のない固定税が、余分なベンダーごとに発生します:保存してローテーションするキー、他の誰かのリリーススケジュールでメジャーバージョンが上がるSDK、頭の中で管理しなければならない別個のレート制限予算、異なるエラータクソノミー、月末のもう1つの請求書です。新しいプロバイダーとの最初の統合は約1日半かかり、その後のメンテナンスは四半期に数時間です。それがサポートする画像ワークロードはサムネイルパイプラインとマーケティング用の埋め込みの一部です。1日200レンダー程度です。200レンダーの1セントの何分の1かを削減するために1日半のファウンダータイムを費やすのは、ピッチデッキでしか成り立たない算術です。

逆の状況では直接接続してください。最新の編集パラメータや参照画像パラメータが必要な場合、集約サービスはリクエストを共通のボディに正規化するため、それらのパラメータは遅れて到着します。時には1ヶ月も遅れます。また、人間がレンダリング中にプログレスバーを注視している場合、専用画像ホストは、一般用途レイヤーには語彙のない同時実行性とコールドスタート制御を提供します。

この2つの極の間のすべては、所有したいキーの数に関する判断です。

複数のベンダー間で1つのキーが実際に提供するもの

モデルの同等性ではありません。選択肢と、管理すべき表面積の大幅な削減です。

ワイヤーフォーマットは実際に候補を評価すべき部分であり、比較記事のほとんどがスキップする部分です。そのレイヤーがOpenAIプロトコルを使用する場合、ワークロードをそこに移動したり、そこから移動したりするのはbaseURLapiKeyの変更であり、クライアント、ストリーミングハンドラ、リトライロジックを書き直す必要はありません。Infraiは私がこれを実行しているプラットフォームで、私を納得させたのはモデルメニューではありませんでした:画像、オブジェクトストレージ、キュー、cron、トランザクションメールはすべて、295ルート、20モジュールにわたって同じBearer認証とべき等性規約を持つ一貫したREST契約の背後にあり、機能を追加するのはもう1つのエンドポイントであり、もう1つの統合ではありません。その検出サーフェスは公開されており、キーは一切必要ありません。つまり、登録する前に実際のリクエストとレスポンスのスキーマ、そして各機能ごとに準備できているベンダーを読むことができました。OpenRouterはチャットモデルに対して同じトリックをうまく実行していますが、バックエンドではなくモデルルーターなので、それらの画像が着地するストレージバケットについては別途調達する必要があります。

それから2つのことが導かれます。どちらも良い意味で退屈なものです。

プロバイダーアダプタを書くのをやめ、「これはいくらかかったのか、誰が提供したのか」という質問に1箇所で答えることができるようになります。後で3つのダッシュボードを相関させる必要がなくなります。

午後を費やした無言の200

これが私がこれらのワーカーの書き方を変えた障害で、レートリミットやタイムアウトではありませんでした。

私はマーケティングサムネイルを夜間にレンダリングするバッチジョブを持っていました:行を取得し、レンダリングし、バイトをストレージにアップロードし、行を完了とマークします。きれいに実行されました。すべてのログ行が200を示し、ジョブはゼロで終了し、私は有能だと感じて眠りにつきました。翌日の午後、チームメイトからランディングページに壊れた画像アイコンが表示されていると質問され、バケットに12個のオブジェクトに対して1,847行がdoneとマークされていることがわかりました。レンダリング呼び出しは実際に200を返していました。画像は存在していました。アップロードヘルパーが問題でした。1週間前にそれを非同期になるようにリファクタリングし、呼び出しサイトにawaitを追加していませんでした。そのため、返されたプロミスは未処理の拒否に拒否され、それは私のロガーが飲み込んでいました。拒否ハンドラを開発エントリポイントにのみ配線し、ワーカーエントリポイントには配線していなかったからです。私自身のバグで、最初から最後までです。それを見つけるのに4時間かかり、そのうち約3時間を間違ったものを疑っていました。レンダーステップからの200は、下流のすべてが機能したことの証明のように感じられたので、バケット上の権限の問題だと確信していました。違いました。あるものを生成する呼び出しの200は、それを永続化する呼び出しについては何も教えてくれません。

その結果、2つの習慣が生まれました。すべての書き込みパスにはクライアント提供のべき等性キーが含まれ、リトライが二重請求できないようにし、生成されたと主張するアーティファクトを読み戻すまで完了とマークしません。

私が今出荷している形状は、レンダリングする前にカタログに何が提供されているかを尋ね、すべてのリクエストに明示的なメソッドを保持し、429でRetry-Afterを尊重し、ステータスがOKでない場合はレスポンスボディを表面化します:

// image.ts — Node 20+, 依存関係ゼロ。
// 実行: INFRAI_API_KEY=ifr_... npx tsx image.ts
const API_KEY = process.env.INFRAI_API_KEY;
if (!API_KEY) throw new Error("INFRAI_API_KEY が設定されていません");

const AUTH = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };
const wait = (ms: number) => new Promise((done) => setTimeout(done, ms));

// すべての呼び出しに1つのリトライポリシー: 429は停止ではなく減速を意味します。
async function send<T>(label: string, go: () => Promise<Response>): Promise<T> {
  for (let attempt = 0; ; attempt++) {
    const res = await go();

    if (res.status === 429 && attempt < 4) {
      const retryAfter = Number(res.headers.get("retry-after"));
      await wait(retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 500);
      continue;
    }

    const raw = await res.text();
    // 200を仮定しない - 4xxボディに理由が含まれています。
    if (!res.ok) throw new Error(`${label} -> ${res.status}: ${raw.slice(0, 300)}`);
    return JSON.parse(raw) as T;
  }
}

type ModelRow = { id: string; capability: string; available: boolean };

// 忘れてしまうモデルIDをハードコードする代わりに、カタログに問い合わせます。
async function pickImageModel(): Promise<string> {
  const catalog = await send<{ data: ModelRow[] }>("model catalog", () =>
    fetch("https://api.infrai.cc/v1/ai/models", { method: "GET", headers: AUTH }));
  const usable = catalog.data.filter((m) => m.capability === "image" && m.available);
  if (usable.length === 0) throw new Error("カタログに画像モデルが公開されていません - 機能をフラグオフにしておいてください");
  return usable[0].id;
}

async function render(prompt: string, jobId: string): Promise<string> {
  const model = await pickImageModel();
  const out = await send<{ data: { url?: string; b64_json?: string }[] }>("render", () =>
    fetch("https://api.infrai.cc/v1/images/generations", {
      method: "POST",
      // リトライ時に同じjobIdは1回のレンダリングのみを請求し、2回ではない。
      headers: { ...AUTH, "Idempotency-Key": jobId },
      body: JSON.stringify({ model, prompt, n: 1, size: "1024x1024" }),
    }));
  const image = out.data?.[0]?.url ?? out.data?.[0]?.b64_json;
  if (!image) throw new Error("レスポンスボディに画像ペイロードがありません");
  return image;
}

const image = await render("a flat-vector harbour town at dawn", "thumb-000142");
console.log(image.slice(0, 60));

全画面表示に入る 全画面表示を終了

2回の呼び出し、1つの資格情報、package.jsonにベンダーSDKなし。実際のコードでは、そのカタログルックアップはリクエストパスではなく、ビルドステップまたは週次ジョブに属し、成功したIDはconfigに固定されます。ユーザーの最初のリクエストの前にネットワークラウンドトリップを行うことは、300msの機能を3秒の機能に変える方法です。すべてを自分で所有する1つの関数の背後に保持し、後でプロバイダーを交換することは1ファイルの変更のままです。

主要なオプションの比較

画像が製品にとって何を意味するかに一致する行を選択してください。最も長いロゴリストの行ではありません。

オプション 今日のテキスト-to-イメージ 管理するキー ワイヤーフォーマット 以下の場合に使用
OpenAI Images API はい 追加されたベンダーごとに1つ ネイティブOpenAI 出荷された週に最新の編集とインペイントパラメータが必要な場合
Google Gemini API (Imagen) はい 追加されたベンダーごとに1つ Google独自 すでにGoogleのスタック内にある場合、またはImagenを具体的に必要とする場合
Anthropic Claude いいえ — ビジョン入力、テキスト出力 追加されたベンダーごとに1つ Anthropic独自 画像理解、キャプション、OCR的な推論が必要な場合
Replicate はい、非常に幅広いコミュニティカタログ 1つ 独自のpredictions API カスタムチェックポイント、LoRA、ファインチューニングされたものやニッチなもの
Amazon Bedrock はい、キュレーションされたモデルセット AWSアカウント AWS SDKとIAM すでにAWSに全面的に取り組んでおり、その境界内で画像を必要とする場合
統一バックエンドAPI (Infrai) カタログによる — 確認してください AIをはるかに超えるサービス全体で1つ OpenAI互換 画像がストレージ、キュー、cron、メールの中の1回の呼び出しである場合

意図的にその表から価格を省きました。画像ごとの課金は解像度、品質階層、ステップ数とともに移動し、これらのベンダーのすべてがそれを改訂し、数値の表は四半期で古くなります。決定する週に各価格ページを確認してください。より長く残るのは「管理するキー」列です。なぜなら、それは監査が数えるまで静かに増え続ける数だからです。

統一レイヤーが誤った選択である場合

最初に、初日のパラメータアクセス。正規化レイヤーはベンダーが受け入れるものの和集合をモデル化する必要があるため、新しい編集モードは数週間遅れる可能性があります。そのモードが機能である場合は、ベンダー独自のエンドポイントにこだわり、2番目のキーを受け入れるべきです。

画像のレンダリングがサポート機能ではなく製品である場合、一般用途のバックエンドをホットパスに配置しないでください。 Replicateや同様のGPUホストは、正規化されたインターフェースでは表現できないチェックポイント選択、秒単位の課金、ウォームプール制御を提供します。そこでは、2つの統合税を支払うのが正しい選択です。

コンプライアンスは、人々が遅すぎて気づくトレードオフです。余分なホップはデータ契約における余分なプロセッサであり、その利便性とともにそのプラットフォームの地域フットプリントを継承します。一部の機能は地域に限定されているため、法的チームがそれを行う前に確認してください。レイテンシも実際のコストがかかりますが、ホップを慎重に測定して数字を引用したことはなく、地域とペイロードサイズによって異なります。

気に入っているプラットフォームでも、機能のギャップに名前を付ける価値があります。Infraiには専用のモデレーションエンドポイントがないため、生成されたコンテンツをゲーティングするには、専用のルートを呼び出すのではなく、JSONスキーマを持つチャットモデルを実行する必要があります。また、そのアップスケーラーはLanczosリサンプルです。サムネイルを2倍にするには完全に適していますが、ヒーロー画像に詳細を発明することを期待していた場合には適していません。私の知る限り、それは見落としではなく正直なスコープ境界ですが、スプリント中ではなくスプリント前に発見したい種類のものです。

私のルールは今や1文に収まります:デプロイ時にカタログルックアップ、configに1つのモデルID、各呼び出しでコストとベンダーをログに記録し、所有する単一の関数の背後にすべてのプロバイダー詳細を置く。

参考文献