# Images（OpenAI/Grok）

文生图与图像编辑。该端点在网关里同时开放给 **OpenAI 分组**和 **Grok 分组**。

```text
POST https://api.clomio.ai/v1/images/generations
POST https://api.clomio.ai/v1/images/edits
# 兼容别名:
POST https://api.clomio.ai/images/generations
POST https://api.clomio.ai/images/edits
```

> **分组要求:** OpenAI 或 Grok 分组支持；其他分组返回 404。见 [端点 × 分组](#/api-usage)。Videos 端点是 Grok-only，这里只作为相邻能力旁注，具体见 [Videos](#/api-videos)。

---

## 路由与上游形态

| 场景 | 网关行为 | 适合用途 |
|------|----------|----------|
| `/v1/images/generations` / `/v1/images/edits` + OpenAI 分组 | 使用 OpenAI-compatible Images 能力 | 单次文生图、单次编辑、SDK `client.images.generate/edit` |
| `/v1/images/*` + OpenAI/Codex 图片模型 | 使用分组开放的图片能力 | Codex/Responses 图片工作流 |
| `/v1/images/*` + Grok 分组 | 使用 xAI/Grok 图片能力；模型名前缀需是 `grok-imagine-` | Grok Imagine 图片生成/编辑 |
| `/v1/responses` + `tools:[{"type":"image_generation"}]` | 不是本页 Images API；这是 Responses 图片工具路径 | 多轮对话中生成/继续编辑图片 |

OpenAI 官方也把 Image API 与 Responses API 图片工具区分为两条入口：Image API 适合单次生成/编辑，Responses API 适合对话式、多步图片体验。参考：<https://developers.openai.com/api/docs/guides/image-generation>、<https://developers.openai.com/api/docs/guides/tools-image-generation>。

Grok Imagine 图片属于 xAI/Grok 能力。模型名请使用控制台分组开放的 `grok-imagine-*` 前缀模型；不要把 OpenAI `gpt-image-*` 模型名拿到 Grok 分组里测。若上游返回临时 URL，建议立刻下载保存到自己的对象存储或本地文件，避免后续链接过期、权限变化或无法复现。

### Grok/xAI Imagine 响应差异

Grok Imagine 默认常见 `data[0].url` 响应，也支持 `aspect_ratio`、`resolution` 等字段；需要内联图片时传 `response_format:"b64_json"`。当前服务对显式 `response_format:"url"` 返回 400，因此想拿 URL 时请省略该字段。响应还可能带 `mime_type`、`revised_prompt` 和 usage 元数据,客户端应按实际响应读取。

JSON edits 使用本文下方的 `images[]` 形态；`image:{...}` 不是当前服务接受的 JSON 编辑格式。

---

## 请求校验与字段边界

服务会先做以下请求校验/规范化：

- 请求体不能为空；JSON 请求必须是合法 JSON，否则返回 `Request body is empty` / `failed to parse request body`；`multipart/form-data` 必须带 boundary。
- `model` 为空时默认填 `gpt-image-2`；非空时必须是图片模型前缀：`gpt-image-*` 或 `grok-imagine-*`，否则 400：`images endpoint requires an image model, got "..."`。
- `n` 必须是正整数，上限为 10；JSON 类型错误返回 `invalid n field type`，越界返回 `n must be between 1 and 10`。
- `size` 会被规范化：空值保持空、`auto` 保持 `auto`、`1k/2k/4k` 会映射为尺寸；不合法 `宽x高` 会尽量校正到最近的合法尺寸，无法解析时回落 `1024x1024`。
- 合法尺寸约束：宽高为正数、最大边不超过 `3840`、宽高比不超过 `3:1`、总像素在 `655360..8294400`，并按 16 像素步进校正。
- `stream` 必须是 boolean，JSON 非 boolean 返回 `invalid stream field type`，multipart 无法解析为 bool 返回 `invalid stream field value`；`output_compression` / `partial_images` 必须是数字，范围分别为 0..100 / 0..3。multipart 单个 part 约 50MB 上限,超过会返回 `payload exceeds 52428800 bytes`。
- `response_format` 只接受空值或 `b64_json`；显式传 `url` 会被拒绝：`response_format=url is not supported for gpt-image models; use b64_json`。Grok 想拿 URL 时请省略该字段。
- `background:"transparent"` 需要 `output_format` 为 `png` 或 `webp`；`output_compression` 只在 `output_format` 为 `jpeg` 或 `webp` 时有效；`input_fidelity` 不支持 `gpt-image-2`。
- edits：`multipart/form-data` 必须上传 `image` 文件；JSON edits 必须提供 `images[].image_url` 或 `images[].file_id`。
- edits 的 `mask` 图片若尺寸与首张 `image` 不同，网关会尝试按首图尺寸重采样为 PNG。

其他模型支持细节主要由模型决定：例如 `quality`、`moderation`、`style` 是否被接受，以及返回的错误字段。OpenAI 官方说明 `gpt-image-2` 支持灵活尺寸但不支持透明背景；服务会先拒绝明显不合法的 `transparent` + 非 `png/webp` 组合。参考：<https://developers.openai.com/api/docs/guides/image-generation>。

---

## 文生图: generations

### 请求参数

| 参数 | 类型 | 必填 | 处理 / 说明 |
|------|------|:----:|------|
| `model` | string | 否 | 空值默认 `gpt-image-2`；非空必须为 `gpt-image-*` 或 `grok-imagine-*` |
| `prompt` | string | 上游通常要求 | 画面描述；具体必填和长度限制以模型为准 |
| `n` | integer | 否 | 默认 1；必须 >0，上限 10 |
| `size` | string | 否 | OpenAI/GPT Image 常用尺寸字段,支持 `auto`、`1k/2k/4k`、`宽x高`；网关会规范化/就近校正 |
| `aspect_ratio` | string | 否 | Grok/xAI Imagine 常用画幅字段,如 `16:9` |
| `resolution` | string | 否 | Grok/xAI Imagine 常用分辨率字段,如 `1k`/`2k` |
| `quality` | string | 否 | `low` / `medium` / `high` / `auto` 等，是否支持由模型决定 |
| `background` | string | 否 | `opaque` / `auto`；透明背景取决于模型，`gpt-image-2` 当前不支持 `transparent` |
| `output_format` | string | 否 | `png` / `webp` / `jpeg` |
| `output_compression` | integer | 否 | JPEG/WebP 压缩质量，0-100；必须是数字 |
| `moderation` | string | 否 | 模型支持时的审核强度提示 |
| `style` | string | 否 | 旧模型/特定模型选项，网关识别后透传 |
| `stream` | boolean | 否 | 流式返回；必须是 boolean |
| `partial_images` | integer | 否 | 流式时的中间图数量；必须是数字，范围 0..3 |
| `response_format` | string | 否 | 只接受空值或 `b64_json`；`url` 会被拒绝 |
| `conversation_id` / `parent_message_id` | string | 否 | 多轮图片工作流的可选会话标识 |
| `original_file_id` / `original_gen_id` | string | 否 | 引用已有图片或生成结果的可选标识 |
| `mask_file_id` | string | 否 | 引用已有蒙版的可选标识 |

### 响应

OpenAI GPT Image 响应示例应优先按 `data[].b64_json` 处理，不要把示例写成读取 URL 字段；Grok/xAI Imagine 默认常见 `data[].url`，如果需要 inline/base64 再显式传 `response_format:"b64_json"`：

```json
{
  "created": 1730000000,
  "data": [{ "b64_json": "<base64 图片数据>" }],
  "usage": { "input_tokens": 12, "output_tokens": 1024, "total_tokens": 1036 }
}
```

旧模型或部分兼容上游可能返回 `data[].url`；Grok/xAI 默认也常见 URL。GPT Image 常规示例应解码 `b64_json`，OpenAI 官方示例也是读取 `response.data[0].b64_json` 后 base64 解码保存。

### 最小可验证 curl

下面请求可验证 OpenAI/Grok 图片路由、图片模型前缀、`response_format` 限制和常规 `data[].b64_json` 响应处理；把 `response_format` 改为 `url` 可验证错误 `response_format=url is not supported for gpt-image models; use b64_json`。

### 示例

```bash
curl https://api.clomio.ai/v1/images/generations \
  -H "Authorization: Bearer 你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "一只在钢琴上弹奏的橘猫,扁平插画风格,浅色背景",
    "size": "1024x1024",
    "quality": "high",
    "n": 1
  }' | jq -r '.data[0].b64_json' | base64 --decode > cat.png
```

```python
import base64
from openai import OpenAI
client = OpenAI(api_key="你的密钥", base_url="https://api.clomio.ai/v1")

r = client.images.generate(
    model="gpt-image-2",
    prompt="一只在钢琴上弹奏的橘猫,扁平插画风格",
    size="1024x1024",
)
img = base64.b64decode(r.data[0].b64_json)
open("cat.png", "wb").write(img)
print("已保存 cat.png")
```

```javascript
import fs from "fs";
import OpenAI from "openai";
const client = new OpenAI({ apiKey: "你的密钥", baseURL: "https://api.clomio.ai/v1" });

const r = await client.images.generate({
  model: "gpt-image-2",
  prompt: "一只在钢琴上弹奏的橘猫,扁平插画风格",
  size: "1024x1024",
});
fs.writeFileSync("cat.png", Buffer.from(r.data[0].b64_json, "base64"));
console.log("已保存 cat.png");
```

---

## 图像编辑: edits

用 `multipart/form-data` 上传原图及可选蒙版，或用 JSON 提供已上传文件/图片 URL 引用。

### 请求参数: multipart/form-data

| 字段 | 类型 | 必填 | 处理 / 说明 |
|------|------|:----:|------|
| `model` | string | 否 | 空值默认 `gpt-image-2`；非空必须为图片模型前缀 |
| `image` / `image[]` | file | 是 | 原图；可多张，单个 part 当前读取上限 50MB |
| `prompt` | string | 上游通常要求 | 修改描述；具体必填和长度限制以模型为准 |
| `mask` | file | 否 | 蒙版；尺寸不一致时网关尝试按首图尺寸重采样 |
| `size` | string | 否 | 同 generations，网关会规范化/就近校正 |
| `n` | integer | 否 | 默认 1；必须 >0，上限 10 |
| `quality` | string | 否 | 透传，是否支持由模型决定 |
| `background` | string | 否 | 透传/模型决定；`gpt-image-2` 不支持透明背景 |
| `output_format` | string | 否 | `png` / `webp` / `jpeg` |
| `output_compression` | integer | 否 | 必须是整数 |
| `input_fidelity` | string | 否 | 是否支持由模型决定；OpenAI 文档说明 `gpt-image-2` 自动高保真，不允许手动改变 |
| `style` / `moderation` / `partial_images` / `stream` | mixed | 否 | 按模型支持范围处理 |
| `original_file_id` / `original_gen_id` / `mask_file_id` | string | 否 | 可与远程图片或已上传文件引用配合 |

### 请求参数: JSON edits

JSON 方式适合复用已上传文件或远程图片：

```json
{
  "model": "gpt-image-2",
  "prompt": "把背景换成星空",
  "images": [
    { "image_url": "https://example.com/cat.png" }
  ],
  "mask": { "image_url": "https://example.com/mask.png" },
  "size": "1024x1024"
}
```

`images` 必须是数组，且至少一个元素包含 `image_url` 或 `file_id`；也可用 `original_file_id`、`original_gen_id`、`mask.file_id` 或 `mask_file_id` 引用已有素材。

### 示例

```bash
curl https://api.clomio.ai/v1/images/edits \
  -H "Authorization: Bearer 你的密钥" \
  -F model="gpt-image-2" \
  -F image="@cat.png" \
  -F prompt="把背景换成星空" \
  -F size="1024x1024" \
  | jq -r '.data[0].b64_json' | base64 --decode > edited.png
```

```python
import base64
from openai import OpenAI
client = OpenAI(api_key="你的密钥", base_url="https://api.clomio.ai/v1")

r = client.images.edit(
    model="gpt-image-2",
    image=open("cat.png", "rb"),
    prompt="把背景换成星空",
)
img = base64.b64decode(r.data[0].b64_json)
open("edited.png", "wb").write(img)
print("已保存 edited.png")
```

---

## Responses image_generation tool 对照

如果你的请求本来是对话式 `/v1/responses`，不要改成 `/v1/images/*`；应在 Responses 请求中声明图片工具：

```json
{
  "model": "gpt-5.5",
  "input": "画一张白底产品图",
  "tools": [{
    "type": "image_generation",
    "size": "1024x1024",
    "quality": "high",
    "output_format": "png"
  }]
}
```

网关的 Responses 图片工具路径会规范化 `tools[].size`，并把旧字段 `format` / `compression` 迁移为 `output_format` / `output_compression`。OpenAI 官方说明该工具返回 `image_generation_call`，图片结果在该调用项的 `result` 字段中；它和 `client.images.generate()` 的 `data[0].b64_json` 不是同一个响应形态。

---

## 实战场景: 批量生成并保存到本地

```python
import base64
from openai import OpenAI

client = OpenAI(api_key="你的密钥", base_url="https://api.clomio.ai/v1")

prompts = ["赛博朋克城市夜景", "水彩风格的山间小屋", "扁平插画的办公场景"]
for i, p in enumerate(prompts):
    r = client.images.generate(
        model="gpt-image-2",
        prompt=p,
        size="1024x1024",
        quality="high",
    )
    img = base64.b64decode(r.data[0].b64_json)
    with open(f"out_{i}.png", "wb") as f:
        f.write(img)
    print("已保存 out_%d.png" % i)
```

---

## 排障

| 现象 | 常见原因 | 处理 |
|------|----------|------|
| 404 `Images API is not supported for this platform` | API Key 所在分组不是 OpenAI/Grok | 换到 OpenAI 或 Grok 分组 |
| 403 `Image generation is not enabled for this group` | 当前分组未开放图片能力 | 更换已开放图片能力的 OpenAI/Grok 分组；如应已开放，带 `x-request-id` 提交工单 |
| 403 `Codex image generation is not enabled for this group` | 当前 OpenAI 分组未开放 Codex/Responses 图片能力 | 改用原生 Images API，或更换已开放该能力的 OpenAI 分组 |
| 400 `images endpoint requires an image model, got ...` | 实际使用的模型不是图片模型 | 检查请求 `model` 是否来自控制台图片模型列表；图片端点只能用 `gpt-image-*` / `grok-imagine-*`，持续异常带 `x-request-id` 提交工单 |
| 400 `Request body is empty` / `failed to parse request body` | 空 body 或 JSON 非法 | 发送合法 JSON 或 multipart |
| 400 `response_format=url is not supported for gpt-image models; use b64_json` | 请求显式要求 URL 返回 | 移除 `response_format` 或改为 `b64_json` |
| 400 `invalid stream field type` / `invalid stream field value` | `stream` 不是 boolean | JSON 传 true/false，multipart 传 true/false 字符串 |
| 400 `invalid output_compression field type` / `invalid partial_images field type` / `partial_images must be between 0 and 3` / `output_compression must be between 0 and 100` | 数值类型或范围不合法 | 按文档范围修正 |
| 400 `background=transparent requires output_format png or webp` | 透明背景和输出格式不匹配 | 把 `output_format` 改为 `png`/`webp` 或取消透明背景 |
| 400 `output_compression is only supported when output_format is jpeg or webp` | 对 PNG/auto 使用压缩参数 | 改为 `jpeg`/`webp` 或删除 `output_compression` |
| 400 `input_fidelity is not supported for gpt-image-2` | `gpt-image-2` 不接受该字段 | 删除 `input_fidelity` 或换支持模型 |
| 400 `multipart boundary is required` / `payload exceeds 52428800 bytes` | multipart 缺 boundary 或单个 part 太大 | 修正 Content-Type 或压缩/拆分文件 |
| 400 `image file is required` | edits multipart 未上传 `image` | 使用 `-F image=@file.png` 或 JSON `images[]` |
| 没有 `url` 字段 | GPT Image 常规返回 `b64_json` | 用 `base64.b64decode(r.data[0].b64_json)` 保存 |
| Grok Imagine 返回 URL 后过期/打不开 | 上游图片 URL 可能是临时链接或受权限控制 | 成功后马上下载保存；不要把 URL 当长期素材库 |
| 透明背景失败 | 所选模型不支持，如 `gpt-image-2` | 改用 `background:"auto"/"opaque"` 或换模型 |

## 字段约束速查

| 字段 | 可选值/范围 | 说明 |
|---|---|---|
| `model` | `gpt-image-*` 或 `grok-imagine-*` | 必须是当前分组开放的图片模型 |
| `n` | `1` 至 `10` | 生成数量；上游可能有更低限制 |
| `size` | `auto`、`1k`、`2k`、`4k` 或合法 `宽x高` | GPT Image 会按模型规则校正尺寸 |
| `quality` | `auto`、`low`、`medium`、`high` | Grok 模型是否支持由上游决定 |
| `background` | `auto`、`opaque`、`transparent` | transparent 还要求 `output_format` 为 png/webp |
| `output_format` | `png`、`jpeg`、`webp` | 具体模型支持由上游决定 |
| `output_compression` | `0` 至 `100` | 仅 jpeg/webp |
| `response_format` | 空值或 `b64_json` | GPT Image 拒绝显式 `url`；Grok 省略时通常返回 URL |
| `stream` | `true` / `false` | 流式图片还可配 `partial_images`=`0..3` |
| `aspect_ratio` | 例如 `1:1`、`16:9`、`9:16` | Grok Imagine 扩展字段 |
| `resolution` | 例如 `1k`、`2k`、`4k` | Grok Imagine 扩展字段 |

## 完整成功响应

```json
{
  "created": 1780000000,
  "data": [{"b64_json":"BASE64_IMAGE","revised_prompt":"..."}],
  "usage": {"input_tokens": 12, "output_tokens": 1024, "total_tokens": 1036}
}
```

Grok URL 模式可能返回 `data[].url`；客户端应同时兼容 `b64_json`、`url` 和 `mime_type`。

## 图片批处理 API

批处理是异步任务 API，不等同于单次 `/generations`。规范路径如下：

| 方法 | 路径 | 用途 |
|---|---|---|
| `POST` | `/v1/images/batches` | 提交批任务 |
| `GET` | `/v1/images/batches` | 按状态、时间、游标查询任务 |
| `GET` | `/v1/images/batches/models` | 查询批处理可用模型 |
| `GET` | `/v1/images/batches/{id}` | 查询任务 |
| `GET` | `/v1/images/batches/{id}/items` | 查询任务条目 |
| `GET` | `/v1/images/batches/{id}/items/{custom_id}/content` | 下载单个结果 |
| `GET` | `/v1/images/batches/{id}/download` | 下载 ZIP（含 manifest） |
| `POST` | `/v1/images/batches/{id}/cancel` | 取消任务 |
| `DELETE` | `/v1/images/batches/{id}` | 删除任务记录 |
| `DELETE` | `/v1/images/batches/{id}/outputs` | 清理输出文件 |

提交体核心字段：`model`、`task_name`、`items[]`；每个 item 至少包含 `custom_id` 和 `prompt`，可选 `output_count`、`reference_images`。`output_count` 必须为正整数，条目数量、文件大小和参考图限制由当前分组配置决定。

```bash
curl https://api.clomio.ai/v1/images/batches \
  -H "Authorization: Bearer 你的密钥" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: batch-demo-001" \
  -d '{
    "model":"gpt-image-2",
    "task_name":"catalog-covers",
    "items":[
      {"custom_id":"cover-001","prompt":"蓝色极简产品封面","output_count":1},
      {"custom_id":"cover-002","prompt":"橙色极简产品封面","output_count":1}
    ]
  }'
```

批处理错误常见为 `API_KEY_REQUIRED`、`INVALID_ITEMS`、`BATCH_NOT_FOUND`、`ITEM_NOT_FOUND`、`INVALID_STATUS`、`IMAGE_INDEX_OUT_OF_RANGE` 和 `BATCH_CANCEL_NOT_ALLOWED`；错误体包含 `error.code`、`error.message`，下载接口成功时返回 `image/*` 或 `application/zip` 二进制。
