# 多模态能力

来源: https://platform.lyrouter.com/ai_gateway/docs/multimodal
更新日期: 2026-10-19

除文本对话与向量外，网关还支持**视觉理解**（图片输入）、**图像生成与编辑**、**视频生成**。文本类接口（`chat/completions`、`embeddings`）见 [调用网关](https://platform.lyrouter.com/ai_gateway/docs/call-gateway)。

本文覆盖网关上**当前全部可调用的多模态模型**，逐个列出接口、全部参数（含取值范围与默认值）、计费口径和可直接运行的示例。

> [!note]
> 模型的增减以控制台**模型广场**与 `/v1/models` 为准。本文随模型上架定期更新，短期内可能有个别新模型尚未写入。

## 模型总览

### 图像生成 / 编辑（同步，直接在响应里拿图）

| 模型 | 接口 | 请求处理 | 计费 |
|------|------|------|------|
| `bytedance/doubao-seedream-5.0-pro` | `/v1/images/generations` | 透传 | 按张，分档 |
| `bytedance/doubao-seedream-5.0-lite` | `/v1/images/generations` | 透传 | 按张，不分档 |
| `openai/gpt-image-2` | `/v1/images/generations`<br>`/v1/images/edits` | 透传 | 按 token，四项 |
| `openai/gpt-image-2.5-flare` | 同上 | 透传 | 按 token，三项 |
| `openai/gpt-image-2.5-sunburst` | 同上 | 透传 | 同上 |
| `google/gemini-3-pro-image` | 同上 | **契约转换** | 按 token，三项 |
| `google/gemini-3.1-flash-image` | 同上 | **契约转换** | 按 token，两项 |

**透传**的在生成接口（`/v1/images/generations`）上也会丢掉几个参数：GPT-Image 系列固定丢 `response_format`，2.5 两个还丢 `stream` / `stream_options`。被丢的参数会在响应头 `X-Gateway-Unsupported-Parameters` 里列出。编辑接口（`/v1/images/edits`）不丢，这几个参数会转发给上游（只传 `stream: true` 时网关目前还会补上 `stream_options`，上游返回 400），不要传。

GPT-Image 的「四项」是输入文本 / 输入图片 / 输出 / 缓存命中，2.5 系列的 `usage` 不含缓存字段，只有前三项；Gemini 的「三项」是输入 / 输出 / 思考，只有 Pro 产生思考 token，Flash 只有前两项。Seedream pro 的参考图（输入图片）另计一项，每次请求第 1 张不收费。分档维度见各模型详情。

### 视频生成（异步任务）

| 模型 | 接口 | 请求处理 | 计费 |
|------|------|------|------|
| `bytedance/doubao-seedance-2.5` | `/v1/videos` | 契约转换 | 按 token，分辨率 × 视频输入分档 |
| `bytedance/doubao-seedance-2.0-fast` | `/v1/videos` | 契约转换 | 按 token，按视频输入分档 |
| `bytedance/doubao-seedance-2.0-mini` | `/v1/videos` | 契约转换 | 同上 |
| `minimax/minimax-h3` | `/v1/videos`<br>`/v1/videos/regenerations`<br>`/v1/videos/context` | 契约转换 | 按秒，按分辨率分档 |
| `minimax/minimax-h3-max`<br>⚠️ 已被服务商下线，目前不可用，请改用 `minimax/minimax-h3` | `/v1/videos` | 契约转换 | 按秒，按分辨率分档 |

MiniMax 的升清（`/videos/regenerations`）单独一档，参考图单独计一项；上下文增强（`/videos/context`）产出的是文本，按 token 计。

> [!important]
> **本文不写具体单价。** 价格随运营调整，不同环境（测试 / 生产）也不相同 —— 请以**控制台模型广场**上该模型的实时价格为准，逐次调用的实际扣费见[查看日志](https://platform.lyrouter.com/ai_gateway/docs/logs)。
>
> 这里只说明**计费口径**：按什么计量（张 / token / 秒 / 字符）、分成哪几项、按什么维度分档。口径是接口契约的一部分，不随调价变化。

### 视觉理解（图片输入、输出文本）

走标准 `/v1/chat/completions`，模型为带「视觉」能力标签的对话模型，见下文「视觉理解」一节。

鉴权与其它调用一致——始终用你的**网关密钥**作为 Bearer token，网关据此识别工作空间并注入上游凭据（你的密钥不会透传给上游）：

```
Authorization: Bearer sk-你的密钥
```

（截图：在输出模态中选择图像和视频，查看当前工作空间可用的多模态模型与计费单位。）

## 请求处理模式：透传与契约转换

**读参数表之前先看这一节**——同一个参数在不同模型上会不会生效，取决于这个模型走哪种模式。

| 模式 | 网关做什么 | 你该照谁的文档写参数 |
|------|-----------|---------------------|
| **透传** | 请求体原样转发给上游，只改写模型名 | **厂商原生参数**，厂商支持什么你就能用什么 |
| **契约转换** | 把网关的统一参数翻译成厂商的私有形状 | **网关契约**（下文每个模型都列全了） |

两种模式都不会静默吞掉参数：

- 契约转换时，契约外的参数会被丢弃，并在响应头 `X-Gateway-Unsupported-Parameters` 里逐个列出（Gemini 图像模型只转发 `prompt`、`model`、`image`、`size`，其余都按此处理）；视频模型对契约内但这个模型不支持的参数直接返回 400 并说明原因。例外见下文「视频通用参数（网关契约）」：Seedance 的 `reference_audio` 会被静默丢弃，用表单提交时也有几个字段不会按预期处理。
- 不想要这种"丢了也继续"的行为，请求时带上 `X-Gateway-Strict-Parameters: true`，被丢弃的参数会让请求直接返回 400，错误码 `unsupported_parameters`。

> [!warning]
> 有一类参数即使在透传模型上也会被**有意丢弃**：转发给上游只会让请求失败，而它又改变不了结果。目前只在 GPT-Image 系列的生成接口上：`response_format`（该系列固定返回 base64，上游对这个参数时灵时不灵），以及 2.5 两个模型的 `stream` / `stream_options`（上游会开启流式，导致请求失败）。丢弃同样会写进 `X-Gateway-Unsupported-Parameters`，带 `X-Gateway-Strict-Parameters: true` 时同样返回 400。编辑接口不做这种丢弃。

## 视觉理解（图片输入对话）

图片输入走标准的 `chat/completions`，在 `content` 里以 `image_url` 携带图片（`data:` 内联 base64 或公网 URL），与 OpenAI 多模态格式一致：

```bash
curl https://api.lyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "支持视觉的模型名",
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "这张图里有什么？" },
        { "type": "image_url", "image_url": { "url": "https://example.com/cat.jpg" } }
      ]
    }]
  }'
```

> [!tip]
> 需要选用**支持视觉**的模型（模型广场里带「视觉」能力标签）。给纯文本模型发图片会被上游拒绝。

## 图像生成与编辑（同步）

两个端点与 OpenAI 的图像 API 一致，请求发出后直接在响应里拿到图片，不需要轮询：

```
POST /v1/images/generations    文生图
POST /v1/images/edits          图生图 / 图像编辑
```

> [!warning]
> **各模型耗时差别很大。** Seedream 约 20 秒；GPT-Image 系列一次生成实测约 20~60 秒（最长一次约 83 秒），生成接口上参数错误的 400 实测在 2 秒内返回；Gemini 3 Pro 实测 20~250 秒，偶有超过 300 秒仍未返回。客户端超时请按分钟级设置，不要沿用文本接口的 30 秒。

响应是 OpenAI 形状，产物固定在顶层 `data[]` 数组里：

```json
{
  "created": 1760000000,
  "data": [ { "b64_json": "…" } ],
  "usage": { "input_tokens": 13, "output_tokens": 196, "total_tokens": 209 }
}
```

### 基本用法

**文生图**：

```bash
curl https://api.lyrouter.com/v1/images/generations \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bytedance/doubao-seedream-5.0-lite",
    "prompt": "一只戴墨镜的柴犬坐在冲浪板上，黄昏海面，电影感",
    "size": "2K"
  }'
```

**图像编辑**（`multipart/form-data`，把原图作为文件段上传）：

```bash
curl https://api.lyrouter.com/v1/images/edits \
  -H "Authorization: Bearer sk-你的密钥" \
  -F model=openai/gpt-image-2 \
  -F quality=medium \
  -F image=@cat.png \
  -F prompt="把背景换成雪山"
```

**图像编辑（JSON 形态）**——把图片作为 base64 放在 JSON 体里：

```bash
curl https://api.lyrouter.com/v1/images/edits \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-3-pro-image",
    "prompt": "把背景换成雪山",
    "image": "data:image/png;base64,iVBORw0KGgo…"
  }'
```

`image` 可以是单个字符串，也可以是字符串数组（多张参考图）。

> [!note]
> **Gemini 两个模型现在两种形态都收**（2026-10-09 起）。它们的上游是 Google 原生
> `generateContent`，只认 JSON（图片要落成 `inlineData`）—— 收到 multipart 时网关会先把它
> 归一成字段表，再按契约翻译过去，所以官方 OpenAI SDK 的 `images.edit()` 也能直接驱动它们。
>
> 在此之前这条路走不通。旧文档、旧版规格附件里「Gemini 只能用 JSON」的说法**已经过期**。

两种请求体的支持情况（2026-10-09 实测）：

| 模型 | multipart（文件上传） | JSON（base64） |
|---|---|---|
| `openai/gpt-image-2` | ✅ | ✅ |
| `openai/gpt-image-2.5-flare` | ✅ | ✅ |
| `openai/gpt-image-2.5-sunburst` | ✅ | ✅ |
| `google/gemini-3-pro-image` | ✅ | ✅ |
| `google/gemini-3.1-flash-image` | ✅ | ✅ |

> [!note]
> **透传模型**（GPT-Image 系列）的 multipart 路径上，网关只定位并改写 `model` 字段，
> **文件段一个字节都不动**。**契约转换模型**（Gemini 系）则相反：网关会解析 multipart、
> 把文件段转成 base64，再按该模型的契约重组请求体 —— 这也是它为什么有 30 MB 的内联上限。
> JSON 路径同理：GPT-Image 系列的 JSON 请求体原样转发，图片由上游校验；Gemini 系的图片格式
> 优先取 data URI 里声明的 MIME，传裸 base64 时按 PNG / JPEG / WEBP / GIF 的字节特征识别，
> 都认不出时返回 400 并指明是第几张图 —— 网关**不会**替你挑一个默认格式。
> 另外请注意上游会校验图片本身：文件损坏或色彩模式不受支持的图片会被退回
> `Invalid image file or mode`，那是图片的问题，不是接口没接通。
> Seedream 的图生图不走这个端点，走 `/v1/images/generations` 的 `image` 参数。

下面按模型列出全部参数。**先看这个模型走哪种处理模式**——透传的照厂商文档写，契约转换的照网关契约写。

---

### Seedream 5.0 pro / lite（火山方舟）

两个模型都是**透传**——下表的参数原样送到上游，厂商支持什么就能用什么。

#### 能力对比

| 能力 | `doubao-seedream-5.0-pro` | `doubao-seedream-5.0-lite` |
|------|---------------------------|----------------------------|
| 文生图 | ✓ | ✓ |
| 文生组图（一次出多张） | 暂不支持 | ✓ |
| 单图 / 多图生图 | ✓ | ✓ |
| 单图 / 多图生组图 | 暂不支持 | ✓ |
| **交互编辑**（坐标定位编辑） | ✓ | ✗ |
| **图层拆分**（拆成底图 + 最多 16 图层） | ✓ | ✗ |
| 流式输出 | 暂不支持 | ✓ |
| 联网搜索 | 暂不支持 | ✓ |
| 可选分辨率档位 | `1K`、`1.5K`、`2K` | `2K`、`3K`、`4K` |
| 输出格式 | `png`、`jpeg` | `png`、`jpeg` |
| 提示词优化模式 | 标准、极速 | 标准 |
| 生成数量限制 | 单图，或 1 底图 + 最多 16 图层 | 参考图数 + 生成图数 ≤ 15 |
| 限流 | 500 张/分钟 | 500 张/分钟 |

#### 参数

| 参数 | 类型 | 默认 | 取值 | 说明 |
|------|------|------|------|------|
| `model` | string | — | 见上表 | 必填 |
| `prompt` | string | — | — | 提示词。图层拆分时可省略（不传则模型自动识别要拆的元素） |
| `image` | string / string[] | — | 图片 URL 或 `data:image/<格式>;base64,<编码>` | 参考图。生图场景建议不超过 10 张（超过时服务商目前不拒绝，但输出可能异常）；图层拆分仅支持 1 张 |
| `size` | string | `2K`（生图）/ `auto`（图层拆分） | 见下 | 两种写法不可混用 |
| `response_format` | string | `url` | `url`、`b64_json` | |
| `output_format` | string | — | `png`、`jpeg` | 图层拆分时只控制底图格式，**图层恒为 png** |
| `background` | string | `opaque` | `transparent`、`opaque` | 透明通道。仅图生图、且只支持输入 1 张带透明通道的图 |
| `watermark` | boolean | `true` | `true`、`false` | `true` 在右下角加「AI生成」水印；不传时默认加，不需要时传 `false` |
| `layer_decomposition` | boolean | `false` | `true`、`false` | **仅 pro**。开启后 `image` 变为必填且只能 1 张 |
| `optimize_prompt_options.mode` | string | `standard` | `standard`、`fast` | **仅 pro**。`fast` 更快、质量略低 |

**`size` 的两种写法**

方式 1，**指定档位（推荐）**：传 `1K` / `1.5K` / `2K`（pro）或 `2K` / `3K` / `4K`（lite），宽高比在 `prompt` 里用自然语言描述，由模型决定具体像素。pro 的档位映射（常见值，不限于此）：

| 档位 | 1:1 | 4:3 | 16:9 | 9:16 | 3:2 | 21:9 |
|------|-----|-----|------|------|-----|------|
| 1K | 1024×1024 | 1152×864 | 1424×800 | 800×1424 | 1248×832 | 1568×672 |
| 1.5K | 1536×1536 | 1792×1344 | 2048×1152 | 1152×2048 | 1872×1248 | 2352×1008 |
| 2K | 2048×2048 | 2368×1776 | 2816×1584 | 1584×2816 | 2496×1664 | 3136×1344 |

> [!tip]
> `1.5K` 与 `1K` **价格相同**，但出图效果更好。

方式 2，**指定宽高像素**（`宽x高`，如 `2048x1024`）：

- 总像素（宽 × 高）：pro 范围 **[921600, 4624220]**；lite 不少于 **3686400**（最小正方形是 `1920x1920`，常见的 `1024x1024` 会被拒绝）
- 宽高比（宽/高）范围 **[1/16, 16]**
- 两条必须同时满足。`512x512` 总像素 262144，低于两款的下限，无效

图层拆分场景只支持档位写法，默认 `auto`：输入图在 [921600, 4624220] 之间按原尺寸输出；小于 1K 按 1K；大于 2K 按 2K。

#### 交互编辑（仅 pro）

在 `prompt` 里用坐标标签指定编辑位置，坐标**归一化到 0~999**（左上角 0,0，右下角 999,999）：

- 点选：`<point>x y</point>`，由模型判断影响范围
- 框选：`<bbox>x1 y1 x2 y2</bbox>`，精确控制区域

换算：`x = round(x_px / 展示宽 * 1000)`，`y` 同理。

| 场景 | 提示词写法 |
|------|-----------|
| 编辑指定点附近的对象 | `把图1<point>520 460</point>位置换成皇冠` |
| 编辑指定区域的对象 | `把图1<bbox>120 180 640 760</bbox>区域替换成花园` |
| 跨图片编辑 | `将图1<bbox>179 283 796 986</bbox>的主体放到图2<bbox>118 331 933 871</bbox>位置` |

> [!tip]
> 框选区域内有多个主体时，在提示词里补一句说明要编辑哪个（「左侧的人」「戴帽子的猫」）。需要保持不变的物体也可以一并框出来并写明「保持不变」。

也可以不用坐标，直接在参考图上手绘草图 / 涂鸦 / 圈选，再用自然语言描述标记位置。

#### 图层拆分（仅 pro）

`layer_decomposition: true` 把单张图拆成 1 张底图和最多 16 个带透明通道的 PNG 图层。

```bash
curl https://api.lyrouter.com/v1/images/generations \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bytedance/doubao-seedream-5.0-pro",
    "image": "https://example.com/poster.png",
    "size": "2K",
    "layer_decomposition": true,
    "watermark": false
  }'
```

响应的 `data[]` 同时包含底图与图层：

| 字段 | 出现在 | 含义 |
|------|--------|------|
| `url` | 底图、图层 | 下载链接，**仅保留 24 小时** |
| `z_index` | 底图、图层 | 叠放顺序。底图固定 `0`，图层递增 |
| `bounding_box.absolute` | 图层 | `[left, top, right, bottom]`，输出底图坐标系中的绝对像素位置 |
| `bounding_box.normalized` | 图层 | 同上但归一化（0~1000），用于还原到任意尺寸画布 |
| `name`、`description` | 图层 | 图层名称与内容描述 |

还原到底图：`x = left`，`y = top`，`w = right - left`，`h = bottom - top`，按 `z_index` 从小到大叠放。
还原到 `W × H` 的画布：`x = left / 1000 × W`，其余同理（归一化坐标是整数，换算有取整误差）。

拆出的图层可以作为输入图再次送进模型单独编辑；想保留透明背景就同时传 `background: transparent` 和 `output_format: png`。

> [!note]
> 图层拆分每次请求**预扣 17 张的限流额度**（按最大输出张数预留），生成完成后按实际张数返还。

#### 输入图限制

| 约束 | 生图场景 | 图层拆分场景 |
|------|----------|--------------|
| 格式 | jpeg、png、webp、bmp、tiff、gif、heic、heif | png、jpeg |
| 总像素（宽×高） | [196, 36,000,000] | [262144, 36,000,000] |
| 宽高长度 | ≥ 14 px | — |
| 宽高比（宽/高） | [1/16, 16] | [1/16, 16] |
| 单张大小 | ≤ 30 MB | ≤ 30 MB |
| 张数 | 建议不超过 10 张（超过时服务商目前不拒绝，但输出可能异常） | 仅 1 张 |

base64 需遵循 `data:image/<格式>;base64,<编码>`，`<格式>` 必须小写。

#### 计费

按**张**计，分档维度是**分辨率档位**与**是否图层拆分**：

| 模型 | 分档 |
|------|------|
| `doubao-seedream-5.0-pro` | 1K / 1.5K（≤ 261 万像素）一档，2K 一档 |
| `doubao-seedream-5.0-pro` | 图层拆分时另有两档（同样按 1K/1.5K 与 2K 分） |
| `doubao-seedream-5.0-lite` | 不分档 |

`seedream-5.0-pro` 的**参考图**单独计一项：每次请求的第 1 张参考图不收费，超出部分按张计费。这里数的是输入的参考图，不是出图张数。图层拆分按实际产出的图片张数（底图 + 各图层）计费。

> [!tip]
> `1.5K` 与 `1K` **同档同价**，出图更好——没有特殊理由时优先用 `1.5K`。

---

### GPT-Image-2 / 2.5-flare / 2.5-sunburst

**透传**，参数与 OpenAI 图像 API 一致。

| 参数 | 类型 | 必填 | 取值 | 说明 |
|------|------|------|------|------|
| `model` | string | 是 | `openai/gpt-image-2`、`openai/gpt-image-2.5-flare`、`openai/gpt-image-2.5-sunburst` | |
| `prompt` | string | 是 | — | |
| `quality` | string | 见说明 | 逐模型不同，见下表 | `gpt-image-2` **必填**；建议所有 GPT-Image 模型都显式传入 |
| `size` | string | 否 | `宽x高` | 只接受 `宽x高`，不接受档位名；须满足下文四条硬约束，否则 400 |
| `n` | integer | 否 | 最大 10 | 出图张数 |
| `user` | string | 否 | — | 终端用户标识 |

**`quality` 逐模型取值**：

| 模型 | 取值 | 默认 |
|------|------|------|
| `gpt-image-2` | `low`、`medium`、`high`、`auto` | 无，**必填**：不传会间歇性返回 400（`INVALID_REQUEST_BODY`）；`auto` 实测等同 `low` |
| `gpt-image-2.5-flare`、`gpt-image-2.5-sunburst` | `low`、`medium`、`high`、`xhigh`、`max`、`auto` | `low`（`auto` 实测等同 `low`） |

`xhigh`、`max` 两档是否可用由上游账号能力决定，可能被拒；收到 `Invalid value: 'xhigh'` 时改用 `high`。

**`size` 的四条硬约束**（由服务商校验，任一不满足返回 400）：

| 约束 | 服务商报错原文 | 示例 |
|------|---------------|------|
| 宽和高都能被 16 整除 | `Invalid size '<宽>x<高>'. Width and height must both be divisible by 16.` | `1008x1008` ✓；`1254x1254`、`1312x1199` ✗ |
| 最长边 ≤ 3840 | `Invalid size '<宽>x<高>'. The longest edge must be less than or equal to 3840.` | `3840x2160` ✓；`4096x4096`、`1024x4096` ✗ |
| 总像素不少于约 100 万 | `Invalid size '<宽>x<高>'. Requested resolution is below the current minimum pixel budget.` | `1008x1008`（约 102 万）✓；`512x1024`（约 52 万）✗ |
| 总像素不超过约 830 万 | `Invalid size '<宽>x<高>'. Requested resolution exceeds the current pixel budget.` | `3840x2160`（约 829 万）✓；`3840x2176`（约 836 万）✗ |

GPT-Image 不接受档位名：传 `4K` 返回 `Invalid size '4K'. Expected WIDTHxHEIGHT, for example '1824x1024'.`

> [!warning]
> **生成接口上 `response_format` 会被丢弃。** 这个系列固定返回 base64，而上游对该参数的处理不稳定（实测同一模型、同一套参数，21 分钟内在 400 与 200 之间来回翻）。网关统一丢弃它并在 `X-Gateway-Unsupported-Parameters` 里说明，把「随机失败」换成「确定行为」。2.5 系列同时丢弃 `stream` / `stream_options`——传了上游会开启流式，导致请求失败（500）。
>
> **编辑接口（`/v1/images/edits`）上网关不丢弃这几个参数**，会转发给上游，请不要传：只传 `stream: true` 时网关目前还会自行补上 `stream_options`，上游返回 400 `Unknown parameter: 'stream_options'.`

> [!warning]
> **一次生成要几十秒。** 实测约 20~60 秒（最长一次约 83 秒，`gpt-image-2.5-flare`）；参数错误导致的 400 实测在 2 秒内返回。客户端超时仍请按分钟设置。

#### 计费

按 token 计：**输入文本**、**输入图片**、**输出**各有独立单价。**缓存命中**另有一项单价，但 2.5 系列的 `usage` 不含缓存字段，不产生这一项。

上游在 `usage.input_tokens_details` 里分开报文字与图片 token，网关按两个单价分别计费 —— 所以同样长度的提示词，带图片和不带图片的费用不同。

输出 token 主要由 `quality` 决定：`1024x1024` 时 2.5 系列 `low` 输出 196 token、`max` 输出 7024 token，最高档约为最低档的 36 倍。同名档位在两代模型上不是同一个量 —— 2.5 系列的 `high` 与 `gpt-image-2` 的 `medium` 输出 token 相同，跨模型比较请比 token，不要比档位名。

> [!note]
> **GPT-Image 不是像素越多越贵**（只适用于 GPT-Image）：输出 token 不随像素增加，例如 `1024x1536`（约 157 万像素）的输出 token 比 `1024x1024`（约 105 万像素）还少。

---

### Gemini 3 Pro Image / 3.1 Flash Image

这两个走**契约转换**——网关把统一参数翻译成 Gemini 原生的 `generateContent` 形状。因此**只有下表这些参数会被送出去**：

| 参数 | 是否生效 | 说明 |
|------|---------|------|
| `model` | ✓ | `google/gemini-3-pro-image`、`google/gemini-3.1-flash-image` |
| `prompt` | ✓ | 翻译成 `contents[].parts[].text`。必填，不写或为空返回 400 |
| `size` | ✓ | 见下，两种写法行为不同 |
| `image` | ✓ | 参考图 / 待编辑图，一张或多张，翻译成 `inlineData`。data URI 取其中声明的格式；裸 base64 按 PNG / JPEG / WEBP / GIF 的字节特征识别，认不出时返回 400 并指明第几张图。不接受图片 URL |
| `n` | **丢弃** | Gemini 的候选数参数属于文本生成，图像端点上是否生效没有证据。宁可明示丢弃，也不编一个「请求 200、设置静默不生效、账单照出」的映射 |
| `quality` | **丢弃** | Gemini 契约里没有这个概念 |
| `response_format` | **丢弃** | 只能返回 base64（`b64_json`） |
| `user` 及其他参数 | **丢弃** | — |

被丢弃的参数会出现在响应头 `X-Gateway-Unsupported-Parameters` 里；带 `X-Gateway-Strict-Parameters: true` 则直接 400。

#### `size`：档位名或宽高

| 写法 | 映射到 | 实际效果 |
|------|--------|---------|
| 档位名（`1K`、`2K`、`4K`） | `imageConfig.imageSize` | **只控制分辨率**，宽高比由上游默认。各档输出 token 见下表 |
| 宽高（`1376x768`） | `imageConfig.aspectRatio` + `imageConfig.imageSize` | 先匹配最接近的支持比例（误差 2% 以内），再按长边**向上**取分辨率档位，**比例与分辨率都生效** |
| 宽高比超出 2% 误差（如 `1000x700`） | 整个 `size` 不下发 | 比例和分辨率档都被丢弃，按上游默认的比例和分辨率出图。请求仍返回 200、仍出图、仍计费，唯一的信号是响应头 `X-Gateway-Unsupported-Parameters: size`；严格模式下返回 400 |

> [!warning]
> **不要使用 `512` 档。** `512` 档在生产环境被服务商拒绝（两个模型都是）。写宽高时长边不超过 704 会被网关映射到 `512` 档，同样可能被拒绝，请让长边大于 704。

写宽高时按长边向上取档，超过 4K 的按 4K。同一档位在两个模型上的输出 token 不同：

| 长边 | 档位 | `gemini-3-pro-image` 输出 token | `gemini-3.1-flash-image` 输出 token |
|------|------|------|------|
| ≤ 1376 | `1K` | 1120 | 1120 |
| ≤ 2816 | `2K` | 1120（2K 档不生效，与 1K 相同） | 1680 |
| 更大（封顶） | `4K` | 2000 | 2520 |

出图尺寸由服务商按比例档和分辨率档决定，不等于请求的宽高，例如 `gemini-3-pro-image` 请求 `4096x2304` 实际出图 `5504x3072`；请以返回图片的实际宽高为准。

例如 `1024x1024` 映射为 1:1 + `1K`，`2048x2048` 映射为 1:1 + `2K`。

> [!warning]
> 向上取档意味着长边略超过某一档时会落到下一档：`1500x1500` 取的是 `2K` 档，账单按实际输出的 token 收。想精确控制分辨率就直接传档位名。
>
> 比例偏差超过 2% 时整个 `size` 被丢弃、请求照常成功，生产环境建议带 `X-Gateway-Strict-Parameters: true`，让这种请求改为返回 400。要 16:9 + 4K 请写 `4096x2304`，不要照抄 `5632x3072`（比例 1.833，偏离 16:9 超过 2%，会被丢弃）。
>
> `size` 不接受比例字符串：`16:9` 会被当作档位名原样传给上游，上游不认识，返回 400（`upstream_rejected`）。要 16:9 请写该比例的宽高，如 `1376x768`。

支持的宽高比：`1:1`、`2:3`、`3:2`、`3:4`、`4:3`、`4:5`、`5:4`、`9:16`、`16:9`、`21:9`、`1:4`、`4:1`、`1:8`、`8:1`。传宽高时匹配误差 2% 以内的最接近比例，超出误差则比例和分辨率档都不下发，`size` 计入丢弃参数。

#### 计费

按 token 计：**输入**、**输出**各有独立单价；`gemini-3-pro-image` 另有**思考**一项（`usage.output_tokens_details.reasoning_tokens`，`usage.total_tokens` 包含它），`gemini-3.1-flash-image` 不产生思考 token。带参考图时，输入图片并入「输入」。

> [!note]
> **思考 token（Pro）单独计费且不可预测。** 只有 `gemini-3-pro-image` 产生思考 token。生产实测每次 115～316 个、中位约 220，由模型自行决定，不随输出分辨率档位或提示词长度变化；它约占单次费用的 1.5%，预算时可按每次约 220 个估算。

## 视频生成（异步任务）

视频生成耗时以分钟计，走**异步任务**：提交拿任务 ID，轮询到终态，再取产物。

```
POST /v1/videos                 提交任务
GET  /v1/videos/{id}            查询任务
GET  /v1/videos/{id}/content    取产物
POST /v1/videos/regenerations   视频升清（仅 MiniMax H3）
POST /v1/videos/context         上下文增强（仅 MiniMax H3）
```

### 三步流程

**第一步，提交**：

```bash
curl https://api.lyrouter.com/v1/videos \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bytedance/doubao-seedance-2.0-mini",
    "prompt": "一只柯基在草地上奔跑，阳光穿过树叶",
    "size": "1280x720",
    "seconds": 5
  }'
```

返回任务对象，`status` 为 `queued`：

```json
{ "id": "aigwt_…", "object": "video", "model": "…", "status": "queued", "created_at": 1760000000 }
```

**第二步，轮询**：`GET /v1/videos/{id}`，直到 `status` 变成 `completed` 或 `failed`。建议 10~30 秒一次。

**第三步，取产物**：`GET /v1/videos/{id}/content` 返回 302，跳转到产物地址下载。任务还没完成时返回 409，`error.code` 为 `not_ready`，继续轮询到 `completed` 再取。

> [!warning]
> 产物地址**有效期很短**（厂商侧通常 24 小时），请及时下载或转存。

### 视频通用参数（网关契约）

下面这些参数对**所有**视频模型通用，网关会翻译成各厂商的私有形状：

| 参数 | 类型 | 说明 |
|------|------|------|
| `model` | string | 模型名 |
| `prompt` | string | 提示词 |
| `seconds` | integer | 时长（秒），正整数 |
| `size` | string | `宽x高`（如 `1280x720`）或档位名（如 `720p`、`2K`） |
| `first_frame` | string | 首帧图片 |
| `last_frame` | string | 尾帧图片 |
| `reference_image` | string / string[] | 参考图 |
| `input_reference` | string / string[] | 参考**视频** |
| `reference_audio` | string / string[] | 参考音频（目前仅 MiniMax H3 支持，不会转发给 Seedance） |
| `generate_audio` | boolean | 生成有声视频。Seedance 不传时默认生成有声视频 |
| `watermark` | boolean | 加水印 |
| `output_format` | string | 输出容器格式 |
| `omni_reference_task_type` | string | 全模态任务类型（Seedance 使用；2.5 和 2.0 系列传入都会被受理） |
| `source_video` | string | 升清源视频（仅 `/v1/videos/regenerations`） |

不在这张表里的字段会被丢弃并写进 `X-Gateway-Unsupported-Parameters`；表里的字段若该模型不支持，会被**显式拒绝**并说明原因，不会悄悄忽略。JSON 请求体里目前已知的例外只有 `reference_audio`：传给 Seedance 时被静默丢弃，既不报错，也**不会**写进 `X-Gateway-Unsupported-Parameters`。用表单提交时的例外见下一段。

用 `multipart/form-data` 提交 `/v1/videos` 时（OpenAI 官方 SDK 的 `videos.create()` 就用这种格式），表单里能正常使用的只有 `model`、`prompt`、`seconds`、`size`、`input_reference`、`watermark`、`generate_audio`、`output_format`、`omni_reference_task_type`，其中 `input_reference` 只能填可公开访问的 `https://` 地址。目前表单里的 `input_reference` 如果是上传的文件，网关会忽略这个文件、按没有参考视频处理并照常计费，不会报错；只有写成非 URL 的文本值时才返回 400 `input_reference must be a URL; …`。`first_frame`、`last_frame`、`reference_image`、`reference_audio` 请用 JSON 请求体提交：目前写在表单里时，网关会把它们换成占位值转给服务商，例如 Seedance 带 `first_frame` 时返回 400 ``The parameter `content[1].image_url` specified in the request is not valid.``，MiniMax 带 `reference_audio` 时任务提交成功、随后异步失败（`content[1].audio_url: invalid param: audio url must be http(s):// or data:...;base64`）。

> [!note]
> 厂商文档里的 `duration` / `resolution` / `ratio` / `content` 对应网关的 `seconds` / `size` / 由 `size` 推导 / 由上面的参考字段组装。**请用网关的名字**，厂商原名不在契约里，会被丢弃。

---

### Seedance 2.5 / 2.0-fast / 2.0-mini（火山方舟）

#### 能力对比

| 能力 | `doubao-seedance-2.5` | `doubao-seedance-2.0-fast` | `doubao-seedance-2.0-mini` |
|------|----------------------|---------------------------|---------------------------|
| 文生视频 | ✓ | ✓ | ✓ |
| 首帧 / 首尾帧生视频 | ✓ | ✓ | ✓ |
| 图片参考 / 视频参考 | ✓ | ✓ | ✓ |
| **音频参考** | ✗ 网关目前不转发 | ✗ 同左 | ✗ 同左 |
| 编辑视频 / 延长视频 | ✓ | ✓ | ✓ |
| 生成有声视频 | ✓ | ✓ | ✓ |
| **参考素材上限** | **30 图 + 1 视频** | 9 图 + 1 视频 | 9 图 + 1 视频 |
| **输出分辨率** | 480p、720p、1080p(10bit) | 480p、720p | 480p、720p |
| 输出宽高比 | 21:9、16:9、4:3、1:1、3:4、9:16、自适应 | 同左 | 同左 |
| **输出时长** | **4~30 秒** | 4~15 秒 | 4~15 秒 |
| **输出格式** | mp4、**mov** | mp4 | mp4 |

- `reference_audio`（参考音频）目前不会转发给 Seedance：传了不报错，也不会出现在 `X-Gateway-Unsupported-Parameters` 中。需要参考音频时请改用 MiniMax H3。
- 参考视频 `input_reference` 每次只能传一个地址，写成数组返回 400。
- 服务商的联网搜索、返回尾帧等能力没有对应的网关参数，目前无法通过 LYRouter 使用。

> [!note]
> Seedance 2.5 的 1080p 采用 **10bit 位深 + H.265/HEVC** 编码，少数播放器不兼容；`mov` 采用 H.264 + yuv444p + PCM，建议用 VLC / mpv / ffplay / IINA 播放。

#### `size` 的两种写法

| 写法 | 网关怎么处理 |
|------|-------------|
| `宽x高`（如 `1280x720`） | 取**短边**作为分辨率档（→ `720p`），并按宽高比就近匹配到厂商支持的比例 |
| 档位名（如 `720p`、`1080p`） | 原样送出，宽高比由厂商按提示词自适应 |

#### 任务类型与特殊限制

Seedance 会根据输入素材和提示词**自动判断任务类型**，不同类型有不同的硬限制：

| 任务类型 | 怎么触发 | 限制 |
|---------|---------|------|
| 文生视频 | 只传 `prompt` | 无特殊限制 |
| 首帧 / 首尾帧 | 传 `first_frame`（+ `last_frame`） | 输出宽高比自动跟随首帧图片 |
| 参考生视频 | 传 `reference_image` / `input_reference` | 无特殊限制 |
| **视频编辑** | 传 `input_reference` + 提示词表达编辑意图 | **不要传 `seconds`**；输出宽高比与时长自动跟随源视频；源视频时长须 4~30 秒 |
| **视频延长** | 传 `input_reference` + 提示词表达延长意图 | 输出宽高比自动跟随源视频 |

> [!warning]
> **视频编辑任务不能指定时长。** 厂商要求该场景时长固定跟随源视频，网关的 `seconds` 只接受正整数，所以**编辑任务请直接不传 `seconds`**，让厂商用默认值。传了会被厂商拒绝（`InvalidParameter.TaskTypeConstraint`）。

用 `omni_reference_task_type` 显式声明任务类型，可以把厂商的**异步报错提前成同步报错**（提交时就失败，而不是任务跑到一半失败）：

| 值 | 含义 | 提示词需包含的关键词 |
|----|------|---------------------|
| `auto`（默认） | 由模型判断 | — |
| `reference` | 参考生视频 | — |
| `edit` | 视频编辑 | 编辑视频 / 增加 / 加上 / 删除 / 去掉 / 修改 / 替换 / 改成 |
| `extend` | 视频延长 | 向前延长 / 向后延长 / 延续 / 续写 |

服务商只在多模态参考场景接受 `edit`、`extend`：用在文生视频（只传 `prompt`）上会在提交时返回 400 `InvalidParameter.TaskTypeConstraint`。

声明的类型与模型实际判定的不一致时，可能异步失败（`InvalidParameter.TaskTypeMismatch`），也可能按声明的类型执行并计费：实测声明 `edit`、提示词不含编辑关键词时，任务按编辑完成。提示词里用 `@视频1`、`@图像1` 指代参考素材。

#### 输入素材限制（Seedance 2.5）

| | 限制 |
|---|---|
| **图片** | jpeg、png、webp、bmp、tiff、gif、heic、heif；宽高 [300, 6000] px；宽高比 [0.4, 2.5]；单张 ≤ 30 MB；首帧 1 张 / 首尾帧 2 张 / 参考图 1~30 张 |
| **视频** | mp4、mov（H.264 或 H.265，音频 AAC/MP3/PCM）；分辨率 480p 或 720p；时长 [2,30] 秒（编辑任务 [4,30]）；宽高 [300,6000] px；宽高比 [0.4,2.5]；单个 ≤ 200 MB；帧率 [24,60] |
| **请求体** | 总大小 ≤ 64 MB，大文件请用公网 URL，不要用 base64 |

> [!warning]
> Seedance **不接受含真人人脸的参考图 / 参考视频**，会被输入审核拦截。

#### 计费

按输出 token 计（任务对象中的 `usage.completion_tokens`），分档维度是**分辨率**与**有无视频输入**：

| 模型 | 分档 |
|------|------|
| `doubao-seedance-2.5` | 480p/720p 与 1080p 两档，每档再按有无视频输入分 |
| `doubao-seedance-2.0-fast` | 只按有无视频输入分两档 |
| `doubao-seedance-2.0-mini` | 同上 |

「有视频输入」指请求带了参考视频 `input_reference`（参考视频、视频编辑、视频延长）；只传首帧 / 首尾帧的图生视频不算。**有视频输入时按另一档单价计费，单价更低，但总价更高**：带参考视频时计量的 token 大约翻倍（见下文「等效秒」）。

> [!note]
> token 数 = 固定开销 + 每秒 token × 等效秒，已经完整体现了分辨率与时长的差异（实测 480p 4 秒 ≈ 40,594 token，8 秒 ≈ 80,770，720p 4 秒 ≈ 87,300），所以不需要再按时长分档。不带参考视频时，等效秒就是输出秒数。

**带参考视频时，计量的是「等效秒」，不是输出时长**：

| 输出时长 | 等效秒 |
|------|------|
| ≤ 6 秒 | 输出秒数 + 参考视频秒数 |
| > 6 秒 | 1.5 × 输出秒数 + 2，与参考视频时长无关 |

6 秒是拐点。例如参考视频 5 秒时，输出 5 秒按 10 秒计量，输出 10 秒按 17 秒计量。

---

### MiniMax H3 / H3-Max

> [!warning]
> `minimax/minimax-h3-max` **已被服务商下线，目前不可用，请改用 `minimax/minimax-h3`**。现在提交 H3-Max 任务会返回 HTTP 200，但任务对象是空的、没有任务 ID，无法轮询，也不扣费。本节保留 H3-Max 的差异说明，仅供对照。

#### 能力对比

| | `minimax/minimax-h3` | `minimax/minimax-h3-max`（已下线） |
|---|---|---|
| 定位 | 标准版 | **极速版** |
| 分辨率 | `480P`、`768P`、`2K` | `480P`、`768P`（**不支持 2K**） |
| 时长 | 4~15 秒 | 5~15 秒（**不支持 4 秒**） |
| 文生视频 / 图生视频（首尾帧） | ✓ | ✓ |
| 多模态参考（图 / 视频 / 音频） | ✓ | ✓，但**不支持多张参考图**（服务商会拒绝并建议改用 H3） |
| 视频升清 `/v1/videos/regenerations` | ✓ | ✗ |
| 上下文增强 `/v1/videos/context` | ✓ | ✗ |

#### `size`：短边必须正好落在档位上

| 写法 | 网关怎么处理 |
|------|-------------|
| 档位名（`480P`、`768P`、`2K`） | 原样送出 |
| `宽x高` | 取**短边**查档位表：短边必须**精确**是 `480` 或 `768`，否则网关直接拒绝并告诉你合法值；宽高比按就近匹配 |

> [!note]
> 网关在这里替你拒，而不是把一个拼出来的档位发给厂商——厂商只会回一句「ratio 非法」，看不出是尺寸引起的。

`size` 和 `seconds` 在 MiniMax 上都要显式传：服务商要求提供分辨率和时长，网关不补默认值。

`480P` 档实际产物的短边是 576：实测文生视频用 `480P`、多模态参考生视频用 `854x480`，产物都是 1038×576。

#### 三种输入场景与 `ratio` 规则

| 场景 | 怎么传 | 宽高比 |
|------|--------|--------|
| 文生视频 | 只传 `prompt` | **必须显式指定**，不能自适应。`size` 写 `宽x高` 时网关按就近比例推导；写档位名或不写时按 16:9 |
| 图生视频 | `first_frame`（+ `last_frame`） | 由输入图决定，传了也会被忽略。实测 1:1 的首尾帧加 `854x480` 产出 768×768 |
| 多模态参考生视频 | `reference_image` / `input_reference` / `reference_audio` | 可选，默认自适应 |

> [!warning]
> **图生视频与多模态参考互斥**：传了 `reference_*` 就不能再传 `first_frame` / `last_frame`，反之亦然。

#### 这个模型明确不支持的参数

以下参数在网关契约里存在（别的模型用得上），但 MiniMax 没有对应概念，传了会被**显式拒绝**并说明原因，不会悄悄忽略：

`generate_audio`（有声由参考音频承载）、`output_format`（没有容器格式选项）、`omni_reference_task_type`（没有全模态任务类型）、`source_video`（只属于升清接口）。

#### 输入素材限制

| | 限制 |
|---|---|
| **图片** | JPG、JPEG、PNG、WEBP、HEIC、HEIF；宽高 [256, 5760] px；宽高比 [0.4, 2.5]；≤ 30 MB；首帧 ≤ 1、尾帧 ≤ 1、参考图 ≤ 9 |
| **视频** | MP4、MOV（视频 H.264/H.265，音频 AAC/MP3）；≤ 50 MB；≤ 3 个；单段 [2,15] 秒且总时长 ≤ 15 秒；宽高 [256,5760] px；宽高比 [0.4,2.5]；帧率 [23.976, 60] |
| **音频** | WAV、MP3；≤ 15 MB；≤ 3 个；单段 [2,15] 秒且总时长 ≤ 15 秒 |
| **请求体** | 总大小 ≤ 64 MB，大文件请用公网 URL |

#### 视频升清（`/v1/videos/regenerations`）

把一条 **H3 768P** 产物重新生成为 2K。只支持 H3 自己产出的、符合 768P 规格的视频，不是通用的视频放大。

```bash
curl https://api.lyrouter.com/v1/videos/regenerations \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "生成那条 768P 时实际送入模型的最终提示词",
    "source_video": "https://example.com/h3-768p.mp4",
    "size": "2K"
  }'
```

源视频必须满足：含音轨、24 fps、宽和高都能被 32 整除、面积在 589,824 ~ 1,032,192 像素之间、总帧数 107~362（每档递增 17 帧，约 4~15 秒）。

> [!warning]
> **`prompt` 必须是生成 768P 时实际送入模型的最终提示词**，参考素材也要与当时一致。任何输入不一致都可能拿不到预期效果。如果当时用过上下文增强，要用增强**之后**的提示词。

#### 上下文增强（`/v1/videos/context`）

把多模态上下文理解成一段结构化的、语义更丰富的提示词。**这个接口只返回文本，不生成视频**。

```bash
curl https://api.lyrouter.com/v1/videos/context \
  -H "Authorization: Bearer sk-你的密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/minimax-h3",
    "prompt": "史诗级太空歌剧预告：女舰长独自站在观景窗前",
    "seconds": 5
  }'
```

同样是异步任务，轮询到终态后产物是**文本**（增强后的提示词），拿去喂给 `/v1/videos` 即可。这个接口的 `seconds` 是**必填**的（4~15）。

> [!warning]
> 目前网关不会拒绝用其他模型调用这个接口：用 Seedance 模型调用会生成一条普通视频并按视频计费，拿不到增强后的提示词。上下文增强请只用 `minimax/minimax-h3`。

#### 计费

视频按**秒**计，按**分辨率**分档：

| 模型 | 分档 |
|------|------|
| `minimax-h3` | 480P / 768P / 2K 三档；升清（`/videos/regenerations`）单独一档 |
| `minimax-h3-max`（已被服务商下线，目前不可用，请改用 `minimax-h3`） | 480P / 768P 两档 |

**参考图单独计一项**（两个模型的参考图单价不同）：`minimax-h3` 的参考图前 5 张免费，超出部分按张计费。

**上下文增强**（`/videos/context`）按 token 计，输入与输出两项分开 —— 它产出的是文本，不是视频。

> [!note]
> 计费秒数是**输入 + 输出**的总和（任务对象中的 `usage.duration_seconds`）。传了参考视频时，参考视频的秒数同样计入。升清（`/videos/regenerations`）例外：`duration_seconds` 只计源视频的秒数（源视频 4 秒计 4 秒）。

---

## 在代码里调用

前面每一节给的都是 `curl`，便于逐个参数对照。下面按任务分组，点开即可取用 —— 每组内切换语言。

所有接口共用同一个 base 与同一把密钥：

```text
base_url = https://api.lyrouter.com/v1
header   = Authorization: Bearer sk-你的密钥
```

> [!warning]
> **超时按分钟设。** 图像同步接口实测最长约 250 秒（Gemini 3 Pro），偶有超过 300 秒仍未返回；沿用文本接口的 30 秒会在成功返回之前就被客户端切断。视频一律走异步轮询。

**文生图**

Python:
```python
import httpx

BASE = "https://api.lyrouter.com/v1"
H = {"Authorization": f"Bearer {KEY}"}

r = httpx.post(f"{BASE}/images/generations", headers=H, timeout=300, json={
    "model": "bytedance/doubao-seedream-5.0-lite",
    "prompt": "一只戴墨镜的柴犬坐在冲浪板上，黄昏海面，电影感",
    "size": "2K",
})
r.raise_for_status()
# Seedream 默认返回图片地址（约 24 小时后失效），拿到就下载
url = r.json()["data"][0]["url"]
open("out.png", "wb").write(httpx.get(url, timeout=120).content)
```

Node:
```js
import { writeFileSync } from "node:fs";

const BASE = "https://api.lyrouter.com/v1";
const H = { Authorization: `Bearer ${process.env.LYROUTER_KEY}` };

const r = await fetch(`${BASE}/images/generations`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "bytedance/doubao-seedream-5.0-lite",
    prompt: "一只戴墨镜的柴犬坐在冲浪板上",
    size: "2K",
  }),
  signal: AbortSignal.timeout(300_000),
});
const { data } = await r.json();
// Seedream 默认返回图片地址（约 24 小时后失效），拿到就下载
const img = await fetch(data[0].url);
writeFileSync("out.png", Buffer.from(await img.arrayBuffer()));
```

图像编辑有两种请求体，选哪种取决于模型（支持矩阵见上面「图像生成与编辑」一节）：

**图像编辑**

Python · multipart:
文件上传形态，五个模型都可用（Gemini 系自 2026-10-09 起也支持）。

```python
r = httpx.post(f"{BASE}/images/edits", headers=H, timeout=300,
    files={"image": open("cat.png", "rb")},
    data={"model": "openai/gpt-image-2",
          "prompt": "把背景换成雪山",
          "quality": "medium"})
```

Python · JSON:
图片转 base64 放进请求体。五个模型都可用。

```python
import base64

b64 = base64.b64encode(open("cat.png", "rb").read()).decode()
r = httpx.post(f"{BASE}/images/edits", headers=H, timeout=300, json={
    "model": "google/gemini-3-pro-image",
    "prompt": "把背景换成雪山",
    # 单张传字符串；多张参考图传数组
    "image": f"data:image/png;base64,{b64}",
})
```

Node · JSON:
```js
import { readFileSync } from "node:fs";

const b64 = readFileSync("cat.png").toString("base64");
const r = await fetch(`${BASE}/images/edits`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "google/gemini-3-pro-image",
    prompt: "把背景换成雪山",
    image: `data:image/png;base64,${b64}`,
  }),
  signal: AbortSignal.timeout(300_000),
});
```

**视觉理解（读本地图片）**

Python:
```python
import base64, mimetypes

p = "cat.jpg"
mime = mimetypes.guess_type(p)[0] or "image/jpeg"
b64 = base64.b64encode(open(p, "rb").read()).decode()

r = httpx.post(f"{BASE}/chat/completions", headers=H, timeout=120, json={
    "model": "<带「视觉」能力标签的模型>",
    "messages": [{"role": "user", "content": [
        {"type": "text", "text": "这张图里有什么？"},
        {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}},
    ]}],
})
print(r.json()["choices"][0]["message"]["content"])
```

Node:
```js
import { readFileSync } from "node:fs";

const b64 = readFileSync("cat.jpg").toString("base64");
const r = await fetch(`${BASE}/chat/completions`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "<带「视觉」能力标签的模型>",
    messages: [{ role: "user", content: [
      { type: "text", text: "这张图里有什么？" },
      { type: "image_url", image_url: { url: `data:image/jpeg;base64,${b64}` } },
    ]}],
  }),
  signal: AbortSignal.timeout(120_000),
});
const j = await r.json();
console.log(j.choices[0].message.content);
```

**视频：提交 → 轮询 → 下载**

Python:
```python
import time, httpx

# 1 提交
r = httpx.post(f"{BASE}/videos", headers=H, timeout=120, json={
    "model": "bytedance/doubao-seedance-2.0-mini",
    "prompt": "一只柯基在草地上奔跑，阳光穿过树叶",
    "size": "1280x720",
    "seconds": 5,
})
r.raise_for_status()
task_id = r.json()["id"]

# 2 轮询到终态。10~30 秒一次即可，不要更密。
while True:
    time.sleep(15)
    task = httpx.get(f"{BASE}/videos/{task_id}", headers=H, timeout=60).json()
    print(task["status"])
    if task["status"] in ("completed", "failed"):
        break

if task["status"] == "failed":
    raise SystemExit(task)      # 失败原因在任务对象里

# 3 取产物。默认 302 到产物地址，所以要跟随重定向；
#   产物地址有效期通常 24 小时，拿到就存。
v = httpx.get(f"{BASE}/videos/{task_id}/content", headers=H,
              timeout=600, follow_redirects=True)
open("out.mp4", "wb").write(v.content)
```

Node:
```js
import { writeFileSync } from "node:fs";

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// 1 提交
const sub = await fetch(`${BASE}/videos`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "bytedance/doubao-seedance-2.0-mini",
    prompt: "一只柯基在草地上奔跑，阳光穿过树叶",
    size: "1280x720",
    seconds: 5,
  }),
});
const created = await sub.json();
// 提交失败时没有任务 ID，不要继续轮询
if (!sub.ok || !created.id) throw new Error(JSON.stringify(created));
const { id } = created;

// 2 轮询到终态
let task;
for (;;) {
  await sleep(15_000);
  task = await (await fetch(`${BASE}/videos/${id}`, { headers: H })).json();
  console.log(task.status);
  if (task.status === "completed" || task.status === "failed") break;
}
if (task.status === "failed") throw new Error(JSON.stringify(task));

// 3 取产物（fetch 默认跟随重定向）
const v = await fetch(`${BASE}/videos/${id}/content`, { headers: H });
writeFileSync("out.mp4", Buffer.from(await v.arrayBuffer()));
```

### 用 OpenAI 官方 SDK

对外契约是 OpenAI 形状，所以官方 SDK 大部分能直接用 —— 把 `base_url` 指到网关即可：

```python
from openai import OpenAI
client = OpenAI(api_key=KEY, base_url="https://api.lyrouter.com/v1", timeout=300)
```

| SDK 方法 | 能不能用 |
|---|---|
| `client.chat.completions.create(...)` | ✅ 文本与视觉理解都可以 |
| `client.images.generate(...)` | ✅ 全部图像模型 |
| `client.images.edit(...)` | ✅ 全部图像模型（含 Gemini，2026-10-09 起） |
| `client.videos.create(...)` | ✅ 需要较新版本的 openai Python SDK（实测 1.58.1 没有 `client.videos`，2.48.0 可用）。SDK 以 multipart 提交，网关会转换成上游需要的格式；URL 形式的 `input_reference` 要写成 `extra_body={"input_reference": url}`，直接传字符串会在客户端报 `RuntimeError: Expected entry at input_reference to be bytes, an io.IOBase instance, PathLike or a tuple…`；`first_frame`、`last_frame`、`reference_image`、`reference_audio` 请用 JSON 提交，写在表单里目前会出错（见「视频通用参数」）；查询与下载用上面的 HTTP 写法 |

> [!note]
> `images.edit()` 无条件发 `multipart/form-data`。Gemini 的上游只认 JSON，但网关会替你把 multipart 归一并翻译过去，所以**现在用它驱动 Gemini 也可以**（2026-10-09 起）。此前这条路会失败。

要发网关的扩展参数、或想完全控制请求体时，SDK 自带调任意端点的逃生舱：

**SDK 逃生舱：用 client.post 发任意请求体**

Python:
```python
import httpx

resp = client.post(
    "/images/edits",
    cast_to=httpx.Response,          # 拿原始响应，不走 SDK 的模型解析
    body={"model": "google/gemini-3-pro-image",
          "prompt": "把背景换成雪山",
          "image": f"data:image/png;base64,{b64}"},
)
img_b64 = resp.json()["data"][0]["b64_json"]
```
## 常见错误

### 返回 400 `unsupported_parameters`，应该如何处理？

**答：** 请求启用了 `X-Gateway-Strict-Parameters: true`，且含有不受支持的参数。按错误信息列出的参数名删除参数，或改用接口契约中的名称。

### 请求成功，但响应头有 `X-Gateway-Unsupported-Parameters`，是什么意思？

**答：** 有参数被丢弃，但请求仍按剩余参数执行。检查这些参数是否必要；需要时改用契约中的名称，不要把请求成功当作全部参数已经生效。

### GPT-Image 返回 400 `INVALID_REQUEST_BODY`，应该检查什么？

**答：** 检查是否传入 `quality`。`gpt-image-2` 必须传 `quality`（`low`、`medium`、`high` 或 `auto`），不传会间歇性返回 400。其他 GPT-Image 模型也建议显式传入。结合具体错误核对请求正文。

### GPT-Image 提示宽高必须能被 16 整除，怎么办？

**答：** `Invalid size '<宽>x<高>'. Width and height must both be divisible by 16.` 表示尺寸不符合约束。将宽和高都设置为 16 的倍数，再按模型支持的尺寸规则调用。

### GPT-Image 提示 `longest edge`、`pixel budget` 或 `Invalid size '4K'`，怎么办？

**答：** `size` 违反了其余几条硬约束：最长边不能超过 3840（`Invalid size '<宽>x<高>'. The longest edge must be less than or equal to 3840.`），总像素要在约 100 万到约 830 万之间（`Invalid size '<宽>x<高>'. Requested resolution is below the current minimum pixel budget.` / `Invalid size '<宽>x<高>'. Requested resolution exceeds the current pixel budget.`），并且只能写 `宽x高`，不能写档位名（`Invalid size '4K'. Expected WIDTHxHEIGHT, for example '1824x1024'.`）。对照 GPT-Image 小节的 `size` 约束表调整尺寸，例如要接近 4K 的 16:9 图写 `3840x2160`。

### GPT-Image 提示 `Invalid value: 'xhigh'`，怎么办？

**答：** `xhigh`、`max` 只有 2.5 系列支持，而且是否可用由上游账号能力决定，可能被拒。改用 `high`。

### Gemini 返回 200，但图片比例或分辨率不是请求的，怎么办？

**答：** 先看响应头 `X-Gateway-Unsupported-Parameters` 是否含 `size`：宽高比与支持比例的偏差超过 2% 时，整个 `size`（比例和分辨率档）都被丢弃，请求按服务商默认值出图并照常计费。改写成与支持比例精确一致的宽高，例如 16:9 + 4K 写 `4096x2304`。如果没有被丢弃，再核对档位：`gemini-3-pro-image` 的 `2K` 档不生效（输出 token 与 `1K` 相同）；出图尺寸本身也不一定等于请求的宽高，以返回图片的实际宽高为准。生产环境建议带 `X-Gateway-Strict-Parameters: true`，让被丢弃的 `size` 直接返回 400。

### Seedream 提示 `image size must be at least 3686400 pixels`，怎么办？

**答：** 请求低于 Seedream lite 的最小像素约束（3686400，最小正方形是 `1920x1920`）。使用 `2K` 档位或更大的宽高，并核对该模型的尺寸规则。

### MiniMax 提示短边必须是 480 或 768，怎么办？

**答：** 模型档位是离散集合。使用档位名（如 `768P`、`2K`），或使用短边精确为 480 / 768 的尺寸，并核对所选任务的支持范围。

### 任务失败并返回 `InvalidParameter.TaskTypeConstraint`，怎么办？

**答：** Seedance 的任务类型与参数冲突。对照本页“任务类型与特殊限制”检查参数；编辑任务不要传 `seconds`，文生视频不要把 `omni_reference_task_type` 设为 `edit` 或 `extend`。

### 任务失败并返回 `InvalidParameter.TaskTypeMismatch`，怎么办？

**答：** 显式声明的 `omni_reference_task_type` 与模型判定不一致。按本页任务类型表中的关键词写提示词，或改用 `auto`。

### 多模态调用出现 504 或客户端超时，应该怎样检查？

**答：** 先确认客户端是否沿用了文本接口的 30 秒超时。图像请求按分钟级设置等待时间，视频使用异步任务轮询；查询超时不等于任务失败，避免立即重复提交。

### 产物地址打不开（返回 403 或 404），怎么办？

**答：** 检查链接是否过期：服务商签发的产物地址通常 24 小时后失效，过期后访问返回 403 或 404。及时下载或转存产物。视频的产物地址在任务完成时写入一次，重新查询任务拿到的仍是同一个地址；链接过期后需要重新提交任务。

### 多模态错误正文是什么结构？

**答：** 错误响应使用 OpenAI 形状，读取 `error.message`、`error.type` 与 `error.code`，并结合 HTTP 状态和请求日志定位：

```json
{ "error": { "message": "…", "type": "invalid_request_error", "code": "unsupported_parameters" } }
```

## API 规格与代码生成

多模态接口的完整规格可直接下载，喂给代码生成器就能得到你所用语言的客户端：

这两个文件由**控制台**提供（就是你正在看这篇文档的站点），不在网关地址上 —— 下面示例里的 `<your-console-host>` 因此与别处的 `api.lyrouter.com` 不是同一个域名。

| 文件 | 用途 |
|------|------|
| [OpenAPI 规格（YAML）](https://platform.lyrouter.com/ai_gateway/api-spec/multimodal-openapi.yaml) | 机器读；可直接用于 openapi-generator、openapi-typescript 等工具 |
| [接口参考（Markdown）](https://platform.lyrouter.com/ai_gateway/api-spec/multimodal-api-reference.md) | 人读的完整参考，含每个参数的取值范围与计费口径 |

生成 TypeScript 客户端：

```bash
npx openapi-typescript https://<your-console-host>/ai_gateway/api-spec/multimodal-openapi.yaml \
  -o ./src/gateway-api.d.ts
```

生成 Python 客户端：

```bash
openapi-generator-cli generate \
  -i https://<your-console-host>/ai_gateway/api-spec/multimodal-openapi.yaml \
  -g python -o ./gateway-client
```

> [!note]
> 对外契约是 **OpenAI 形状**：产物固定在顶层 `data[]` 数组里，`usage` 固定在顶层。可以直接用 OpenAI 官方 SDK 调用图像接口，创建视频任务也可以用 SDK 的 `videos.create()`；查询和下载视频建议用 HTTP 直调。

## 接下来

- [调用网关](https://platform.lyrouter.com/ai_gateway/docs/call-gateway)：文本对话、向量、通用请求格式
- [查看日志](https://platform.lyrouter.com/ai_gateway/docs/logs)：逐次调用的用量与费用
- [用量分析](https://platform.lyrouter.com/ai_gateway/docs/analytics)：按模型 / 工作空间的费用视角
