Tạo ảnh Tương thích OpenAI Tích hợp API Media persistence Billing safety

Tích hợp API tạo ảnh: pattern tương thích OpenAI

Tích hợp tạo và chỉnh sửa ảnh qua endpoint tương thích OpenAI, giới hạn n/size an toàn và persist media tạm đúng cách với gpt-image-2.

AveMujica API 8 phút đọc

Tích hợp API tạo ảnh là việc product code gọi một image endpoint ổn định—thường là POST /v1/images/generations và thường kèm POST /v1/images/edits—trong khi đưa model selection, credential, quota và media storage ra khỏi từng client. Trong gateway tương thích OpenAI, application gửi prompt cùng model, n, size, quality tùy chọn và các field GPT Image như output_format, rồi decode payload base64; chỉ xử lý response URL legacy khi model còn hỗ trợ. Hướng dẫn này trình bày đường tích hợp có hình dạng production bằng các gateway route repository đã expose, không bịa provider price hoặc quality score.

Hình dạng đúng tối thiểu cho GPT Image

Dùng một base URL đã authenticate, một Bearer token do gateway phát hành và contract OpenAI GPT Image hiện tại. Tài liệu chính thức đưa gpt-image-2 làm image model chính hiện nay; GPT Image generation trả data[].b64_json. Parameter response_format áp dụng cho model DALL·E legacy, không áp dụng cho 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

Hãy coi response là media tạm thời cộng billing metadata, không phải product asset bền vững. Nếu product cần ảnh sau khi temporary provider URL hết hạn—với model legacy hoặc adaptor không phải OpenAI—hãy download ngay vào object storage bạn kiểm soát, hoặc dùng path persistence của personal image workbench mà gateway authorize bằng user ownership.

Luôn kiểm tra lại field theo model trong hướng dẫn tạo ảnh OpenAI và Images API reference hiện tại. Google document native Gemini image generation riêng trên ai.google.dev. Gateway không thay thế các tài liệu đó; nó chuẩn hóa access.

Vì sao tích hợp qua gateway thay vì nhiều SDK

Product team thường thất bại khi tích hợp ảnh vì lý do vận hành, không phải quality model:

  • mỗi service giữ provider key khác nhau
  • prompt experiment bypass budget
  • temporary CDN URL bị lưu vào database rồi hỏng về sau
  • image n hoặc resolution nhân chi phí mà không validation
  • personal sandbox và team project dùng chung một key nên cost attribution sụp đổ

Pattern gateway đa provider—trình bày rộng hơn trong multi-provider LLM gateway và OpenAI-compatible API gateway—giữ client ổn định trong khi operator map stable alias tới channel, enforce quota và ghi usage log. Với media, cùng control plane còn phải trả lời “sau này ai được download asset này?”

Endpoint và request field quan trọng

Generations

POST /v1/images/generations là entry point text-to-image. Các field thường xuất hiện trong image request DTO của repository này gồm:

FieldVai trò trên GPT Image / gateway
modelƯu tiên stable alias gpt-image-2 hoặc alias bạn map; ID gpt-image-1* cũ đang trên đường deprecation của OpenAI hướng tới gpt-image-2
prompttext instruction bắt buộc
nsố lượng tùy chọn; pointer type giữ việc field có xuất hiện trong JSON, nhưng gateway này normalize n=0 hoặc omitted thành 1 trước billing
sizechuỗi resolution như 1024x1024, 1536x1024, 1024x1536; GPT Image 2 cũng document rule WIDTHxHEIGHT rộng hơn
qualityquality token riêng provider khi được hỗ trợ
output_formatpng / jpeg / webp cho family GPT Image
output_compressioncompression cho format hỗ trợ
response_formatchỉ DALL·E legacy (url hoặc b64_json); không hỗ trợ cho GPT Image vì luôn trả base64
background, moderation, streamextension theo model family khi adapter hỗ trợ
usertag end-user để trace abuse

Đừng giả định mọi field hoạt động trên mọi model. Validate với model thực sự enabled trong environment và tài liệu hiện tại của provider.

Edits

POST /v1/images/edits là surface edit/inpaint. Integration thường gửi multipart form field cho source image, mask tùy chọn và prompt. Product code nên:

  1. nhận user upload vào temporary store có giới hạn size
  2. normalize MIME type trước khi gọi gateway
  3. giữ cùng auth và billing owner như generations
  4. không bao giờ log raw image byte trong application log

Nếu product chỉ cần “regenerate bằng prompt chặt hơn”, generations có thể đã đủ. Chỉ thêm edits khi user phải giữ composition hoặc brand asset.

Billing safety là một phần của integration

Image generation là API nhiều multiplier. Client độc hại hoặc có bug đặt n cực lớn có thể biến một HTTP request thành thảm họa accounting. Trong codebase này:

  • request validation từ chối n vượt MaxImageN (128) trước quota math
  • n thiếu hoặc bằng 0 được normalize thành 1, nên “giữ pointer presence” không đồng nghĩa “tạo zero image”
  • pre-consume và settle path vẫn phải từ chối charge âm hoặc saturated
  • quality/size ratio chỉ thành multiplier sau validation

Quy tắc product:

  • mặc định UI n là 1
  • không nhận n do user cung cấp nếu chưa có upper clamp thấp hơn platform max
  • hiển thị estimated cost trước multi-variant generation
  • nếu pre-consume thất bại, UX phải fail; đừng “queue optimistic” vào wallet khác

TypeScript: generate, decode, persist

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: cùng contract với decode tường minh

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

Ưu tiên b64_json, mặc định của GPT Image, khi bạn kiểm soát storage và muốn một round trip. Chỉ đề cập url như hành vi model legacy hoặc provider adapter. Không bao giờ giả định URL còn hiệu lực vô hạn.

Personal image workbench và team video workbench

Repository này expose image workbench có scope personal / session / token:

  • UI: image workbench
  • User API dưới /api/user/image-workbench/*, gồm preset, template, run, asset, generate, edit
  • Biến thể API-key dưới /api/image-workbench/* cho generate/edit/download
  • Admin template tooling dưới /api/image-workbench/admin/*
  • Object key và history dùng user ownership

Trong tree hiện tại không có route team-scoped /api/teams/:team_id/image-workbench. Đừng tuyên bố có team image history hoặc project-scoped image asset trừ khi fork của bạn bổ sung chúng.

Trái lại, video workbench có route team/project dưới /api/teams/:team_id/video-workbench. Nếu product manager yêu cầu “shared image history giống video”, đó là feature gap chứ không phải thứ image workbench hiện có. Multi-tenant billing cho raw /v1/images/* vẫn theo billing owner của token; xem kiến trúc LLM gateway đa tenant.

Dùng workbench khi operator hoặc designer cần interactive history và preset. Dùng raw /v1/images/* khi backend service generate asset bằng code. Cả hai path nên dùng cùng model alias và usage-log field để attribution về sau.

Checklist vòng đời media

  1. Submit bằng scoped gateway key, không phải provider key.
  2. Validate n (nhớ 0 trở thành 1 ở đây), size và prompt length trước khi gửi.
  3. Record request id / usage log id cho support.
  4. Decode b64_json hoặc fetch một legacy URL đúng một lần rồi persist object-store key theo retention policy.
  5. Authorize download sau này theo owner: user cho image workbench asset; billing owner cho API job bạn tự lưu.
  6. Expire hoặc re-moderate asset khi product policy yêu cầu.
  7. Observe failure rate và cost theo model bằng monitoring stack.

Nếu bỏ bước 4, product bug sẽ xuất hiện vài tuần sau dưới dạng thumbnail hỏng chứ không phải API error.

Lưu ý về prompt và product engineering

  • Tách system intent là brand rule khỏi user prompt trong app layer; Images API thường chỉ có một chuỗi prompt, nên cần compose cẩn thận.
  • Lưu final prompt đã gửi, không chỉ UI draft, vì provider có thể trả revised_prompt.
  • Giới hạn concurrent image job mỗi user; retry dễ vô tình fan-out image endpoint.
  • Map HTTP 429/5xx thành bounded retry và dùng idempotent client key khi stack hỗ trợ (LLM API retries và idempotency).
  • Với experiment nhiều model, chỉ đổi model alias sau khi verify size/quality support; đừng fork toàn client.
  • Ưu tiên gpt-image-2 làm example mặc định; map alias cũ tường minh nếu channel còn expose.

Các lỗi integration phổ biến

Triệu chứngNguyên nhân có thểCách sửa
Không có ảnh sau “success”Tìm url trên GPT ImageĐọc b64_json và decode
Quota burn đột ngộtn không giới hạn hoặc retry stormClamp n, dedupe retry
Mong n=0 nghĩa là “dry run”Gateway normalize 0 → 1Bỏ call thay vì gửi zero
Hoạt động trên personal account nhưng fail với team keyKey thiếu project grant / sai model groupSửa team token grant; đừng giả định image workbench theo team scope
Edit endpoint trả 400Sai multipart field name hoặc mask sizeCăn theo form contract của provider/gateway
Không tìm thấy modelAlias chưa map trên channelMap stable alias → upstream id trong channel config

Đọc liên quan

Nguồn

Hỏi đáp khi failure

Tôi có nên gọi OpenAI, Gemini và provider khác bằng ba SDK không?

Không nếu product chỉ cần một client contract. Ưu tiên gateway path tương thích OpenAI cho generations/edits, và giữ native SDK cho feature gateway chưa map.

response_format=url có an toàn cho production database không?

Đó là default sai cho GPT Image vì không hỗ trợ và luôn trả base64. Với model legacy còn trả URL, chỉ coi URL là temporary pointer và re-host trước khi dùng trên product page dài hạn.

Default an toàn cho n là gì?

Bắt đầu ở 1 trong production UX. Chỉ tăng khi product thật sự cần variant, và giữ hard product max thấp hơn platform max (128 trong codebase này). Đừng gửi n=0 với kỳ vọng no-op.

Image request có cần streaming không?

Thường là không. Chỉ bật streaming nếu adapter và UI hưởng lợi từ partial preview; nếu không hãy giữ request/response path đơn giản.

Team chia sẻ image history mà không leak asset thế nào?

Với gateway hiện tại, image workbench history là personal. Với shared library, xây application-layer share trên byte bạn lưu, hoặc chỉ dùng team project key để billing /v1/images/* trong khi tự triển khai ACL trên persisted object.

Lần nối production đầu tiên

Nối một backend job tới POST /v1/images/generations với model=gpt-image-2, n=1, output_format=png, decode base64 và upload object storage ngay. Xác nhận usage log cho thấy đúng billing owner, rồi chỉ thêm model alias thứ hai sau khi verify compatibility size/quality trong channel config.