spicyapiドキュメント
ガイド
本文

ガイド:画像の生成と編集

画像エンドポイントという一群の共通の作法 — 2 つのリクエスト形、出力サイズの 3 通りの書き方、参照画像が請求に加えるもの、アダプター重み、そしてエラーの読み方。

このガイドが扱うのは、特定のモデルではなく画像エンドポイントという一群です。どのエンドポイントでも変わらない部分と、それ以上に役立つ話として、あるエンドポイント向けに書いたリクエストボディが別のエンドポイントでは通らなくなる「食い違う部分」を説明します。

パラメータ表はあえて載せません。パラメータの出どころはただ 1 つ、呼び出すモデルについてライブカタログが返す inputSchema だけです。このページはその inputSchema の読み方と、そこに明示されていないことを扱います。

知りたいこと読むべきページ
キーと残高から始めて 1 枚目の画像まで通しでクイックスタート
同じ道のりを動画で、手順どおりにチュートリアル:画像から動画を作る
あるモデル固有の全フィールドそのモデルの inputSchema — モデルカタログとスキーマ を参照
ある画像の顔や頭を別の画像へ移す被写体スワップ

MODEL_ID_FROM_CATALOG はプレースホルダーです

以下の例では、実際のモデル ID が入る場所をすべて MODEL_ID_FROM_CATALOG と書いています。この ID は認証付きのライブカタログ(GET /api/v1/models?modality=image)から取得し、課金の発生する操作の前に enabled: true と available: true の両方を確認してください。カタログの参照は無料です。プレースホルダーをそのまま送っても "not found" が返るだけです。

2 つのリクエスト形

画像の仕事は 2 種類のタスクとして届き、カタログ上でも分かれています。

  • 生成エンドポイントは /text-to-image で終わります。言葉を送ると画像が返ります。
  • 編集エンドポイントは /edit で終わります。言葉に加えて画像を 1 枚以上送ります。

編集には表記が 2 通りあり、カタログを絞り込む前に両方を知っておく価値があります。エンドポイントは edit サフィックスで公開される一方、tasks 配列ではそれを image-to-image として分類します。task クエリパラメータはどちらも受け付けます。モデルカタログとスキーマ を参照してください。

どちらの形も、作成エンベロープ、タスクのライフサイクル、結果の形式は他のメディアモデルとまったく同じです。違うのは input の中身だけです。

prompt はどちらでも必須

編集エンドポイントなら画像だけで成立するはずだ、したがって prompt は任意だろう — そう考えたくなります。プロンプトを受け付けるエンドポイントに関しては、そうではありません。prompt はどちらの形でもルートの required に入っており、省くと課金が発生する前に拒否されます。

編集エンドポイントに拒否される例
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
  }
}
prompt: is required

画像は「どこから始めるか」を、プロンプトは「それをどうするか」を伝えます。指示を受け取らなかった編集エンドポイントには、働きかける対象がありません。

例外が 1 つありますが、それは例外的なエンドポイントというより 1 つの系統です。操作そのものがエンドポイント側で固定されているため、prompt をまったく受け付けず画像だけを取る編集エンドポイントがあります。これらは被写体スワップのツールキットに属し、カタログで toolkit フィールドを持つので見分けられます。被写体スワップ を参照してください。これらで prompt: is not a supported field が返るのは不具合ではなく正しい応答です。

入力画像のフィールド名は 3 通り

どれを使うかはそのエンドポイントのスキーマの一部であり、タスク種別から推測できるものではありません。

フィールド形通常の意味
image_url画像参照 1 つ被写体の画像はちょうど 1 枚で、2 枚目という概念がエンドポイントにない
image_urls画像参照の配列編集・合成の素材そのもの。指示が参照する順番で並べる
reference_image_urls画像参照の配列本来なら文字だけの生成を画像で方向づける。被写体ではなくガイド

意外に思われるのは reference_image_urls です。これは生成エンドポイントに現れるので、/text-to-image のエンドポイントでも画像を受け取れます。「text-to-image だからリクエストに画像は入らない」と決めつけないでください。

画像参照とは、公開 HTTPS URL、アップロード済みの spicy:// URI、または小さな data:image/…;base64,… URI のことです。アップロードについては メディア を、正規のフィールド名については Spicy Schema 契約 を参照してください。

配列の上限は想像以上にばらつく

上記の配列には minItems と maxItems があり、サーバーは両方を強制します。そして値はエンドポイントごとに異なります。現在のカタログでは、ちょうど 1 件しか受け付けない配列から 10 件まで受け付ける配列まで幅があります。同じ系統の別エンドポイントで、同じフィールド名でも上限が違うことがあります。

image_urls: must contain at least 1 items
image_urls: must contain at most 3 items

minItems と maxItems は、呼び出すモデルのスキーマから読んでください。配列を素の array としか書かず、要素の形も長さの上限も示さないパラメータ表は、読み手が唯一行動に移せない行になります。このサイトがそうした表を持たないのは、まさにそのためです。

出力サイズの書き方は 3 通り

既存のリクエストボディを 2 つ目のエンドポイントに向けたとき、まず最初にぶつかるのがこれです。

画像カタログ全体では 3 通りの書き方が使われており、各エンドポイントはそのうち 1 通りだけを受け付けます。

書き方フィールド備考
分割aspect_ratio と resolution画面の形とピクセルの段を別々に選ぶ
プリセットsize1 つの enum 値で両方を同時に固定する
明示width と heightピクセル数の整数。それぞれに minimum と maximum がある

新しいエンドポイントでは分割型がもっとも一般的で、3 つのなかでは使い勝手も良いものです。aspect_ratio は生成後に切り抜くのではなく生成前に画面を構成するため、ピクセル予算を変えずに形だけを変えられます。

別の書き方のフィールドは無視ではなく拒否されます

画像の inputSchema はすべて additionalProperties: false です。そのエンドポイントが列挙していないフィールドは拒否されます。つまり、他のエンドポイントから持ってきたリクエストボディは、黙ってデフォルトに落ちるのではなく、そのまま失敗します。

失敗は双方向に起こり、メッセージはフィールド名を挙げます。

size: is not a supported field
aspect_ratio: is not a supported field

どちらも課金されません。代償はお金ではなく前提のほうです。1 つの input オブジェクトを書いて複数の画像エンドポイントに向けることはできませんし、すべてが受け付ける「互換の書き方」も存在しません。

価格の段を選ぶのはどちらか

分割型のエンドポイントでは、2 つのフィールドは通常それぞれ違う役割を請求のなかで担います。

  • resolution は通常、価格の段を選びます。 見積もりの variant に現れ、動かせば金額も動きます。
  • aspect_ratio は通常、選びません。 構図を変えることは拡大縮小ではないからです。

この 2 文の「通常」は本当に効いています。機械可読な答えはフィールド自身にあります。x-ui.affects_price: true と、エンドポイントが宣言している場合は x-pricing.variantFields への所属です。習慣ではなくスキーマを確認してください。料金の読み方 と、注釈のリファレンスである Spicy Schema 契約 を参照してください。

許可される値もエンドポイントごとです。拒否メッセージがそれを列挙するので、無料で確認できます。

aspect_ratio: must be one of: "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"

探索中には便利ですが、enum を読むことの代わりにはなりません。スキーマにはフィールドを省いたときに適用される default も書かれているからです。

参照画像が請求に加えるもの

これは静かに効いてくるもので、応答のどこもそれを指し示さないため、独立した節を立てるだけの価値があります。

メディアモデルの通常の計算は 価格 × 数量 で、数量は入力中の quantityField の値(quantityField が空なら 1 単位)です。画像エンドポイントの quantityField はたいてい num_outputs で、期待どおりに振る舞います。4 枚頼めば見積もりの quantity は 4 になります。この軸は正直です。動くのが見えます。

一部のエンドポイントはそのうえで、入力画像 1 枚ごとに別立ての料金を加えます。そしてこちらは動くのが見えません。同じエンドポイントを、参照 1 枚と配列を埋めた状態の 2 回見積もると、こうなります。

  • unit は同じ。
  • quantity も同じ。
  • variant も同じ。
  • 金額だけが違う。

見積もりを組み立てるときに使う 4 つのフィールドのうち 3 つが、まったく動かないままです。価格 × 数量 から計算した見積もりは実際より少なく出て、応答のどこにもその印はありません。

機械可読な特徴

x-ui.affects_price: true が付いていて、quantityField でもなく、x-pricing.variantFields にも載っていないフィールドは、段を選ばずに金額だけを動かします。そうしたエンドポイントでは、カタログの pricing を下限として読み、金額は見積もりから取ってください。

これは モデルカタログとスキーマ に書かれているのと同じ規則で、画像編集はこの規則にもっとも頻繁に出会う場所です。入力画像の配列はしばしばこの印を持ちます。

どれだけ加算されるかはエンドポイント固有で、決まった比率もありません。現在のカタログには、配列を埋めると参照 1 枚の数倍になるエンドポイントも、追加の参照がまったく無料のエンドポイントも存在し、尋ねる以外に見分ける方法はありません。ですから配列の価格を同系統のエンドポイントから類推しないでください。参照 1 枚の見積もりから満杯の配列の価格を割り出すのも同じです。

実際に送るボディで見積もる

支払う前に、このリクエストそのものの金額を出す
curl -sS https://api.spicyapi.ai/api/v1/jobs/quote \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID_FROM_CATALOG","input":{"prompt":"your instruction","image_urls":["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]}}'

見積もりはタスク作成とまったく同じ検証を行いますが、費用はかからず、何も開始せず、何も確保しません。そのボディちょうどについて estimatedCost、maxCharge、quantity、unit を返します。配列を変えたらもう一度見積もってください。詳細と、応答を取りこぼしたときの復旧手順は 見積もりと互換性 に、実運用の組み込みで見積もりをどこに置くかは 本番組み込み にあります。

アダプター重み

一部のエンドポイントでは、ベースモデルの上に自分でファインチューニングした重みを載せられます。正規のフィールド名は loras、段階を別々に公開するエンドポイントでは high_noise_loras と low_noise_loras です。

オブジェクトの配列、内側にも独自のルール

これらのフィールドは x-ui.widget: "object-list" を持ちます。items がそれ自体で完結したオブジェクトスキーマである配列です。独自の required、独自の additionalProperties: false、そしてサブフィールドごとの型・範囲・default を持ちます。典型的な 1 件は、重みのセット 1 つとその適用の強さを指定します。

2 件を同時に適用する
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "prompt": "your scene",
    "loras": [
      { "path": "https://weights.example.com/style.safetensors", "scale": 1 },
      { "path": "https://weights.example.com/character.safetensors", "scale": 0.6 }
    ]
  }
}

1 件の形はエンドポイント間で固定ではありません。 重みファイルとして何を受け付けるか(直接のアドレスか、組み込みライブラリのキーか)は、そのエンドポイント自身の items サブスキーマによって狭められます。サブフィールド名、入る件数、どれが必須かも同様です。この例ではなく items を読んでください。ウィジェットの説明は Spicy Schema 契約 に、それを入れ子の表にする方法は モデルカタログとスキーマ にあります。

この要素の内側にある規則で 1 つ誤読しやすいものがあります。デフォルトは配列の要素の内側まで届きます。 items.required に載っていて、かつ default を持つサブフィールドは事実上任意です。省けば検証の前にデフォルトが埋められます。デフォルトのないサブフィールドは、欠けていれば拒否されます。つまり items.required は「自分が送る JSON に必ず書かなければならない」という意味ではありません。

重みの入手元

path には次の 3 種類のいずれかを指定できます。

  • 公開リンク。 .safetensors ファイルへのリンクです。エンドポイントが受け付ける場合は、owner/repo 形式の Hugging Face リポジトリ ID も使えます。モデルは生成時にダウンロードするため、新しい重みでの最初の呼び出しは、その後の呼び出しより目に見えて遅くなります。
  • アップロード。 直接アップロードの流れで、ファイルを application/octet-stream として最大 2 GiB までアップロードし、返ってきた spicy://f/… URI を使います。コミット時に、完全な safetensors ファイルであるかを確認します。
  • インポート。 サインインが必要なファイル(非公開またはゲート付きの Hugging Face リポジトリにあるファイルや、API キーが必要な Civitai のファイルなど)は、リンクとアクセストークンを POST /api/v1/files/import に送ります。SpicyAPI がファイルを 1 回だけダウンロードし、spicy://f/… URI を返します。トークンはそのダウンロードにだけ使われ、保存されません。
非公開の Hugging Face リポジトリから LoRA をインポートする
curl -sS https://api.spicyapi.ai/api/v1/files/import \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  --data "$(jq -n --arg url "https://huggingface.co/OWNER/REPO/blob/main/your_lora.safetensors" --arg token "$HF_TOKEN" '{url: $url, token: $token}')"

url には、リポジトリに .safetensors ファイルがちょうど 1 つだけある場合に限り、リポジトリ ID(OWNER/REPO)やリポジトリのページも指定できます。レスポンスの形はコミットと同じで、data.uri を loras[].path に入れます。アップロードした重みとインポートした重みは、ほかのアップロードと同じく 1 日保持され、ほかのアカウントとは共有されません。それを過ぎたら、あらためてアップロードまたはインポートしてください。

送信時に確認されるのはリンクであって、中の重みではありません

タスクを作成する前に、各重みのリンクを確認します。サインインが必要なリンク、404 を返すリンク、ウェブページが開くリンクは 400 で拒否され、課金されません。メッセージには該当する要素が示されます。それ以外は通過するため、後からホストがダウンした場合や、重みの読み込みに失敗した場合は、タスクの失敗として返ってきます。

この確認で見つからない問題は、後からタスクの失敗として返ってきます。errorCode はプラットフォームの 9 分類のいずれかで、メッセージには何が起きたかが示され、そのタスクのために確保された金額は返金されます。エラー を参照してください。

最悪のケースはさらに静かです。別のベースモデル向けに作られた重みは、失敗せずに読み込まれたうえで、出力をほとんど変えないことがあります。読むべきエラーはなく、ベースモデルそのものに見える画像だけが残ります。

だからデプロイ時に確認する

確認をリクエスト経路から出し、リリース時に走るものへ移してください。

  1. 到達性。 本番に投入しようとしているすべての重みアドレスに HEAD リクエストを投げます。プラットフォームもタスク作成時に各リンクを確認しますが、今日使えるリンクが来週使えなくなることはあり、デプロイ時の確認なら、トラフィックより先にそれを見つけられます。
  2. 効き目。 新しい重みファイルごとに 1 回生成し、同じプロンプトと同じシードで重みなしの結果と比べます。区別がつかないなら、そのファイルは期待どおりに働いていません。バッチをそれに任せる前に確かめてください。
  3. 安定性。 アドレスは本番にある限り動き続ける必要があります。消えた重みファイルは原因の見えないタスク失敗に化け、それはデプロイの何日も後に、そのデプロイのせいにされながら起こります。

これらのフィールドについてのエンドポイント固有の助言 — ある値が何に対して測られるのか、どの設定が目に見えて遅くなるのか、スキーマは受け付けるが後悔することになる入力はどれか — は、公開カタログの paramNotes にあります。パラメータ名をキーとし、ドキュメントの 10 言語すべてで提供されます。これは散文であって規則ではありません。何が受理されるかを決めるのは今も inputSchema です。モデルカタログとスキーマ を参照してください。

他のメディアモデルと同じく非同期

画像エンドポイントはリクエスト・レスポンスの呼び出しではなくタスクです。タスクを作成し、ポーリングまたは Webhook で結果を受け取ります。queued と running は非終端、succeeded・failed・expired が終端です。状態の正典は 非同期タスク です。

画像は動画より早く終わるので、同期的な呼び出しのように書きたくなります。踏みとどまってください。ここでは待ち時間が短く同期のように感じられますが、待機の予算が尽きた時点でまだ走っているタスクをコードは扱わなければなりませんし、それを Webhook に委ねるほうがリクエストを開いたまま保持するよりよくスケールします。ポーリングはタスク送信とは独立した、アカウントの読み取り枠を使います。頻繁すぎるポーリングは状態照会のレート制限を招く場合があります。レート制限 を参照してください。

結果を読む

成功したタスクは output.assets[] にファイルを列挙します。画像エンドポイントでのこの配列について 3 点。

  • ファイルは複数入りえます。 num_outputs が 1 より大きければ、画像 1 枚につき 1 件のアセットが返ります。最初からループとして書いてください。assets[0] だけを読むハンドラは、支払った残りを黙って捨てます。
  • 拡張子を決めるのは mime であって、エンドポイント名でもプロンプトでもありません。 エンドポイントが output_format を持つ場合はそのフィールドがエンコードを選びます。持たない場合、エンコードはそのエンドポイント固有のもので、何を受け取ったかを知る手段は mime だけです。
  • コピー中のファイルは pending: true を持ち url を持ちません。 結果は手渡される前に SpicyAPI 自身のストレージへコピーされ、タスクが成功を報告してから数秒かかります。リンクがないことを失敗と扱わず、タスクをもう一度読んでください。

ダウンロードリンクは約 20 分有効で、API キーは不要です。新しいものが必要ならタスクを読み直してください。生成ファイルの保持は最長 14 日、アカウントの保持設定がより短ければそれに従います。結果とダウンロード と 保持と破棄 を参照してください。

拒否を読む

拒否されたリクエストは費用も発生せず何も作らないので、見積もりエンドポイントは、これから送ろうとしているボディの無料のバリデータとしても使えます。

最初の 1 つだけでなく、すべての問題が報告されます。 メッセージは ; で連結されるので、1 回の拒否でボディの不備をすべて知ることができます。

prompt: is required; loras.0.path: is required; loras.0.scale: must be at most 4; bogus: is not a supported field

配列の内側の問題は、その要素を指します。 ポインタは フィールド.添字.サブフィールド の形で、添字は 0 始まりです。

loras.1.scale: must be at most 4

このポインタは修正する人にそのまま渡してください。複数の要素が同時に動いているとき、どれが範囲外かを知っていることが、1 行の修正と二分探索の差になります。

出会う形は次のとおりです。

メッセージ意味対処
X: is requiredX がルートの required にある追加する。編集エンドポイントではたいてい prompt か画像フィールド
X: is not a supported fieldX が properties にない。未知のフィールドは無視されず拒否される削るか、このエンドポイントの書き方に合わせる — 出力サイズ を参照
X: must be one of: …値が enum の外メッセージが受理値を列挙する。enum と default はスキーマにある
X: must contain at least N itemsminItems を下回るエンドポイントが要求する配列は空では送れない
X: must contain at most N itemsmaxItems を超えるこのエンドポイントの上限を読む。どのエンドポイントでも同じではない
X: must be an array値が宣言された型ではない単一の要素をリストで包む
X: must be at most N charactersmaxLength を超えるプロンプトにもエンドポイントごとの上限がある
X.<n>.<sub>: is required要素 <n> のサブフィールドが必須でデフォルトを持たないその要素にだけ追加する
X.<n>.<sub>: is not a supported field要素スキーマが additionalProperties: falseそのキーをその要素から削る
X.<n>.<sub>: must be at most Nサブフィールドが自身の範囲の外収める。範囲は配列単位ではなくサブフィールド単位

通るはずのリクエストが拒否されたときは、たいていスキーマが古いのが原因です。400 の後はカタログを読み直し、恒久的なスナップショットは決して持たないでください。キャッシュした写しがどれだけ有効かは カタログのキャッシュ にあります。残りは トラブルシューティング が扱います。

次に読むもの

このページの内容