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.
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
nhoặ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:
| Field | Vai 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 |
prompt | text instruction bắt buộc |
n | số 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 |
size | chuỗi resolution như 1024x1024, 1536x1024, 1024x1536; GPT Image 2 cũng document rule WIDTHxHEIGHT rộng hơn |
quality | quality token riêng provider khi được hỗ trợ |
output_format | png / jpeg / webp cho family GPT Image |
output_compression | compression cho format hỗ trợ |
response_format | chỉ DALL·E legacy (url hoặc b64_json); không hỗ trợ cho GPT Image vì luôn trả base64 |
background, moderation, stream | extension theo model family khi adapter hỗ trợ |
user | tag 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:
- nhận user upload vào temporary store có giới hạn size
- normalize MIME type trước khi gọi gateway
- giữ cùng auth và billing owner như generations
- 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
nvượtMaxImageN(128) trước quota math nthiếu hoặc bằng0được normalize thành1, 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
nlà1 - không nhận
ndo 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
- Submit bằng scoped gateway key, không phải provider key.
- Validate
n(nhớ0trở thành1ở đây), size và prompt length trước khi gửi. - Record request id / usage log id cho support.
- Decode
b64_jsonhoặc fetch một legacy URL đúng một lần rồi persist object-store key theo retention policy. - Authorize download sau này theo owner: user cho image workbench asset; billing owner cho API job bạn tự lưu.
- Expire hoặc re-moderate asset khi product policy yêu cầu.
- 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
modelalias sau khi verify size/quality support; đừng fork toàn client. - Ưu tiên
gpt-image-2là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ứng | Nguyê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ột | n không giới hạn hoặc retry storm | Clamp n, dedupe retry |
Mong n=0 nghĩa là “dry run” | Gateway normalize 0 → 1 | Bỏ call thay vì gửi zero |
| Hoạt động trên personal account nhưng fail với team key | Key thiếu project grant / sai model group | Sửa team token grant; đừng giả định image workbench theo team scope |
| Edit endpoint trả 400 | Sai multipart field name hoặc mask size | Căn theo form contract của provider/gateway |
| Không tìm thấy model | Alias chưa map trên channel | Map stable alias → upstream id trong channel config |
Đọc liên quan
Nguồn
- OpenAI image generation guide
- OpenAI Images API reference
- OpenAI deprecations, GPT Image cũ →
gpt-image-2 - Google Gemini image generation
- OWASP Top 10 for LLM Applications
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.