diff --git a/docs/协议文档/OpenAI/OpenAI图像生成文档.md b/docs/协议文档/OpenAI/OpenAI图像生成文档.md
index 4ff06e9..45837dd 100644
--- a/docs/协议文档/OpenAI/OpenAI图像生成文档.md
+++ b/docs/协议文档/OpenAI/OpenAI图像生成文档.md
@@ -28,8 +28,8 @@ OpenAI 图像 API 提供三个端点,支持 DALL-E 系列和 GPT Image 系列
| 端点 | 方法 | 路径 | 功能 | 支持模型 |
|------|------|------|------|----------|
-| 图像生成 | POST | `/v1/images/generations` | 根据文本提示生成图像 | `dall-e-2`, `dall-e-3`, `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5` |
-| 图像编辑 | POST | `/v1/images/edits` | 编辑现有图像(支持多图) | `dall-e-2`, `gpt-image-1` |
+| 图像生成 | POST | `/v1/images/generations` | 根据文本提示生成图像 | `dall-e-2`, `dall-e-3`, `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2` |
+| 图像编辑 | POST | `/v1/images/edits` | 编辑现有图像(支持多图) | `dall-e-2`, `gpt-image-1`, `gpt-image-2` |
| 图像变体 | POST | `/v1/images/variations` | 基于现有图像生成变体 | `dall-e-2` |
### 基本请求示例(图像生成)
@@ -85,12 +85,12 @@ Authorization: Bearer sk-xxxxxxxxxxxxxxxx
| 参数名 | 类型 | 必填 | 默认值 | 适用模型 | 说明 |
|--------|------|------|--------|----------|------|
| `prompt` | string | **是** | — | 全部 | 图像描述文本。GPT Image 最长 32000 字符,dall-e-2 最长 1000 字符,dall-e-3 最长 4000 字符 |
-| `model` | string | **是** | `dall-e-2` | — | 模型 ID:`dall-e-2`、`dall-e-3`、`gpt-image-1`、`gpt-image-1-mini`、`gpt-image-1.5` |
+| `model` | string | **是** | `dall-e-2` | — | 模型 ID:`dall-e-2`、`dall-e-3`、`gpt-image-1`、`gpt-image-1-mini`、`gpt-image-1.5`、`gpt-image-2` |
| `n` | integer | 否 | `1` | 全部 | 生成图像数量。dall-e-2/dall-e-3: 1;GPT Image: 1-10 |
| `size` | string | 否 | `1024x1024` | 全部 | 图像尺寸,各模型支持值不同,详见下方 |
| `quality` | string | 否 | `auto` | GPT Image; dall-e-3 | 图像质量。GPT Image: `auto`/`high`/`medium`/`low`;dall-e-3: `standard`/`hd` |
| `response_format` | string | 否 | `url` | 全部 | 返回格式:`url` 或 `b64_json`。GPT Image 默认 `b64_json` |
-| `background` | string | 否 | `auto` | GPT Image | 背景透明度:`transparent`、`opaque`、`auto`。仅支持 PNG/WebP 输出格式 |
+| `background` | string | 否 | `auto` | GPT Image | 背景透明度:`transparent`、`opaque`、`auto`。仅支持 PNG/WebP 输出格式。**gpt-image-2 不支持 `transparent`** |
| `output_format` | string | 否 | `png` | GPT Image | 输出图像格式:`png`、`jpeg`、`webp` |
| `output_compression` | integer | 否 | — | GPT Image | 输出压缩率(0-100),仅 `jpeg` 和 `webp` 格式有效。100 = 无压缩 |
| `partial_images` | integer | 否 | `0` | GPT Image | 流式中间图数量(0-3)。设为非 0 时自动启用流式模式 |
@@ -125,6 +125,27 @@ Authorization: Bearer sk-xxxxxxxxxxxxxxxx
| `1536x1024`(横向) |
| `1024x1536`(纵向) |
+**gpt-image-2**(灵活分辨率):
+
+支持任意满足以下约束的尺寸:
+- 最大边长 ≤ 3840px
+- 两边均为 16 的倍数
+- 长短边比例 ≤ 3:1
+- 总像素数 655,360 ~ 8,294,400
+
+常用尺寸:
+
+| 尺寸 | 说明 |
+|------|------|
+| `auto` | 默认,模型自动选择 |
+| `1024x1024` | 正方形 |
+| `1536x1024` | 横向 |
+| `1024x1536` | 纵向 |
+| `2048x2048` | 2K 正方形 |
+| `2048x1152` | 2K 横向 |
+| `3840x2160` | 4K 横向 |
+| `2160x3840` | 4K 纵向 |
+
### 3.3 请求示例
**DALL-E 3 高质量生成**:
@@ -364,7 +385,7 @@ GPT Image 模型在响应中包含额外的元数据字段:
## 7. 流式响应 (GPT Image 模型)
-仅 GPT Image 模型(`gpt-image-1`、`gpt-image-1-mini`、`gpt-image-1.5`)支持流式响应。通过设置 `stream: true` 或 `partial_images > 0` 启用。
+仅 GPT Image 模型(`gpt-image-1`、`gpt-image-1-mini`、`gpt-image-1.5`、`gpt-image-2`)支持流式响应。通过设置 `stream: true` 或 `partial_images > 0` 启用。
**Content-Type**:`text/event-stream`
@@ -414,23 +435,23 @@ data: {"type":"generation.completed","data":[{"b64_json":"iVBORw0KGgo...final","
## 8. 各模型参数差异对比
-| 参数 | dall-e-2 | dall-e-3 | gpt-image-1 / mini / 1.5 |
-|------|----------|----------|--------------------------|
-| **生成端点** | 支持 | 支持 | 支持 |
-| **编辑端点** | 支持(mask) | 不支持 | 支持(多图,无 mask) |
-| **变体端点** | 支持 | 不支持 | 不支持 |
-| **prompt 最大长度** | 1000 字符 | 4000 字符 | 32000 字符 |
-| **n(生成数量)** | 1 | 1 | 1-10 |
-| **尺寸** | 256/512/1024 正方形 | 1024 正方形 + 横纵 1792 | 1024 正方形 + 1536 横纵 |
-| **quality** | 不支持 | `standard`/`hd` | `auto`/`high`/`medium`/`low` |
-| **style** | 不支持 | `vivid`/`natural` | 不支持 |
-| **background** | 不支持 | 不支持 | `transparent`/`opaque`/`auto` |
-| **output_format** | 不支持 | 不支持 | `png`/`jpeg`/`webp` |
-| **output_compression** | 不支持 | 不支持 | 0-100 |
-| **流式响应** | 不支持 | 不支持 | 支持(`partial_images`) |
-| **moderation** | 不支持 | 不支持 | `auto`/`low` |
-| **response_format 默认值** | `url` | `url` | `b64_json` |
-| **Usage 统计** | 不返回 | 不返回 | 返回 |
+| 参数 | dall-e-2 | dall-e-3 | gpt-image-1 / mini / 1.5 | gpt-image-2 |
+|------|----------|----------|--------------------------|-------------|
+| **生成端点** | 支持 | 支持 | 支持 | 支持 |
+| **编辑端点** | 支持(mask) | 不支持 | 支持(多图,无 mask) | 支持(多图,无 mask) |
+| **变体端点** | 支持 | 不支持 | 不支持 | 不支持 |
+| **prompt 最大长度** | 1000 字符 | 4000 字符 | 32000 字符 | 32000 字符 |
+| **n(生成数量)** | 1 | 1 | 1-10 | 1-10 |
+| **尺寸** | 256/512/1024 正方形 | 1024 正方形 + 横纵 1792 | 1024 正方形 + 1536 横纵 | 灵活分辨率(最大边 ≤ 3840px,16 倍数,比例 ≤ 3:1) |
+| **quality** | 不支持 | `standard`/`hd` | `auto`/`high`/`medium`/`low` | `auto`/`high`/`medium`/`low` |
+| **style** | 不支持 | `vivid`/`natural` | 不支持 | 不支持 |
+| **background** | 不支持 | 不支持 | `transparent`/`opaque`/`auto` | `opaque`/`auto`(**不支持 transparent**) |
+| **output_format** | 不支持 | 不支持 | `png`/`jpeg`/`webp` | `png`/`jpeg`/`webp` |
+| **output_compression** | 不支持 | 不支持 | 0-100 | 0-100 |
+| **流式响应** | 不支持 | 不支持 | 支持(`partial_images`) | 支持(`partial_images`) |
+| **moderation** | 不支持 | 不支持 | `auto`/`low` | `auto`/`low` |
+| **response_format 默认值** | `url` | `url` | `b64_json` | `b64_json` |
+| **Usage 统计** | 不返回 | 不返回 | 返回 | 返回 |
---
@@ -549,17 +570,17 @@ type ImageData struct {
### 11.3 待完善项
-1. **GPT Image 新参数未覆盖**:当前 `ImageRequest` 缺少 `background`、`output_format`、`output_compression`、`partial_images`、`stream`、`moderation` 字段
-2. **GPT Image 增强响应未覆盖**:当前 `ImageResponse` 缺少 `background`、`output_format`、`size`、`quality`、`usage` 字段
+1. ~~**GPT Image 新参数未覆盖**~~:已覆盖(`ImageRequest` 已包含 `partial_images`、`stream` 等字段)
+2. ~~**GPT Image 增强响应未覆盖**~~:已覆盖(`ImageResponse` 已包含 `background`、`output_format`、`size`、`quality`、`usage` 字段)
3. **图像编辑端点未注册**:`/v1/images/edits` 路径尚未在 `Path2RelayMode` 中注册
4. **图像变体端点未注册**:`/v1/images/variations` 路径尚未在 `Path2RelayMode` 中注册
-5. **流式图像响应未实现**:GPT Image 的 SSE 流式响应需要专门的流处理器
-6. **Usage 统计缺失**:当前 `handleImageResponse` 返回空的 `Usage{}`,GPT Image 模型应返回实际的 token 用量
+5. ~~**流式图像响应未实现**~~:已实现(`handleImageStreamResponse` 支持 SSE 流式)
+6. ~~**Usage 统计缺失**~~:已实现(`handleImageResponse` 从 GPT Image 响应中提取 `usage`)
7. **计费缺失**:DALL-E 和 GPT Image 的定价模型不同(DALL-E 按次计费,GPT Image 按 token 计费),需要区分
-### 11.4 DTO 扩展建议
+### 11.4 DTO 扩展(已实现)
-为支持 GPT Image 的完整功能,建议扩展 DTO:
+以下 DTO 扩展已在 `relay/dto/usage.go` 中实现:
```go
// ImageRequest 图像生成请求(扩展版)
@@ -633,5 +654,6 @@ case strings.HasSuffix(path, "/images/variations"):
| `gpt-image-1` | 按 token 计费 | 使用 `usage` 字段中的 token 数计费 |
| `gpt-image-1-mini` | 按 token 计费 | 价格低于 gpt-image-1 |
| `gpt-image-1.5` | 按 token 计费 | 价格低于 gpt-image-1 |
+| `gpt-image-2` | 按 token 计费 | text input $5/M, image input $8/M, text output $10/M, image output $30/M tokens |
-Relay 层的 `BillingProvider` 需要根据模型类型选择不同的计费策略:DALL-E 模型使用固定价格计费,GPT Image 模型使用 token 计费。当前 `handleImageResponse` 返回空 Usage,需要区分处理。
+Relay 层的 `BillingProvider` 需要根据模型类型选择不同的计费策略:DALL-E 模型使用固定价格计费,GPT Image 模型使用 token 计费。`handleImageResponse` 已支持从 GPT Image 响应中提取 `usage` 字段用于计费。
diff --git a/docs/协议文档/豆包系列/seedance_2.0_API_创建视频.md b/docs/协议文档/豆包系列/seedance_2.0_API_创建视频.md
new file mode 100644
index 0000000..646bb46
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedance_2.0_API_创建视频.md
@@ -0,0 +1,645 @@
+`POST https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks` [ ](https://api.volcengine.com/api-explorer/?action=CreateContentsGenerationsTasks&data=%7B%7D&groupName=%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90API&query=%7B%7D&serviceCode=ark&version=2024-01-01)[运行](https://api.volcengine.com/api-explorer/?action=CreateContentsGenerationsTasks&data=%7B%7D&groupName=%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90API&query=%7B%7D&serviceCode=ark&version=2024-01-01)
+本文介绍创建视频生成任务 API 的输入输出参数,供您使用接口时查阅字段含义。模型会依据传入的图片及文本信息生成视频,待生成完成后,您可以按条件查询任务并获取生成的视频。
+:::tip
+请确保您的账户余额大于等于 200 元([前往充值](https://console.volcengine.com/finance/fund/recharge)),或已[购买资源包](https://console.volcengine.com/common-buy/fast/ark_bd%7C%7Cd682ppeeq1mp7kd5q0e0),否则无法开通 Seedance 2.0 及 Seedance 2.0 fast 模型。
+
+:::
+**模型能力==^new^==**
+
+* **Doubao Seedance 2.0 系列==^new^==** ** (有声视频/无声视频)**
+ * **多模态参考生视频==^new^==**:输入++参考图片(0~9)+参考视频(0~3)+ 参考音频(0~3)+ 文本提示词(可选)++ 生成 1 个目标视频。注意不可单独输入音频,应至少包含 1 个参考视频或图片。支持生成全新视频、编辑视频、延长视频,[阅读教程](https://www.volcengine.com/docs/82379/2291680) 获取详细代码示例。
+ * **图生视频\-首尾帧**:输入++首帧图片+尾帧图片+文本提示词(可选)++ 生成 1 个目标视频。
+ * **图生视频\-首帧**:输入++首帧图片+文本提示词(可选)++ 生成 1 个目标视频。
+ * **文生视频**:输入++文本提示词++生成 1 个目标视频。
+* **Doubao Seedance 1.5 pro (有声视频/无声视频)**
+ 【图生视频\-首尾帧】【图生视频\-首帧】【文生视频】
+* **Doubao Seedance 1.0 pro**
+ 【图生视频\-首尾帧】【图生视频\-首帧】【文生视频】
+* **Doubao Seedance 1.0 pro fast**
+ 【图生视频\-首帧】【文生视频】
+
+
+Tips:一键展开折叠,快速检索内容
+打开页面右上角开关,**ctrl ** + **f** 可检索页面内所有内容。
+
+
+
+```mixin-react
+return (
+去调试
+
+`}>
+
+ [模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#2705b333)  [模型计费](https://www.volcengine.com/docs/82379/1544106?redirect=1&lang=zh#02affcb8)  [API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
+  [调用教程](https://www.volcengine.com/docs/82379/1366799)  [接口文档](https://www.volcengine.com/docs/82379/1520758)  [常见问题](https://www.volcengine.com/docs/82379/1359411)  [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
+`}>);
+```
+
+
+---
+
+
+
+## 请求参数
+> 跳转 [响应参数](#y2hhTyHB)
+
+
+### 请求体
+
+---
+
+
+**model** `string` %%require%%
+您需要调用的模型的 ID (Model ID),[开通模型服务](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false),并[查询 Model ID](https://www.volcengine.com/docs/82379/1330310) 。
+您也可通过 Endpoint ID 来调用模型,获得限流、计费类型(前付费/后付费)、运行状态查询、监控、安全等高级能力,可参考[获取 Endpoint ID](https://www.volcengine.com/docs/82379/1099522)。
+
+---
+
+
+**content** `object[]` %%require%%
+输入给模型,生成视频的信息,支持文本、图片、音频、视频、样片任务 ID。
+:::warning
+Seedance 2.0 系列模型不支持直接上传含有真人人脸的参考图/视频。为了便利创作者对肖像的使用,平台推出了以下解决方案,详情参见 [教程](https://www.volcengine.com/docs/82379/2291680?lang=zh#5c67c9a1)。
+
+* 支持使用部分模型的含人脸原始产物作为输入素材
+* 支持使用预置虚拟人像作为输入素材
+* 支持使用已授权真人素材作为输入
+
+:::
+支持以下几种组合:
+
+* **文本**
+* **文本(可选)+ 图片**
+* **文本(可选)+ 视频**
+* **文本(可选)+ 图片 + 音频**
+* **文本(可选)+ 图片 + 视频**
+* **文本(可选)+ 视频 + 音频**
+* **文本(可选)+ 图片 + 视频 + 音频**
+* **样片任务 ID**:样片指使用 Seedance 模型成功生成的样片视频,模型可基于样片生成高质量正式视频。
+
+
+信息类型
+
+---
+
+
+**文本信息** `object`
+输入给模型的提示词信息。
+
+属性
+
+---
+
+
+content.**type ** `string` %%require%%
+输入内容的类型,此处应为 `text`。
+
+---
+
+
+content.**text ** `string` %%require%%
+输入给模型的文本提示词,描述期望生成的视频。
+:::tip
+
+* 提示词语言支持:所有模型均支持中英文提示词;seedance 2.0 及 seedance 2.0 fast 额外支持日语、印尼语、西班牙语、葡萄牙语。
+* 提示词字数建议:中文提示词不超过500字,英文提示词不超过1000词。字数过多易导致信息分散,模型可能忽略细节、仅关注重点,进而造成视频缺失部分元素。
+* 更多使用技巧:提示词的详细使用技巧,请参见 [seedance 提示词指南](https://www.volcengine.com/docs/82379/2222480?lang=zh)。
+
+
+
+:::
+
+---
+
+
+**图片信息==^new^==** `object`
+输入给模型的图片信息。
+
+属性
+
+---
+
+
+content.**type ** `string` %%require%%
+输入内容的类型,此处应为 `image_url`。
+
+---
+
+
+content.**image_url ** `object` %%require%%
+输入给模型的图片对象。
+
+属性
+
+---
+
+
+content.image_url.**url ** `string` %%require%%
+图片 URL 、图片 Base64 编码、素材 ID。
+
+* 图片 URL:填入图片的公网 URL。
+* Base64 编码:将本地文件转换为 Base64 编码字符串,然后提交给大模型。遵循格式:`data:image/<图片格式>;base64,`,注意 `<图片格式>` 需小写,如 `data:image/png;base64,{base64_image}`。
+* 素材 ID:用于视频生成的预置素材及虚拟人像的 ID,遵循格式:asset://。可从 [素材&虚拟人像库](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision?modelId=doubao-seedance-2-0-260128) 获取。
+
+:::tip 传入单张图片要求
+
+* 格式:jpeg、png、webp、bmp、tiff、gif。其中,Seedance 1.5 pro 和 Seedance 2.0 系列模型新增支持 heic 和 heif。
+* 宽高比(宽/高): (0.4, 2.5)
+* 宽高长度(px):(300, 6000)
+* 大小:单张图片小于 30 MB。请求体大小不超过 64 MB。大文件请勿使用Base64编码。
+* 图片数量:
+ * 图生视频\-首帧:1 张
+ * 图生视频\-首尾帧:2 张
+ * Seedance 2.0 系列 多模态参考生视频:1~9 张
+
+:::
+
+---
+
+
+content.**role ** `string` `条件必填`
+图片的位置或用途。
+:::warning
+
+* **图生视频\-首帧**、**图生视频\-首尾帧**、**多模态参考生视频**(包括参考图、视频、音频)为 3 种互斥场景,**不可混用**。
+* **多模态参考生视频**可通过提示词指定参考图片作为首帧/尾帧,间接实现“首尾帧+多模态参考”效果。若需严格保障首尾帧和指定图片一致,**优先使用图生视频\-首尾帧**(配置 role 为 first_frame/last_frame)。
+
+
+:::
+图生视频\-首帧
+
+* **支持模型:** 所有模型
+* **字段role取值:** 需要传入1个 image_url 对象,字段 role 为 first_frame 或不填。
+
+
+图生视频\-首尾帧
+
+* **支持模型:** Seedance 2.0 系列,Seedance 1.5 pro、Seedance 1.0 pro
+* **字段role取值:** 需要传入2个image_url对象,且字段 role 必填。
+ * 首帧图片对应的字段 role 为:first_frame
+ * 尾帧图片对应的字段 role 为:last_frame
+
+:::tip
+传入的首尾帧图片可相同。首尾帧图片的宽高比不一致时,以首帧图片为主,尾帧图片会自动裁剪适配。
+
+:::
+
+图生视频\-参考图
+
+* **支持模型:** Seedance 2.0 系列(1~9 张图片)
+* **字段role取值:** 必填,每张参考图对应的字段 role 均为:reference_image
+
+
+
+---
+
+
+**视频信息==^new^==** `object`
+输入给模型的视频信息。仅 Seedance 2.0 系列支持输入视频。
+方舟平台信任 Seedance 2.0 系列模型生成的含人脸视频,您可使用**本账号下近30天内由上述模型生成的含人脸原始视频**,作为输入素材进行二次创作,详情参见 [教程](https://www.volcengine.com/docs/82379/2291680?lang=zh#341d7f71)。
+
+属性
+content.**type ** `string` %%require%%
+输入内容的类型,此处应为`video_url`。
+
+---
+
+
+content.**video_url** ** ** `object` %%require%%
+输入给模型的视频对象。
+
+属性
+content.video_url.**url ** `string` %%require%%
+视频URL、素材 ID。
+
+* 视频 URL:填入视频的公网 URL。
+* 素材 ID:用于视频生成的预置素材及虚拟人像视频的 ID,遵循格式:asset://。可从[素材&虚拟人像库](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision?modelId=doubao-seedance-2-0-260128)获取。
+
+:::tip 传入单个视频要求
+
+* 视频格式:mp4、mov,支持编码格式见下表。
+* 分辨率:480p,720p,1080p
+* 时长:单个视频时长 [2, 15] s,最多传入 3 个参考视频,所有视频总时长不超过 15s。
+* 尺寸:
+ * 宽高比(宽/高):[0.4, 2.5]
+ * 宽高长度(px):[300, 6000]
+ * 总像素数:[640×640=409600, 2206×946=2086876],即宽和高的乘积符合 [409600, 2086876] 的区间要求。
+* 大小:单个视频不超过 50 MB。
+* 帧率 (FPS):[24, 60]
+
+
+:::
+|**容器格式** |**常用文件扩展名** |**MIME** |**支持编码** |
+|---|---|---|---|
+|MP4 |.mp4 |video/mp4 |视频:H.264/AVC、H.265/HEVC|\
+| | | |音频:AAC、MP3 |
+|QuickTime |.mov |video/quicktime |视频:H.264/AVC、H.265/HEVC|\
+| | | |音频:AAC、MP3 |
+
+
+
+
+---
+
+
+content.**role ** `string` `条件必填`
+视频的位置或用途。当前仅支持 reference_video:参考视频。
+
+
+---
+
+
+**音频信息==^new^==** `object`
+输入给模型的音频信息。仅 Seedance 2.0 系列支持输入音频。
+注意不可单独输入音频,应至少包含 1 个参考视频或图片。
+
+属性
+content.**type ** `string` %%require%%
+输入内容的类型,此处应为`audio_url`。
+
+---
+
+
+content.**audio_url** ** ** `object` %%require%%
+输入给模型的音频对象。
+
+属性
+content.audio_url.**url ** `string` %%require%%
+音频 URL 、音频 Base64 编码、素材 ID。
+
+* 音频 URL:填入音频的公网 URL。
+* Base64 编码:将本地文件转换为 Base64 编码字符串,然后提交给大模型。遵循格式:`data:audio/<音频格式>;base64,`,注意 `<音频格式>` 需小写,如 `data:audio/wav;base64,{base64_audio}`。
+* 素材 ID:用于视频生成的虚拟人的音频素材 ID,遵循格式:asset://。可从[素材&虚拟人像库](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision?modelId=doubao-seedance-2-0-260128)获取。
+
+:::tip 传入单个音频要求
+
+* 格式:wav、mp3
+* 时长:单个音频时长 [2, 15] s,最多传入 3 段参考音频,所有音频总时长不超过 15 s。
+* 大小:单个音频不超过 15 MB,请求体大小不超过 64 MB。大文件请勿使用Base64编码。
+
+
+
+:::
+
+---
+
+
+content.**role ** `string` `条件必填`
+音频的位置或用途。当前仅支持 reference_audio:参考音频。
+
+
+
+---
+
+
+**样片信息 ** `object`
+基于样片任务 ID,生成正式视频。仅 Seedance 1.5 pro 支持该功能。[阅读](https://www.volcengine.com/docs/82379/1366799?lang=zh#5acd28c8)[文档](https://www.volcengine.com/docs/82379/1366799?lang=zh#5acd28c8) 获取 draft 功能的使用教程和注意事项。
+
+属性
+
+---
+
+
+content.**type ** `string` %%require%%
+输入内容的类型,此处应为 `draft_task`。
+
+---
+
+
+content.**draft_task** ** ** `object` %%require%%
+输入给模型的样片任务。
+
+属性
+
+---
+
+
+content.draft_task.**id ** `string` %%require%%
+样片任务 ID。平台将自动复用 Draft 视频使用的用户输入(**model、** content.**text、** content.**image_url、generate_audio、seed、ratio、duration、camera_fixed ** ),生成正式视频。其余参数支持指定,不指定将使用本模型的默认值。
+使用分为两步:Step1: 调用本接口生成 Draft 视频。Step2: 如果确认 Draft 视频符合预期,可基于 Step1 返回的 Draft 视频任务 ID,调用本接口生成最终视频。[阅读文档](https://www.volcengine.com/docs/82379/1366799?lang=zh#5acd28c8) 获取详细教程。
+
+
+
+
+---
+
+
+**callback_url** `string`
+填写本次生成任务结果的回调通知地址。当视频生成任务有状态变化时,方舟将向此地址推送 POST 请求。
+回调请求内容结构与[查询任务API](https://www.volcengine.com/docs/82379/1521309)的返回体一致。
+回调返回的 status 包括以下状态:
+
+* queued:排队中。
+* running:任务运行中。
+* succeeded: 任务成功。(如发送失败,即5秒内没有接收到成功发送的信息,回调三次)
+* failed:任务失败。(如发送失败,即5秒内没有接收到成功发送的信息,回调三次)
+* expired:任务超时,即任务处于**运行中或排队中**状态超过过期时间。可通过 **execution_expires_after ** 字段设置过期时间。
+
+
+---
+
+
+**return_last_frame** `boolean` `默认值 false`
+
+* true:返回生成视频的尾帧图像。设置为 `true` 后,可通过 [查询视频生成任务接口](https://www.volcengine.com/docs/82379/1521309) 获取视频的尾帧图像。尾帧图像的格式为 png,宽高像素值与生成的视频保持一致,无水印。
+ 使用该参数可实现生成多个连续视频:以上一个生成视频的尾帧作为下一个视频任务的首帧,快速生成多个连续视频,调用示例详见 [教程](https://www.volcengine.com/docs/82379/1366799?lang=zh#141cf7fa)。
+* false:不返回生成视频的尾帧图像。
+
+
+---
+
+
+**service_tier** `string` `默认值 default`
+> 不支持修改已提交任务的服务等级
+> Seedance 2.0 系列 不支持离线推理
+
+指定处理本次请求的服务等级类型,枚举值:
+
+* default:在线推理模式,RPM 和并发数配额较低(详见 [模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#2705b333)),适合对推理时效性要求较高的场景。
+* flex:离线推理模式,TPD 配额更高(详见 [模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#2705b333)),价格为在线推理的 50%, 适合对推理时延要求不高的场景。
+
+
+---
+
+
+**execution_expires_after ** `integer` `默认值 172800`
+任务超时阈值。指定任务提交后的过期时间(单位:秒),从 **created at** 时间戳开始计算。默认值 172800 秒,即 48 小时。取值范围:[3600,259200]。
+不论使用哪种 **service_tier**,都建议根据业务场景设置合适的超时时间。超过该时间后任务会被自动终止,并标记为`expired`状态。
+
+---
+
+
+**generate_audio ** `boolean` `默认值 true`
+> 仅 Seedance 2.0 系列、Seedance 1.5 pro 支持
+
+控制生成的视频是否包含与画面同步的声音。
+
+* true:模型输出的视频包含同步音频。模型会基于文本提示词与视觉内容,自动生成与之匹配的人声、音效及背景音乐。建议将对话部分置于双引号内,以优化音频生成效果。例如:男人叫住女人说:“你记住,以后不可以用手指指月亮。”
+* false:模型输出的视频为无声视频。
+
+:::warning
+生成的有声视频均为单声道,和传入的音频声道数无关。
+
+:::
+---
+
+
+**draft ** `boolean` `默认值 false`
+> 仅 Seedance 1.5 pro 支持
+
+控制是否开启样片模式。[阅读文档](https://www.volcengine.com/docs/82379/1366799?lang=zh#5acd28c8) 获取使用教程和注意事项。
+
+* true:开启样片模式,生成一段预览视频,快速验证场景结构、镜头调度、主体动作与 prompt 意图是否符合预期。消耗 token 数较正常视频更少,使用成本更低。
+* false:关闭样片模式,正常生成一段视频。
+
+:::tip
+开启样片模式后,将使用 480p 分辨率生成 Draft 视频(使用其他分辨率会报错),不支持返回尾帧功能,不支持离线推理功能。
+
+:::
+---
+
+
+**tools==^new^==** ** ** `object[]`
+> 仅 Seedance 2.0 系列 支持
+
+配置模型要调用的工具。
+
+属性
+tools.**type ** `string`
+指定使用的工具类型。
+
+* web_search:联网搜索工具。[阅读教程](https://www.volcengine.com/docs/82379/1366799?lang=zh#c40ed3ef) 获取详细代码示例。
+
+:::tip
+
+* 开启联网搜索后,模型会根据用户的提示词自主判断是否搜索互联网内容(如商品、天气等)。可提升生成视频的时效性,但也会增加一定的时延。
+* 实际搜索次数可通过 [查询视频生成任务 API](https://www.volcengine.com/docs/82379/1521309?lang=zh) 返回的 usage.tool_usage.**web_search** 字段获取,如果为 0 表示未搜索。
+
+:::
+
+---
+
+
+**safety_identifier==^new^==** `string`
+终端用户的唯一标识符,用于协助平台检测您的应用中可能违反火山方舟使用政策的用户。该标识符为英文字符串,需保证对单个用户固定且唯一,长度不超过 64 个字符。推荐传入对用户名、用户 ID 或邮箱进行哈希处理后生成的字符串,避免泄露用户隐私信息。
+
+---
+
+
+
+:::warning 部分参数升级说明
+
+* **对于 resolution、ratio、duration、frames、seed、camera_fixed、watermark 参数,平台升级了参数传入方式,示例如下。所有模型依然兼容支持旧方式。**
+* 不同模型,可能对应支持不同的参数与取值,详见 [输出视频格式](https://www.volcengine.com/docs/82379/1366799?lang=zh#9fe4cce0)。当输入的参数或取值不符合所选的模型时,该参数将被忽略或触发报错:
+ * 新方式:在 request body 中直接传入参数。此方式为**强校验,** 若参数填写错误,模型会返回错误提示。
+ * 旧方式:在文本提示词后追加 \-\-[parameters]。此方式为**弱校验,** 若参数填写错误,该参数将被忽略或触发报错。
+
+
+:::
+**新方式(推荐):在 request body 中直接传入参数**
+```JSON
+...
+ // Specify the aspect ratio of the generated video as 16:9, duration as 5 seconds, resolution as 720p, seed as 11, and include a watermark. The camera is not fixed.
+ "model": "doubao-seedance-1-5-pro-251215",
+ "content": [
+ {
+ "type": "text",
+ "text": "小猫对着镜头打哈欠"
+ }
+ ],
+ // All parameters must be written in full; abbreviations are not supported
+ "resolution": "720p",
+ "ratio":"16:9",
+ "duration": 5,
+ // "frames": 29, Either duration or frames is required
+ "seed": 11,
+ "camera_fixed": false,
+ "watermark": true
+...
+```
+
+
+
+
+**旧方式:在文本提示词后追加 \-\-[parameters]**
+```JSON
+...
+ // Specify the aspect ratio of the generated video as 16:9, duration as 5 seconds, resolution as 720p, seed as 11, and include a watermark. The camera is not fixed.
+ "model": "doubao-seedance-1-5-pro-251215",
+ "content": [
+ {
+ "type": "text",
+ "text": "小猫对着镜头打哈欠 --rs 720p --rt 16:9 --dur 5 --seed 11 --cf false --wm true"
+ // "text": "小猫对着镜头打哈欠 --resolution 720p --ratio 16:9 --duration 5 --seed 11 --camerafixed false --watermark true"
+ }
+ ]
+...
+```
+
+
+
+
+---
+
+
+**resolution ** `string`
+> Seedance 2.0 系列、Seedance 1.5 pro 默认值:`720p`
+> Seedance 1.0 pro & pro\-fast 默认值:`1080p`
+
+视频分辨率,枚举值:
+
+* 480p
+* 720p
+* 1080p:Seedance 2.0 fast 不支持
+
+
+---
+
+
+**ratio ** `string`
+> Seedance 2.0 系列、Seedance 1.5 pro 默认值为 `adaptive`
+> 其他模型:文生视频默认值 `16:9`,图生视频默认值 `adaptive`
+
+生成视频的宽高比例。不同宽高比对应的宽高像素值见下方表格。
+
+* 16:9
+* 4:3
+* 1:1
+* 3:4
+* 9:16
+* 21:9
+* adaptive:根据输入自动选择最合适的宽高比(详见下文说明)
+
+:::warning **adaptive ** 适配规则
+当配置 **ratio** 为 `adaptive` 时,模型会根据生成场景自动适配宽高比;实际生成的视频宽高比可通过 [查询视频生成任务 API](https://www.volcengine.com/docs/82379/1521309?lang=zh) 返回的 **ratio** 字段获取。
+**支持模型:**
+
+* Seedance 2.0 系列、Seedance 1.5 Pro 支持
+* 其他模型仅图生视频场景支持
+
+**取值规则:**
+
+* 文生视频:根据输入的提示词,智能选择最合适的宽高比。
+* 首帧 / 首尾帧生视频:根据上传的首帧图片比例,自动选择最接近的宽高比。
+* 多模态参考生视频:根据用户提示词意图判断,如果是首帧生视频/编辑视频/延长视频,以该图片/视频为准选择最接近的宽高比;否则,以传入的第一个媒体文件为准(优先级:视频>图片)选择最接近的宽高比。
+
+:::
+
+
+不同宽高比对应的宽高像素值
+Note:图生视频,选择的宽高比与您上传的图片宽高比不一致时,方舟会对您的图片进行裁剪,裁剪时会居中裁剪,详细规则见 [图片裁剪规则](https://www.volcengine.com/docs/82379/1366799?lang=zh#f76aafc8)。
+
+|分辨率 |宽高比|宽高像素值|宽高像素值|\
+| | |Seedance 1.0 系列 |Seedance 1.5 pro|\
+| | | |Seedance 2.0 系列 |
+|---|---|---|---|
+|480p |16:9 |864×480 |864×496 |
+|^^|4:3 |736×544 |752×560 |
+|^^|1:1 |640×640 |640×640 |
+|^^|3:4 |544×736 |560×752 |
+|^^|9:16 |480×864 |496×864 |
+|^^|21:9 |960×416 |992×432 |
+|720p |16:9 |1248×704 |1280×720 |
+|^^|4:3 |1120×832 |1112×834 |
+|^^|1:1 |960×960 |960×960 |
+|^^|3:4 |832×1120 |834×1112 |
+|^^|9:16 |704×1248 |720×1280 |
+|^^|21:9 |1504×640 |1470×630 |
+|1080p |16:9 |1920×1088 |1920×1080 |\
+|> Seedance 2.0 fast 不支持 | | | |
+|^^|4:3 |1664×1248 |1664×1248 |
+|^^|1:1 |1440×1440 |1440×1440 |
+|^^|3:4 |1248×1664 |1248×1664 |
+|^^|9:16 |1088×1920 |1080×1920 |
+|^^|21:9 |2176×928 |2206×946 |
+
+
+
+
+---
+
+
+**duration** `integer` `默认值 5`
+> duration 和 frames 二选一即可,frames 的优先级高于 duration。如果您希望生成整数秒的视频,建议指定 duration。
+
+生成视频时长,仅支持整数,单位:秒。
+
+* Seedance 1.0 pro、Seedance 1.0 pro fast: [2, 12] s。
+* Seedance 1.5 pro: [4,12] 或设置为`-1`
+* Seedance 2.0 系列: [4,15] 或设置为`-1`
+
+:::warning
+Seedance 2.0 系列、Seedance 1.5 pro 支持两种配置方法
+
+* 指定具体时长:支持有效范围内的任一整数。
+* 智能指定:设置为 `-1`,表示由模型在有效范围内自主选择合适的视频长度(整数秒)。实际生成视频的时长可通过 [查询视频生成任务 API](https://www.volcengine.com/docs/82379/1521309?lang=zh) 返回的 **duration** 字段获取。注意视频时长与计费相关,请谨慎设置。
+
+
+:::
+---
+
+
+**frames** `integer`
+> Seedance 2.0 系列、Seedance 1.5 pro 暂不支持
+> duration 和 frames 二选一即可,frames 的优先级高于 duration。如果您希望生成小数秒的视频,建议指定 frames。
+
+生成视频的帧数。通过指定帧数,可以灵活控制生成视频的长度,生成小数秒的视频。
+由于 frames 的取值限制,仅能支持有限小数秒,您需要根据公式推算最接近的帧数。
+
+* 计算公式:帧数 = 时长 × 帧率(24)。
+* 取值范围:支持 [29, 289] 区间内所有满足 `25 + 4n` 格式的整数值,其中 n 为正整数。
+
+例如:假设需要生成 2.4 秒的视频,帧数=2.4×24=57.6。由于 frames 不支持 57.6,此时您只能选择一个最接近的值。根据 25+4n 计算出最接近的帧数为 57,实际生成的视频为 57/24=2.375 秒。
+
+---
+
+
+**seed** `integer` `默认值 -1`
+种子整数,用于控制生成内容的随机性。
+取值范围:[\-1, 2^32\-1]之间的整数。
+:::warning
+
+* 相同的请求下,模型收到不同的seed值,如:不指定seed值或令seed取值为\-1(会使用随机数替代)、或手动变更seed值,将生成不同的结果。
+* 相同的请求下,模型收到相同的seed值,会生成类似的结果,但不保证完全一致。
+
+
+:::
+---
+
+
+**camera_fixed** `boolean` `默认值 false`
+> 参考图场景不支持,Seedance 2.0 系列 暂不支持
+
+是否固定摄像头。枚举值:
+
+* true:固定摄像头。平台会在用户提示词中追加固定摄像头,实际效果不保证。
+* false:不固定摄像头。
+
+
+---
+
+
+**watermark** `boolean` `默认值 false`
+生成视频是否包含水印。枚举值:
+
+* false:生成视频不含水印。
+* true:生成视频右下角会展示`AI 生成`水印。
+
+
+---
+
+
+
+## 响应参数
+> 跳转 [请求参数](#RxN8G2nH)
+
+**id ** `string`
+视频生成任务 ID 。仅保存 7 天(从 **created at** 时间戳开始计算),超时后将自动清除。
+
+* 设置`"draft": true`,为 Draft 视频任务 ID。
+* 设置 `"draft": false`,为正常视频任务 ID。
+
+创建视频生成任务为异步接口,获取 ID 后,需要通过 [查询视频生成任务 API](https://www.volcengine.com/docs/82379/1521309) 来查询视频生成任务的状态。任务成功后,会输出生成视频的`video_url`。
+
+
diff --git a/docs/协议文档/豆包系列/seedance_2.0_API_取消任务.md b/docs/协议文档/豆包系列/seedance_2.0_API_取消任务.md
new file mode 100644
index 0000000..08ed298
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedance_2.0_API_取消任务.md
@@ -0,0 +1,50 @@
+`DELETE https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/{id}` [运行](https://api.volcengine.com/api-explorer/?action=DeleteContentsGenerationsTasks&data=%7B%22id%22%3A%22cgt-20250331175019-68d9t%22%7D&groupName=%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90API&query=%7B%7D&serviceCode=ark&version=2024-01-01)
+取消排队中的视频生成任务,或者删除视频生成任务记录。
+
+```mixin-react
+return (
+ [模型列表](https://www.volcengine.com/docs/82379/1330310)  [模型计费](https://www.volcengine.com/docs/82379/1099320#%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90%E6%A8%A1%E5%9E%8B)  [API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
+  [调用教程](https://www.volcengine.com/docs/82379/1366799)  [接口文档](https://www.volcengine.com/docs/82379/1521675)  [常见问题](https://www.volcengine.com/docs/82379/1359411)  [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
+`}>
+);
+```
+
+
+---
+
+
+
+## 请求参数
+> 跳转 [响应参数](#7mi8G8RI)
+
+:::tip
+下面参数为Query String Parameters,在URL String中传入。
+
+:::
+---
+
+
+**id** `string` %%require%%
+需要取消或者删除的视频生成任务。
+任务状态不同,调用`DELETE`接口,执行的操作有所不同,具体说明如下:
+
+|当前任务状态 |是否支持DELETE操作 |操作含义 |DELETE操作后任务状态 |
+|---|---|---|---|
+|queued |是 |任务取消排队,任务状态被变更为cancelled。 |cancelled |
+|running |否 |\- |\- |
+|succeeded |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
+|failed |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
+|cancelled |否 |\- |\- |
+|expired |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
+
+
+---
+
+
+
+## 响应参数
+> 跳转 [请求参数](#RxN8G2nH)
+
+本接口无返回参数。
+
diff --git a/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成.md b/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成.md
new file mode 100644
index 0000000..84fc87c
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成.md
@@ -0,0 +1,250 @@
+`GET https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/{id}` [运行](https://api.volcengine.com/api-explorer/?action=GetContentsGenerationsTask&data=%7B%22id%22%3A%22cgt-20250331175019-68d9t%22%7D&groupName=%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90API&query=%7B%7D&serviceCode=ark&version=2024-01-01)
+查询视频生成任务的状态。
+:::tip
+仅支持查询最近 7 天的历史数据。时间计算统一采用UTC时间戳,返回的7天历史数据范围以用户实际发起查询请求的时刻为基准(精确到秒),时间戳区间为 [T\-7天, T)。
+
+:::
+```mixin-react
+return (
+ [模型列表](https://www.volcengine.com/docs/82379/1330310)  [模型计费](https://www.volcengine.com/docs/82379/1099320#%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90%E6%A8%A1%E5%9E%8B)  [API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
+  [调用教程](https://www.volcengine.com/docs/82379/1366799)  [接口文档](https://www.volcengine.com/docs/82379/1521309)  [常见问题](https://www.volcengine.com/docs/82379/1359411)  [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
+`}>
+);
+```
+
+
+---
+
+
+
+## 请求参数
+> 跳转 [响应参数](#7mi8G8RI)
+
+
+---
+
+
+**id** `string` %%require%%
+您需要查询的视频生成任务的 ID 。
+:::tip
+上面参数为Query String Parameters,在URL String中传入。
+
+:::
+---
+
+
+
+
+## 响应参数
+> 跳转 [请求参数](#RxN8G2nH)
+
+
+---
+
+
+**id ** `string`
+视频生成任务 ID 。
+
+---
+
+
+**model** `string`
+任务使用的模型名称和版本,`模型名称-版本`。
+
+---
+
+
+**status** `string`
+任务状态,以及相关的信息:
+
+* `queued`:排队中。
+* `running`:任务运行中。
+* `cancelled`:取消任务,取消状态24h自动删除(只支持排队中状态的任务被取消)。
+* `succeeded`: 任务成功。
+* `failed`:任务失败。
+* `expired`:任务超时。
+
+
+---
+
+
+**error** `object / null`
+错误提示信息,任务成功返回`null`,任务失败时返回错误数据,错误信息具体参见 [错误处理](https://www.volcengine.com/docs/82379/1299023#.5pa56Iif6ZSZ6K-v56CB)。
+
+属性
+
+---
+
+
+error.**code** `string`
+错误码。
+
+---
+
+
+error.**message** `string`
+错误提示信息。
+
+
+---
+
+
+**created_at** `integer`
+任务创建时间的 Unix 时间戳(秒)。
+
+---
+
+
+**updated_at** `integer`
+任务当前状态更新时间的 Unix 时间戳(秒)。
+
+---
+
+
+**content** `object`
+视频生成任务的输出内容。
+
+属性
+
+---
+
+
+content.**video_url** `string`
+生成视频的 URL,格式为 mp4。为保障信息安全,生成的视频会在24小时后被清理,请及时转存。
+推荐配置火山引擎 TOS 提供的数据订阅功能,将您的模型推理产物自动转存到自己的 TOS 桶中,便于长期备份或二次加工。详细介绍请参见 [TOS 数据订阅](https://www.volcengine.com/docs/6349/2280949?lang=zh)。
+content.**last_frame_url ** `string`
+视频的尾帧图像 URL。有效期为 24小时,请及时转存。
+说明:[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时设置 `"return_last_frame": true` 时,会返回该参数。
+
+
+---
+
+
+**seed** `integer`
+本次请求使用的种子整数值。
+
+---
+
+
+**resolution ** `string`
+生成视频的分辨率。
+
+---
+
+
+**ratio ** `string`
+生成视频的宽高比。
+
+---
+
+
+**duration** `integer`
+生成视频的时长,单位:秒。
+说明:**duration 和 frames 参数只会返回一个**。[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时未指定 frames,会返回 duration。
+
+---
+
+
+**frames** `integer`
+生成视频的帧数。
+说明:**duration 和 frames 参数只会返回一个**。[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时指定了 frames,会返回 frames。
+
+---
+
+
+**framespersecond** `integer`
+生成视频的帧率。
+
+---
+
+
+**generate_audio** `boolean`
+生成的视频是否包含与画面同步的声音。仅 seedance 2.0 & 2.0 fast、seedance 1.5 pro 会返回该参数。
+
+* `true`:模型输出的视频包含同步音频。
+* `false`:模型输出的视频为无声视频。
+
+
+---
+
+
+**tools==^new^==** ** ** `object[]`
+本次请求模型实际使用的工具。未使用工具时不返回。
+
+属性
+tools.**type ** `string`
+实际使用的工具类型
+
+* web_search:联网搜索工具。
+
+
+---
+
+
+**safety_identifier==^new^==** `string`
+终端用户的唯一标识符。若 [创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时设置了该参数,接口会原样返回此信息。
+
+---
+
+
+**draft** `boolean`
+生成的视频是否为 Draft 视频。仅 seedance 1.5 pro 会返回该参数。
+
+* `true`:表示当前输出为 Draft 视频。
+* `false`:表示当前输出为正常视频。
+
+
+---
+
+
+**draft_task_id ** `string`
+Draft 视频任务 ID。基于 Draft 视频生成正式视频时,会返回该参数。
+
+---
+
+
+**service_tier ** `string`
+实际处理任务使用的服务等级。
+
+---
+
+
+**execution_expires_after** ** ** `integer`
+任务超时阈值,单位:秒。
+
+---
+
+
+**usage** `object`
+本次请求的 token 用量。
+
+属性
+
+---
+
+
+usage.**completion_tokens** `integer`
+模型输出视频花费的 token 数量,可作为计费对账依据。
+:::tip
+seedance 2.0 系列模型存在最低 token 用量限制,如果实际 token 用量 < 最低 token 用量,本字段会返回最低 token 用量,平台按最低 token 用量计费。
+
+:::
+---
+
+
+usage.**total_tokens** `integer`
+本次请求消耗的总 token 数量。视频生成模型不统计输入 token,输入 token 为 0,故 **total_tokens**=**completion_tokens**。
+
+---
+
+
+usage.**tool_usage==^new^==** ** ** `object`
+使用工具的用量信息。
+
+属性
+usage.tool_usage.**web_search ** `integer`
+实际调用联网搜索工具的次数,仅开启联网搜索时返回。
+
+
+
diff --git a/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成任务列表.md b/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成任务列表.md
new file mode 100644
index 0000000..ca5a334
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedance_2.0_API_查询视频生成任务列表.md
@@ -0,0 +1,308 @@
+`GET https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks?page_num={page_num}&page_size={page_size}&filter.status={filter.status}&filter.task_ids={filter.task_ids}&filter.model={filter.model}` [运行](https://api.volcengine.com/api-explorer/?action=ListContentsGenerationsTasks&data=%7B%7D&groupName=%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90API&query=%7B%7D&serviceCode=ark&version=2024-01-01)
+当您要查询符合条件的任务,您可以传入条件筛选参数,返回符合要求的任务。
+:::tip
+仅支持查询最近 7 天的历史数据。时间计算统一采用UTC时间戳,返回的7天历史数据范围以用户实际发起批量查询请求的时刻为基准(精确到秒),时间戳区间为 [T\-7天, T)。
+
+:::
+```mixin-react
+return (
+ [模型列表](https://www.volcengine.com/docs/82379/1330310)  [模型计费](https://www.volcengine.com/docs/82379/1099320#%E8%A7%86%E9%A2%91%E7%94%9F%E6%88%90%E6%A8%A1%E5%9E%8B)  [API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
+  [调用教程](https://www.volcengine.com/docs/82379/1366799)  [接口文档](https://www.volcengine.com/docs/82379/1521675)  [常见问题](https://www.volcengine.com/docs/82379/1359411)  [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
+`}>
+);
+```
+
+
+---
+
+
+
+## 请求参数
+> 跳转 [响应参数](#7mi8G8RI)
+
+:::tip
+下面参数为Query String Parameters,在URL String中传入。
+
+:::
+---
+
+
+**page_num** `integer / null`
+取值范围:[1, 500]
+返回结果的页码。
+
+---
+
+
+**page_size ** `integer / null`
+取值范围:[1, 500]
+返回结果的每页的结果数量。
+
+---
+
+
+**filter.status ** `string / null`
+过滤参数,查询某个任务状态。
+
+* `queued`:排队中的任务。
+* `running`:运行中任务。
+* `cancelled`:取消的任务。
+* `succeeded`: 成功的任务。
+* `failed`:失败的任务。
+
+
+---
+
+
+**filter.task_ids ** `string[] / null`
+视频生成任务 ID,精确搜索,支持同时搜索多个任务 ID。多个任务 ID 之间通过 `&`连接。示例:`filter.task_ids=id1&filter.task_ids=id2`。
+
+---
+
+
+**filter.model ** `string / null`
+与返回参数不同,该字段为任务使用的推理接入点 ID,精确搜索。
+
+---
+
+
+**filter.service_tier ** `string / null` `默认值 default`
+处理任务使用的服务等级。
+
+* `default`:在线推理模式
+* `flex`:离线推理模式
+
+
+## 响应参数
+> 跳转 [请求参数](#RxN8G2nH)
+
+
+---
+
+
+**items ** `object[]`
+查询到的视频生成任务列表。
+
+属性
+
+---
+
+
+items.**id ** `string`
+视频生成任务 ID 。
+
+---
+
+
+items.**model** `string`
+任务使用的模型名称和版本,`模型名称-版本`。
+
+---
+
+
+items.**status** `string`
+任务状态,以及相关的信息:
+
+* `queued`:排队中。
+* `running`:任务运行中。
+* `cancelled`:取消任务(只支持排队中状态的任务被取消)。
+* `succeeded`: 任务成功。
+* `failed`:任务失败。
+* `expired`:任务超时。
+
+
+---
+
+
+items.**error** `object / null`
+错误提示信息,任务成功返回`null`,任务失败时返回错误数据,错误信息具体参见 [错误处理](https://www.volcengine.com/docs/82379/1393047#653d2c40)。
+
+属性
+
+---
+
+
+error.**code** `string`
+错误码。
+
+---
+
+
+error.**message** `string`
+错误提示信息。
+
+
+---
+
+
+items.**created_at** `integer`
+任务创建时间的 Unix 时间戳(秒)。
+
+---
+
+
+items.**updated_at** `integer`
+任务当前状态更新时间的 Unix 时间戳(秒)。
+
+---
+
+
+items.**content** `object`
+当视频生成任务完成,会输出该字段,包含生成视频下载的 URL。
+
+属性
+
+---
+
+
+content.**video_url** `string`
+生成视频的URL。为保障信息安全,生成的视频会在24小时后被清理,请及时转存。
+
+---
+
+
+content.**last_frame_url ** `string`
+视频的尾帧图像 URL。有效期为 24小时,请及时转存。
+说明:[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时设置 `"return_last_frame": true` 时,会返回参数。
+
+
+---
+
+
+items.**seed** `integer`
+本次请求使用的种子整数值。
+
+---
+
+
+items.**resolution ** `string`
+生成视频的分辨率。
+
+---
+
+
+items.**ratio ** `string`
+生成视频的宽高比。
+
+---
+
+
+items.**duration** `integer`
+生成视频的时长,单位:秒。
+说明:**duration 和 frames 参数只会返回一个**。[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时未指定 frames,会返回 duration。
+
+---
+
+
+items.**frames ** `integer`
+生成视频的帧数。
+说明:**duration 和 frames 参数只会返回一个**。[创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时指定了 frames,会返回 frames。
+
+---
+
+
+items.**framespersecond** `integer`
+生成视频的帧率。
+
+---
+
+
+items.**generate_audio** `boolean`
+生成的视频是否包含与画面同步的声音。仅 Seedance 2.0 系列、Seedance 1.5 pro 会返回该参数。
+
+* `true`:模型输出的视频包含同步音频。
+* `false`:模型输出的视频为无声视频。
+
+
+---
+
+
+items.**tools==^new^==** ** ** `object[]`
+本次请求模型实际使用的工具。未使用工具时不返回。
+
+属性
+items.tools.**type ** `string`
+实际使用的工具类型
+
+* web_search:联网搜索工具。
+
+
+---
+
+
+items.**safety_identifier==^new^==** `string`
+终端用户的唯一标识符。若 [创建视频生成任务](https://www.volcengine.com/docs/82379/1520757) 时设置了该参数,接口会原样返回此信息。
+
+---
+
+
+items.**draft** `boolean`
+生成的视频是否为 Draft 视频。仅 Seedance 1.5 pro 会返回该参数。
+
+* `true`:表示当前输出为 Draft 视频。
+* `false`:表示当前输出为正常视频。
+
+
+---
+
+
+items.**draft_task_id ** `string`
+Draft 视频任务 ID。基于 Draft 视频生成正式视频时,会返回该参数。
+
+---
+
+
+items.**service_tier ** `string`
+实际处理任务使用的服务等级。
+
+---
+
+
+items.**execution_expires_after** ** ** `integer`
+任务超时阈值,单位:秒。
+
+---
+
+
+items.**usage** `object`
+本次请求的 token 用量。
+
+属性
+
+---
+
+
+items.usage.**completion_tokens** `integer`
+模型输出视频花费的 token 数量,可作为计费对账依据。
+:::tip
+Seedance 2.0 系列模型存在最低 token 用量限制,如果实际 token 用量 < 最低 token 用量,本字段会返回最低 token 用量,平台按最低 token 用量计费。
+
+:::
+---
+
+
+items.usage.**total_tokens**`integer`
+本次请求消耗的总 token 数量。视频生成模型不统计输入 token,输入 token 为 0,故 **total_tokens**=**completion_tokens**。
+
+---
+
+
+items.usage.**tool_usage==^new^==** ** ** `object`
+使用工具的用量信息。
+
+属性
+items.usage.tool_usage.**web_search ** `integer`
+实际调用联网搜索工具的次数,仅开启联网搜索时返回。
+
+
+
+
+
+---
+
+
+**total ** `integer`
+符合筛选条件的任务数量。
+
+
diff --git a/docs/协议文档/豆包系列/seedance_2.0教程.md b/docs/协议文档/豆包系列/seedance_2.0教程.md
new file mode 100644
index 0000000..88f6ccf
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedance_2.0教程.md
@@ -0,0 +1,2263 @@
+Doubao Seedance 2.0 系列(下文简称 Seedance 2.0 系列)模型支持图像、视频、音频、文本等多种模态内容输入,具备视频生成、视频编辑、视频延长等能力,可高精度还原物品细节、音色、效果、风格、运镜等,保持稳定角色特征,赋予使用者如同导演般的掌控权。本文介绍 Seedance 2.0 系列模型的专属能力,帮助您快速实现 [Video Generation API](https://www.volcengine.com/docs/82379/1520758) 调用。
+:::tip
+请确保您的账户余额大于等于 200 元([前往充值](https://console.volcengine.com/finance/fund/recharge)),或已[购买资源包](https://console.volcengine.com/common-buy/fast/ark_bd%7C%7Cd682ppeeq1mp7kd5q0e0),否则无法开通 Seedance 2.0 系列模型。
+:::
+
+# 新手入门
+本入门教程专为 **API 新手用户** 设计,帮助您一键搭建 Python 开发环境、完成虚拟环境创建和方舟 SDK 安装,并提供直接可运行的 Seedance 2.0 系列调用代码,您只需修改对应的输入素材,即可开始您的视频生成创作。
+**1. 准备工作**
+在开始之前,请确保您已经完成以下准备:
+
+1. **注册账号**:确保您拥有火山引擎账号并已[登录](https://console.volcengine.com/)。
+2. **获取 API Key**:访问 [API Key 管理页面](https://console.volcengine.com/ark/region:ark+cn-beijing/apikey),点击 **创建 API Key**,并复制保存您的 API Key。注意请妥善保管您的 API Key,不要泄露给他人。
+3. [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&advancedActiveKey=model&projectName=default&tab=ComputerVision):请确保您的账户余额大于等于 200 元,或已[购买资源包](https://console.volcengine.com/common-buy/fast/ark_bd%7C%7Cd682ppeeq1mp7kd5q0e0),否则无法开通 Seedance 2.0 系列模型。
+4. **下载并解压文件**:点击下载下方附件,将其解压到您的本地目录(如桌面或“下载”文件夹)。
+ ark_seedance2.0_quickstart_package.zip
+
+
+**2.操作步骤**
+
+```mixin-react
+return (
+
+);
+```
+
+**3.运行说明**
+运行脚本后,您将看到如下流程:
+
+1. **API Key 校验**:脚本会自动检测您本地是否配置了`ARK_API_KEY`环境变量。如果没有,会提示您手动输入。
+2. **素材预览**:脚本会自动在您的默认浏览器中弹出一个本地生成的 HTML 页面,直观地展示本次任务的文本提示词、待替换的参考图片以及原始参考视频。
+3. **任务创建与轮询**:脚本向火山方舟服务器发起异步请求。由于视频生成需要一定时间,控制台会每隔 30 秒打印一次任务状态(如 `running`等)。
+4. **获取结果**:任务成功后,控制台会输出一段最终生成的视频 URL。您可以复制该链接到浏览器下载或在线播放。
+
+**4.下一步**
+在成功跑通本示例后,您可以尝试修改 `python/demo_standard.py`,来打造您专属的视频生成任务:
+
+1. 修改文本提示词
+
+找到代码中的 `user_content` 变量,更改为您想要的画面描述。
+
+2. 替换输入素材 (图片、视频、音频)
+
+您可以将 `reference_image_url`、`reference_video_url` 和 `reference_audio_url` 替换为您自己的素材链接。
+**注意**:请确保 URL 是公网可公开访问的链接(建议存放在 TOS 对象存储服务中,并配置为公共读)。
+
+3. 继续学习下文中丰富的使用示例。
+
+
+# 模型能力
+Seedance 2.0 系列模型目前包括 Doubao Seedance 2.0(下文简称 Seedance 2.0)和 Doubao Seedance 2.0 Fast(下文简称 Seedance 2.0 Fast),它们的模型能力相同。追求最高生成品质,推荐使用 Seedance 2.0;更注重成本与生成速度,不要求极限品质,推荐使用 Seedance 2.0 Fast。
+
+
+|模型名称 | |[Seedance 2.0](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedance-2-0&projectName=default) |[Seedance 2.0 Fast](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedance-2-0-fast&projectName=default) |
+|---|---|---|---|
+|Model ID | |doubao\-seedance\-2\-0\-260128 |doubao\-seedance\-2\-0\-fast\-260128 |
+|[文生视频](https://www.volcengine.com/docs/82379/2298881?lang=zh#4e74bcee) | | | |
+|[图生视频-首帧](https://www.volcengine.com/docs/82379/2298881?lang=zh#979b2d28) | | | |
+|[图生视频-首尾帧](https://www.volcengine.com/docs/82379/2298881?lang=zh#0d55ca07) | | | |
+|[多模态参考](https://www.volcengine.com/docs/82379/2291680?lang=zh#50e1b4ea)【New】 |图片参考 | | |
+|^^|视频参考 | | |
+|^^|组合参考| | |\
+| || | |\
+| |* 图片 + 音频| | |\
+| |* 图片 + 视频| | |\
+| |* 视频 + 音频| | |\
+| |* 图片 + 视频 + 音频 | | |
+|[编辑视频](https://www.volcengine.com/docs/82379/2291680?lang=zh#75a28782)【New】 | | | |
+|[延长视频](https://www.volcengine.com/docs/82379/2291680?lang=zh#46d77653)【New】 | | | |
+|[生成有声视频](https://www.volcengine.com/docs/82379/2298881?lang=zh#979b2d28)| | | |\
+|> "generate_audio": "true" | | | |
+|[联网搜索工具](https://www.volcengine.com/docs/82379/2291680?lang=zh#c40ed3ef)【New】 | | | |
+|[样片模式](https://www.volcengine.com/docs/82379/2298881?lang=zh#5acd28c8) | | | |
+|[返回视频产物对应的尾帧图](https://www.volcengine.com/docs/82379/2298881?lang=zh#141cf7fa)| | | |\
+|> "return_last_frame":| | | |\
+|> "true" | | | |
+|[输出视频规格](https://www.volcengine.com/docs/82379/2298881?lang=zh#9fe4cce0) |输出分辨率|480p, 720p, 1080p |480p, 720p |\
+| |> "resolution": "720p" | | |
+| |输出宽高比|21:9, 16:9, 4:3,|21:9, 16:9, 4:3,|\
+| |> "ratio":"16:9" |1:1, 3:4, 9:16 |1:1, 3:4, 9:16 |
+| |输出时长|4~15 秒 |4~15 秒 |\
+| |> "duration": 5 | | |
+| |输出视频格式 |mp4 |mp4 |
+|[离线推理](https://www.volcengine.com/docs/82379/2298881?lang=zh#c3588bd1)| | | |\
+|> "service_tier": "flex" | | | |
+|在线推理限流 |最大 RPM |企业用户:600|企业用户:600|\
+| | |个人用户:180 |个人用户:180 |
+| |最大并发数 |企业用户:10|企业用户:10|\
+| | |个人用户:3 |个人用户:3 |
+|离线推理限流 |TPD |\- |\- |
+
+
+# 基础使用
+
+## 多模态参考
+输入文本、参考图、视频(可带音轨)和音频等内容,来生成一段新视频。可继承参考图片的角色形象、视觉风格、画面构图;参考视频的主体内容、运镜方式、动作表现、整体风格;以及参考音频的音色、音乐旋律、对话内容等核心信息。
+效果预览如下(访问[模型卡片](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedance-2-0)查看更多示例):
+
+
+|输入:文本 |输入:图片、视频、音频 |输出 |
+|---|---|---|
+|全程使用**视频1**的第一视角构图,全程使用**音频1**作为背景音乐。第一人称视角果茶宣传广告,seedance牌「苹苹安安」苹果果茶限定款;首帧为**图片1**,你的手摘下一颗带晨露的阿克苏红苹果,轻脆的苹果碰撞声;2\-4 秒:快速切镜,你的手将苹果块投入雪克杯,加入冰块与茶底,用力摇晃,冰块碰撞声与摇晃声卡点轻快鼓点,背景音:「鲜切现摇」;4\-6 秒:第一人称成品特写,分层果茶倒入透明杯,你的手轻挤奶盖在顶部铺展,在杯身贴上粉红包标,镜头拉近看奶盖与果茶的分层纹理;6\-8 秒:第一人称手持举杯,你将**图片2**中的果茶举到镜头前(模拟递到观众面前的视角),杯身标签清晰可见,背景音「来一口鲜爽」,尾帧定格为**图片2**。背景声音统一为女生音色。 |||\
+| || |\
+| | | |\
+| | | |\
+| |r2v_tea_audio1.mp3| |\
+| | | |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // 1. Text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // 2. Reference image 1
+ contents.add(Content.builder()
+ .type("image_url")
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+ .url(refImage1)
+ .build())
+ .role("reference_image")
+ .build());
+
+ // 3. Reference image 2
+ contents.add(Content.builder()
+ .type("image_url")
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+ .url(refImage2)
+ .build())
+ .role("reference_image")
+ .build());
+
+ // 4. Reference video
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // 5. Reference audio
+ contents.add(Content.builder()
+ .type("audio_url")
+ .audioUrl(CreateContentGenerationTaskRequest.AudioUrl.builder()
+ .url(refAudio)
+ .build())
+ .role("reference_audio")
+ .build());
+
+ // Create video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .generateAudio(generateAudio)
+ .model(modelId)
+ .content(contents)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println("Task Created: " + createResult);
+
+ // Get task details and poll status
+ String taskId = createResult.getId();
+ pollTaskStatus(taskId);
+ \}
+
+ /**
+ * Poll task status
+ * @param taskId Task ID
+ */
+
+ private static void pollTaskStatus(String taskId) \{
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ try \{
+ while (true) \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ if (getResponse.getError() != null) \{
+ System.out.println("Error: " + getResponse.getError().getMessage());
+ \}
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...%n", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ \} catch (Exception e) \{
+ System.err.println("Error occurred: " + e.getMessage());
+ \} finally \{
+ service.shutdownExecutor();
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+:::tip
+
+* 您可任意组合以下模态内容,注意不支持“文本+音频”、“纯音频” 输入。
+ * 文本
+ * 图片:0~9 张
+ * 视频:0~3 个
+ * 音频:0~3 个
+* **进阶用法**:多模态生视频可通过提示词指定参考图片作为首帧/尾帧,间接实现“首尾帧+多模态参考”效果。若需严格保障首尾帧和指定图片一致,**优先使用图生视频\-首尾帧**(配置 role 为 first_frame/last_frame)。
+* 各个模态信息输入要求参见[多模态输入](/docs/82379/1366799#63a97f09)。
+
+:::
+
+## 编辑视频
+您可以提供待编辑的视频、参考图片或音频,并结合使用提示词,完成多种视频编辑任务,例如:替换视频主体、视频中对象增删改、局部画面重绘/修复等。
+效果预览如下(访问[模型卡片](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedance-2-0)查看更多示例):
+
+
+|输入:文本 |输入:视频&图片 |输出 |
+|---|---|---|
+|将**视频1**礼盒中的香水替换成**图像1**中的面霜,运镜不变 |||\
+| || |\
+| | | |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // 1. Text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // 2. Reference image 1
+ contents.add(Content.builder()
+ .type("image_url")
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+ .url(refImage1)
+ .build())
+ .role("reference_image")
+ .build());
+
+ // 3. Reference video
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // Create video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .generateAudio(generateAudio)
+ .model(modelId)
+ .content(contents)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println("Task Created: " + createResult);
+
+ // Get task details and poll status
+ String taskId = createResult.getId();
+ pollTaskStatus(taskId);
+ \}
+
+ /**
+ * Poll task status
+ * @param taskId Task ID
+ */
+
+ private static void pollTaskStatus(String taskId) \{
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ try \{
+ while (true) \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ if (getResponse.getError() != null) \{
+ System.out.println("Error: " + getResponse.getError().getMessage());
+ \}
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...%n", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ \} catch (Exception e) \{
+ System.err.println("Error occurred: " + e.getMessage());
+ \} finally \{
+ service.shutdownExecutor();
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+
+## 延长视频
+在原有视频基础上,向前或者向后延长视频,或多个视频片段(最多 3 个视频片段)串联成一个连贯视频。
+效果预览如下(访问[模型卡片](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedance-2-0)查看更多示例):
+
+
+|输入:文本 |输入:待延长视频 |输出 |
+|---|---|---|
+|**视频1**中的拱形窗户打开,进入美术馆室内,接**视频2**,之后镜头进入画内,接**视频3** |||\
+| || |\
+| || |\
+| || |\
+| || |\
+| | | |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // 1. Text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // 2. Reference video 1
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo1)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // 3. Reference video 2
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo2)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // 4. Reference video 3
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo3)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // Create video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .generateAudio(generateAudio)
+ .model(modelId)
+ .content(contents)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println("Task Created: " + createResult);
+
+ // Get task details and poll status
+ String taskId = createResult.getId();
+ pollTaskStatus(taskId);
+ \}
+
+ /**
+ * Poll task status
+ * @param taskId Task ID
+ */
+
+ private static void pollTaskStatus(String taskId) \{
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ try \{
+ while (true) \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ if (getResponse.getError() != null) \{
+ System.out.println("Error: " + getResponse.getError().getMessage());
+ \}
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...%n", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ \} catch (Exception e) \{
+ System.err.println("Error occurred: " + e.getMessage());
+ \} finally \{
+ service.shutdownExecutor();
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+:::tip
+
+* 向前或向后延长 1 段视频,生成的视频一般只包含原视频的尾部画面。但您也可以通过提示词灵活控制,使其包含原视频内容。 例如:向前延长视频1,[延长内容描述...],**最后接视频1**。
+* 传入 2~3 段视频,补全中间过渡部分,生成的视频会包含原视频内容和新生成的视频内容。
+
+:::
+
+## 使用联网搜索
+> 联网搜索能力仅适用于纯文本输入
+
+通过配置 tools.**type** 参数为`web_search`即可使用联网搜索工具。
+
+* 开启联网搜索后,模型会根据用户的提示词自主判断是否搜索互联网内容(如商品、天气等)。可提升生成视频的时效性,但也会增加一定的时延。
+* 实际搜索次数可通过 [查询视频生成任务 API](https://www.volcengine.com/docs/82379/1521309?lang=zh) 返回的 usage.tool_usage.**web_search** 字段获取,如果为 0 表示未搜索。
+
+
+
+|输入:文本 |输出 |
+|---|---|
+|微距镜头对准叶片上翠绿的玻璃蛙。焦点逐渐从它光滑的皮肤,转移到它完全透明的腹部,一颗鲜红的心脏正在有力地、规律地收缩扩张。||\
+|:::tip| |\
+|联网搜索玻璃蛙的容貌特征。| |\
+|| |\
+|:::| |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // Create a video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .model(modelId)
+ .content(contents)
+ .generateAudio(generateAudio)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .tools(Collections.singletonList(webSearchTool))
+ .build();
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println(createResult);
+ // Get the details of the task
+ String taskId = createResult.getId();
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ // Polling query section
+ System.out.println("----- polling task status -----");
+ while (true) \{
+ try \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ System.out.println("Error: " + getResponse.getStatus());
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ break;
+ \}
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+
+## 更多能力
+Seedance 2.0 系列模型也支持文生视频、首帧图生视频、首尾帧图生视频、设置视频输出规格等通用基础能力,详情请参见 [视频生成教程](/docs/82379/2298881)。
+
+# 便利创作
+Seedance 2.0 系列模型不支持直接上传含有真人人脸的参考图/视频。为便利创作者使用肖像,平台推出了以下解决方案。
+
+
+|方案 |介绍 |
+|---|---|
+|[信任模型产物作为输入素材](/docs/82379/2291680#341d7f71) |本账号下部分模型生成的含人脸原始产物可作为输入素材,再次调用 Seedance 2.0 系列模型进行二次创作,不会触发输入审核拦截。 |
+|[使用预置虚拟人像](/docs/82379/2291680#2bf01416) |平台预置虚拟人像库,为创作者提供免费、合规、丰富多样的肖像素材。适用于需真人风格人脸但无需指定具体人物,追求零合规风险、快速创作的场景。 |
+|[使用已授权真人素材](/docs/82379/2291680#f952d0c3) |支持使用已获得授权的真人肖像素材进行视频生成。 |
+
+
+## 信任模型产物作为输入素材
+Seedance 2.0 系列模型不支持直接上传含有真人人脸的参考图/视频。为了便利创作者在含人脸场景的二次创作需求,方舟平台信任以下模型生成的含人脸产物,您可使用**本账号下近30天内由以下模型生成的含人脸原始产物**,作为输入素材,再次调用 Seedance 2.0 系列模型进行二次创作。
+
+|信任产物范围 |生效时间|有效期|\
+| |> 信任该时间之后生成的产物 |> 从产物生成时间开始计算 |
+|---|---|---|
+|Seedance 2.0 及 Seedance 2.0 Fast 生成的含人脸视频 |2026年03月11日起 |30天 |
+|Seedance 2.0 及 Seedance 2.0 Fast 生成的含人脸视频对应的尾帧图片 |2026年04月16日起 |30天 |
+|[Seedream 5.0 lite 文生图](https://www.volcengine.com/docs/82379/1824121?lang=zh#9695d195)得到的含人脸图片 |2026年04月16日起 |30天 |
+
+:::warning
+
+* 仅信任方舟平台的产物,不支持跨平台使用。
+* 仅信任同账号下的产物,不支持跨账号使用。
+* 仅信任模型原始产物,二次剪辑或超过有效期后均不可使用。
+* 压缩或转发文件易引发信任失效,建议直接将模型原始产物转存至 TOS 使用。
+* 仅对输入的产物进行信任,输出依然有可能因命中方舟安全审核策略而失败,详情参见 [错误码](https://www.volcengine.com/docs/82379/1299023?lang=zh)。
+* 信任仅对命中人脸审核生效,对于不含人脸场景,模型产物不存在受信问题,支持自由剪辑后进行二次创作。
+
+
+:::
+
+|输入:同账号生成的视频 |输出 |
+|---|---|
+|||\
+|||\
+|> [使用预置虚拟人像](/docs/82379/2291680#2bf01416)示例生成的视频 |> 输入:将面霜的颜色修改为白色。|\
+| |> ratio 修改为16:9 |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // 1. Text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // 2. Reference video
+ contents.add(Content.builder()
+ .type("video_url")
+ .videoUrl(CreateContentGenerationTaskRequest.VideoUrl.builder()
+ .url(refVideo)
+ .build())
+ .role("reference_video")
+ .build());
+
+ // Create video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .generateAudio(generateAudio)
+ .model(modelId)
+ .content(contents)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println("Task Created: " + createResult);
+
+ // Get task details and poll status
+ String taskId = createResult.getId();
+ pollTaskStatus(taskId);
+ \}
+
+ /**
+ * Poll task status
+ * @param taskId Task ID
+ */
+
+ private static void pollTaskStatus(String taskId) \{
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ try \{
+ while (true) \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ if (getResponse.getError() != null) \{
+ System.out.println("Error: " + getResponse.getError().getMessage());
+ \}
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...%n", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ \} catch (Exception e) \{
+ System.err.println("Error occurred: " + e.getMessage());
+ \} finally \{
+ service.shutdownExecutor();
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+
+
+## 使用预置虚拟人像
+对写实风格视频,可通过虚拟人像库预置人像来控制角色样貌。每个素材对应一个独立素材 ID (asset ID), 在 **content.<模态\>_url.url** 字段中传入 `asset://` 即可生成视频。
+:::tip
+开通虚拟人像库,浏览及检索虚拟人像请参见[虚拟人像库](/docs/82379/2223965)。
+
+:::
+
+|输入:文本 |输入:虚拟人像、图片 |输出 |
+|---|---|---|
+|固定机位,近景镜头,清新自然风格。在室内自然光下,**图片1**中美妆博主面带笑容,向镜头介绍**图片2**中的面霜。博主将手里的面霜展示给镜头,开心地说“挖到本命面霜了!”;接着她一边用手指轻轻蘸取面霜展示那种软糯感,一边说“质地像云朵一样软糯,一抹就吸收”;最后她把面霜涂抹在脸颊上,展示着水润透亮的皮肤,同时自信地说“熬夜急救、补水保湿全搞定”。要求画面中人物居中,完整展示人物的整个脑袋和上半身,始终对焦人脸,人脸始终清晰,纯净无任何字幕。| ||\
+|:::warning|> 虚拟人像| |\
+|Asset ID 仅用来向模型传入素材,提示词中仍需使用"**素材类型+序号**”格式引用素材,序号为请求体中该素材在同类素材中的排序。|| |\
+|正确用法:**图片1**中美妆博主| | |\
+|错误用法:asset\-2026\*\*\*\*是美妆博主|> 产品图像 | |\
+|| | |\
+|:::| | |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+
+ // 1. Text prompt
+ contents.add(Content.builder()
+ .type("text")
+ .text(prompt)
+ .build());
+
+ // 2. Reference image 1
+ contents.add(Content.builder()
+ .type("image_url")
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+ .url(refImage1)
+ .build())
+ .role("reference_image")
+ .build());
+
+ // 3. Reference image 2
+ contents.add(Content.builder()
+ .type("image_url")
+ .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+ .url(refImage2)
+ .build())
+ .role("reference_image")
+ .build());
+
+ // Create video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .generateAudio(generateAudio)
+ .model(modelId)
+ .content(contents)
+ .ratio(videoRatio)
+ .duration(videoDuration)
+ .watermark(showWatermark)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println("Task Created: " + createResult);
+
+ // Get task details and poll status
+ String taskId = createResult.getId();
+ pollTaskStatus(taskId);
+ \}
+
+ /**
+ * Poll task status
+ * @param taskId Task ID
+ */
+
+ private static void pollTaskStatus(String taskId) \{
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ try \{
+ while (true) \{
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+
+ if ("succeeded".equalsIgnoreCase(status)) \{
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ break;
+ \} else if ("failed".equalsIgnoreCase(status)) \{
+ System.out.println("----- task failed -----");
+ if (getResponse.getError() != null) \{
+ System.out.println("Error: " + getResponse.getError().getMessage());
+ \}
+ break;
+ \} else \{
+ System.out.printf("Current status: %s, Retrying in 10 seconds...%n", status);
+ TimeUnit.SECONDS.sleep(10);
+ \}
+ \}
+ \} catch (InterruptedException ie) \{
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ \} catch (Exception e) \{
+ System.err.println("Error occurred: " + e.getMessage());
+ \} finally \{
+ service.shutdownExecutor();
+ \}
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+
+## 使用已授权真人素材
+通过真人认证和本人授权后,可将该真人的相关素材(例如该真人的图片、视频、音频)上传至方舟。素材入库成功后,每个素材将获得一个独立素材 ID (asset ID), 在 **content.<模态\>_url.url** 字段中传入 `asset://`即可使用该素材生成视频。真人认证及素材入库流程请参见[录入真人形象素材](/docs/82379/2315856)。
+```Shell
+...
+"content": [
+ {
+ "type": "text",
+ "text": ""
+ },
+ {
+ "type": "image_url",
+ "image_url": {
+ "url": "asset://"
+ },
+ "role": "reference_image"
+ },
+ {
+ "type": "video_url",
+ "video_url": {
+ "url": "asset://"
+ },
+ "role": "reference_video"
+ },
+ {
+ "type": "audio_url",
+ "audio_url": {
+ "url": "asset://"
+ },
+ "role": "reference_audio"
+ }
+ ]
+...
+```
+
+
+
+# 提示词技巧
+提示词中必须使用"**素材类型+序号**”格式引用素材,序号为请求体中该素材在同类素材中的排序。例如 「图片 n」指代`content`数组中第 n 个`type="image_url"`的参考图片(按数组顺序从1开始计数)。**注意不支持使用 Asset ID 指代素材。**
+下文介绍多模态参考、编辑视频、延长视频的提示词典型公式,更多详细内容请参见[Doubao Seedance 2.0 系列提示词指南](/docs/82379/2222480)。
+:::tip
+平台提供 **Seedance 2.0 提示词优化技能**,方便您对提示词进行调优。
+
+* 配置方式:可将技能文件配置到 Code Agent / AI Agent 中使用。以 OpenClaw 为例,下载该 SKILL.md 文件,复制完整内容至对话输入框中,并发送”请帮我安装这个技能”,等待工具自动完成安装。
+* 使用方式:在 AI 对话框输入 `/sd2-pe + 你的提示词内容`,开始调试提示词。
+
+SKILL.md
+
+:::
+**多模态参考**
+
+* 图片参考:参考 / 提取 / 结合 +「图片 n」中的「主体 / 被参考元素描述」,生成「画面描述」,保持「主体 / 被参考元素描述」特征一致。
+* 视频参考:参考「视频 n」的「动作描述 / 运镜描述 / 特效描述」,生成「画面描述」,保持动作细节 / 运镜 / 特效一致。
+* 音频参考:
+ * 音色参考:「角色」说:“「台词」,音色参考「音频 n」。
+ * 音频内容参考:理想出现时机 +「音频 n」。
+
+**编辑视频**
+
+* 增加元素:清晰描述「元素特征」+「出现时机」+「出现位置」
+* 删除元素:点明需要删除的元素,对于保持不变的元素,在提示词中加以强调,表现更佳
+* 修改元素:清晰描述更换元素即可
+
+**延长视频**
+
+* 延长视频:向前/向后延长「视频n」+「需延长的视频描述」
+* 轨道补全:「视频1」+「过渡画面描述」+接「视频2」+「过渡画面描述」+接「视频3」
+
+
+# 使用限制
+参见[使用限制](/docs/82379/1366799#66cb028f)。
+
+# 常见问题
+
+## 视频画面存在跳变
+**典型现象**
+**首帧图生视频**、**首尾帧图生视频**场景中,生成视频部分帧出现画面拉伸、压缩等跳变问题。
+**根因分析**
+输入图片与输出视频的分辨率宽高不一致,引发视频画面帧间跳变。
+**解决方案**
+
+1. 裁剪输入图片:参考 Seedance 2.0 系列模型支持的宽高像素值表格(见 [创建视频生成任务 API](https://www.volcengine.com/docs/82379/1520757?lang=zh) ratio 字段),将输入图片裁剪为目标宽高像素值。
+2. 将 API 的 **ratio** 字段设置为`adaptive`。
+3. 使用 Seedance 2.0 系列模型重新发起首帧/首尾帧图生视频任务。
\ No newline at end of file
diff --git a/docs/协议文档/豆包系列/seedream API 文档.md b/docs/协议文档/豆包系列/seedream API 文档.md
new file mode 100644
index 0000000..f8f9e18
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedream API 文档.md
@@ -0,0 +1,570 @@
+`POST https://ark.cn-beijing.volces.com/api/v3/images/generations` [运行](https://api.volcengine.com/api-explorer/?action=ImageGenerations&groupName=%E5%9B%BE%E7%89%87%E7%94%9F%E6%88%90API&serviceCode=ark&version=2024-01-01&tab=2#N4IgTgpgzgDg9gOyhA+gMzmAtgQwC4gBcIArmADYgA0IUAlgF4REgBMA0tSAO74TY4wAayJoc5ZDSxwAJhErEZcEgCMccALTIIMyDiwaALBoAMG1gFYTADlbWuMMHCwwCxQPhmgUTTA-l6Ao2MAw-4CLeYB4tkHBgDOJgE2KgF+KgABygGHxgNf6gPSmgN2egCwegHEegCFugLCagCfKgOhKgGbx-oBFRoBjkYCTkZGA34qA2Ur+gKyugI76gOSagOJO-oDU5oCnpoBHphWA+Ib+gBVKI4Cf2oAr1oBOQf5wAMaATHaAy+b+gJKKgP1+gL-xgFRxY4CABoCEVoBTPv6A9maAj7b+gKGxgA3OgHnagNxygJJy-peAuyH+gNyugEbpgFgJgHH4wBjfoBvQOygAY5QAz2tkZoBLfUAQjqAQmtAIoagAIEp6AZXlAHBygC51c7+QAUsUNAPjuD38gHSzQKAOYzADMB52y6xagAlTQA55oBSELR0UA2DaAF7V-IAXU0xgB9FQDuioAvIMA9OaAbz1AM8GI0AHJqAAn1soB-PUAS5GAeASKmz-IAAAPW-kAs8qAEB1-IBA80AL4GMlr+QBc+oBUfUagDwVQA2aiAAL5AA)
+本文介绍图片生成模型如 Seedream 5.0 lite 的调用 API ,包括输入输出参数,取值范围,注意事项等信息,供您使用接口时查阅字段含义。
+
+**不同模型支持的图片生成能力简介**
+
+* **doubao seedream 5.0 lite==^new^==** **、doubao seedream 4.5/4.0**
+ * 生成组图(组图:基于您输入的内容,生成的一组内容关联的图片;需配置 **sequential_image_generation ** 为`auto` **)**
+ * 多图生组图,根据您输入的 **++多张参考图片(2\-14)++ ** +++文本提示词++ 生成一组内容关联的图片(输入的参考图数量+最终生成的图片数量≤15张)。
+ * 单图生组图,根据您输入的 ++单张参考图片+文本提示词++ 生成一组内容关联的图片(最多生成14张图片)。
+ * 文生组图,根据您输入的 ++文本提示词++ 生成一组内容关联的图片(最多生成15张图片)。
+ * 生成单图(配置 **sequential_image_generation ** 为`disabled` **)**
+ * 多图生图,根据您输入的 **++多张参考图片(2\-14)++ ** +++文本提示词++ 生成单张图片。
+ * 单图生图,根据您输入的 ++单张参考图片+文本提示词++ 生成单张图片。
+ * 文生图,根据您输入的 ++文本提示词++ 生成单张图片。
+
+
+
+```mixin-react
+return (
+
+ [模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#9df4d9fd)  [模型计费](https://www.volcengine.com/docs/82379/1544106?lang=zh#457edfd0)  [API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
+  [调用教程](https://www.volcengine.com/docs/82379/1548482)  [接口文档](https://www.volcengine.com/docs/82379/1666945)  [常见问题](https://www.volcengine.com/docs/82379/1359411)  [开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
+`}>);
+```
+
+
+---
+
+
+
+## 请求参数
+
+### 请求体
+
+---
+
+
+**model** `string` %%require%%
+您需要调用的模型的 ID (Model ID),[开通模型服务](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false),并[查询 Model ID](https://www.volcengine.com/docs/82379/1330310?lang=zh#9df4d9fd) 。
+您也可通过 Endpoint ID 来调用模型,获得限流、计费类型(前付费/后付费)、运行状态查询、监控、安全等高级能力,可参考[获取 Endpoint ID](https://www.volcengine.com/docs/82379/1099522)。
+
+---
+
+
+**prompt ** `string` %%require%%
+用于生成图像的提示词,支持中英文。(查看提示词指南:[Seedream 4.0-5.0 提示词指南](https://www.volcengine.com/docs/82379/1829186))
+建议不超过300个汉字或600个英文单词。字数过多信息容易分散,模型可能因此忽略细节,只关注重点,造成图片缺失部分元素。
+
+---
+
+
+**image** `string/array`
+输入的图片信息,支持 URL 或 Base64 编码。其中,doubao\-seedream\-5.0\-lite/4.5/4.0 支持单图或多图输入([查看多图融合示例](https://www.volcengine.com/docs/82379/1824121?lang=zh#4a35e28f))。
+
+* 图片URL:请确保图片URL可被访问。
+* Base64编码:请遵循此格式`data:image/<图片格式>;base64,`。注意 `<图片格式>` 需小写,如 `data:image/png;base64,`。
+
+:::tip
+
+* 传入单张图片要求:
+ * 图片格式:jpeg、png(doubao\-seedream\-5.0\-lite/4.5/4.0 模型新增支持 webp、bmp、tiff、gif、heic、heif 格式**==^new^==**)
+ * 宽高比(宽/高)范围:
+ * [1/16, 16] (适用模型:doubao\-seedream\-5.0\-lite/4.5/4.0)
+ * 宽高长度(px) \> 14
+ * 大小:不超过 30MB
+ * 总像素:不超过 `6000x6000=36000000` px (对单张图宽度和高度的像素乘积限制,而不是对宽度或高度的单独值进行限制)
+* doubao\-seedream\-5.0\-lite/4.5/4.0 最多支持传入 14 张参考图。
+
+
+:::
+---
+
+
+**size ** `string`
+
+```mixin-react
+return (
+|1:1 |2048x2048 |\\
+|2K| | |\\
+| | | |
+|^^|4:3 |2304x1728 |
+|^^|3:4 |1728x2304 |
+|^^|16:9 |2848x1600 |
+|^^|9:16 |1600x2848 |
+|^^|3:2 |2496x1664 |
+|^^|2:3 |1664x2496 |
+|^^|21:9 |3136x1344 |
+||1:1 |3072x3072 |\\
+|3K
| | |\\
+| | | |
+|^^|4:3 |3456x2592 |
+|^^|3:4 |2592x3456 |
+|^^|16:9 |4096x2304 |
+|^^|9:16 |2304x4096 |
+|^^|2:3 |2496x3744 |
+|^^|3:2 |3744x2496 |
+|^^|21:9 |4704x2016 |
+||1:1 |4096x4096 |\\
+|4K
| | |\\
+| | | |
+|^^|3:4 |3520x4704 |
+|^^|4:3 |4704x3520 |
+|^^|16:9 |5504x3040 |
+|^^|9:16 |3040x5504 |
+|^^|2:3 |3328x4992 |
+|^^|3:2 |4992x3328 |
+|^^|21:9 |6240x2656 |
+
+
+`}>
+|1:1 |2048x2048 |\\
+|2K| | |\\
+| | | |
+|^^|4:3 |2304x1728 |
+|^^|3:4 |1728x2304 |
+|^^|16:9 |2848x1600 |
+|^^|9:16 |1600x2848 |
+|^^|3:2 |2496x1664 |
+|^^|2:3 |1664x2496 |
+|^^|21:9 |3136x1344 |
+||1:1 |4096x4096 |\\
+|4K
| | |\\
+| | | |
+|^^|3:4 |3520x4704 |
+|^^|4:3 |4704x3520 |
+|^^|16:9 |5504x3040 |
+|^^|9:16 |3040x5504 |
+|^^|2:3 |3328x4992 |
+|^^|3:2 |4992x3328 |
+|^^|21:9 |6240x2656 |
+
+
+`}>
+|1:1 |1024x1024 |\\
+|1K| | |\\
+| | | |
+|^^|4:3 |1152x864 |
+|^^|3:4 |864x1152 |
+|^^|16:9 |1280x720 |
+|^^|9:16 |720x1280 |
+|^^|3:2 |1248x832 |
+|^^|2:3 |832x1248 |
+|^^|21:9 |1512x648 |
+||1:1 |2048x2048 |\\
+|2K
| | |\\
+| | | |
+|^^|4:3 |2304x1728 |
+|^^|3:4 |1728x2304 |
+|^^|16:9 |2848x1600 |
+|^^|9:16 |1600x2848 |
+|^^|3:2 |2496x1664 |
+|^^|2:3 |1664x2496 |
+|^^|21:9 |3136x1344 |
+||1:1 |4096x4096 |\\
+|4K
| | |\\
+| | | |
+|^^|3:4 |3520x4704 |
+|^^|4:3 |4704x3520 |
+|^^|16:9 |5504x3040 |
+|^^|9:16 |3040x5504 |
+|^^|2:3 |3328x4992 |
+|^^|3:2 |4992x3328 |
+|^^|21:9 |6240x2656 |
+
+
+`}>
+);
+```
+
+
+---
+
+
+**sequential_image_generation** `string` `默认值 disabled`
+> 仅 doubao\-seedream\-5.0\-lite/4.5/4.0 支持该参数 | [查看组图输出示例](https://www.volcengine.com/docs/82379/1824121?lang=zh#fc9f85e4)
+
+控制是否关闭组图功能。
+:::tip
+组图:基于您输入的内容,生成的一组内容关联的图片。
+
+:::
+* `auto`:自动判断模式,模型会根据用户提供的提示词自主判断是否返回组图以及组图包含的图片数量。
+* `disabled`:关闭组图功能,模型只会生成一张图。
+
+
+---
+
+
+**sequential_image_generation_options ** `object`
+> 仅 doubao\-seedream\-5.0\-lite/4.5/4.0 支持该参数
+
+组图功能的配置。仅当 **sequential_image_generation ** 为 `auto` 时生效。
+
+属性
+
+---
+
+
+sequential_image_generation_options.**max_images ** ** ** `integer` `默认值 15`
+指定本次请求,最多可生成的图片数量。
+
+* 取值范围: [1, 15]
+
+:::tip
+实际可生成的图片数量,除受到 **max_images ** 影响外 **,** 还受到输入的参考图数量影响。**输入的参考图数量+最终生成的图片数量≤15张**。
+
+:::
+
+---
+
+
+**tools==^new^==** ** ** `array of object`
+> 仅 doubao\-seedream\-5.0\-lite 支持该参数
+
+配置模型要调用的工具。
+
+属性
+
+---
+
+
+tools.**type ** `string`
+指定使用的工具类型。
+
+* `web_search`:联网搜索功能。
+
+:::tip
+
+* 开启联网搜索后,模型会根据用户的提示词自主判断是否搜索互联网内容(如商品、天气等),提升生成图片的时效性,但也会增加一定的时延。
+* 实际搜索次数可通过字段 usage.tool_usage.**web_search** 查询,如果为 0 表示未搜索。
+
+:::
+
+---
+
+
+**stream** `Boolean` `默认值 false`
+> 仅 doubao\-seedream\-5.0\-lite/4.5/4.0 支持该参数 | [查看流式输出示例](https://www.volcengine.com/docs/82379/1824121?lang=zh#e5bef0d7)
+
+控制是否开启流式输出模式。
+
+* `false`:非流式输出模式,等待所有图片全部生成结束后再一次性返回所有信息。
+* `true`:流式输出模式,即时返回每张图片输出的结果。在生成单图和组图的场景下,流式输出模式均生效。
+
+
+---
+
+
+**guidance_scale ** `Float`
+> doubao\-seedream\-5.0\-lite/4.5/4.0 不支持
+
+模型输出结果与prompt的一致程度,生成图像的自由度,又称为文本权重;值越大,模型自由度越小,与用户输入的提示词相关性越强。
+取值范围:[`1`, `10`] 。
+
+---
+
+
+**output_format==^new^==**`string` `默认值 jpeg`
+> 仅 doubao\-seedream\-5.0\-lite 支持该参数
+
+指定生成图像的文件格式。可选值:
+
+* `png`
+* `jpeg`
+
+:::tip
+doubao\-seedream\-4.5/4.0 模型生成图像的文件格式默认为 jpeg,不支持自定义设置。
+
+:::
+---
+
+
+**response_format** `string` `默认值 url`
+指定生成图像的返回格式。支持以下两种返回方式:
+
+* `url`:返回图片下载链接;**链接在图片生成后24小时内有效,请及时下载图片。**
+* `b64_json`:以 Base64 编码字符串的 JSON 格式返回图像数据。
+
+
+---
+
+
+**watermark** `Boolean` `默认值 true`
+是否在生成的图片中添加水印。
+
+* `false`:不添加水印。
+* `true`:在图片右下角添加“AI生成”字样的水印标识。
+
+
+---
+
+
+**optimize_prompt_options ** `object`
+> 仅 doubao\-seedream\-5.0\-lite/4.5/4.0 支持该参数
+
+提示词优化功能的配置。
+
+属性
+optimize_prompt_options.**mode ** `string` `默认值 standard`
+设置提示词优化功能使用的模式。
+
+* `standard`:标准模式,生成内容的质量更高,耗时较长。
+* `fast`:快速模式,生成内容的耗时更短,质量一般;doubao\-seedream\-5.0\-lite/4.5 当前不支持。
+
+
+---
+
+
+
+
+## 响应参数
+
+### 流式响应参数
+请参见[文档](https://www.volcengine.com/docs/82379/1824137?lang=zh)。
+
+
+### 非流式响应参数
+
+---
+
+
+**model** `string`
+本次请求使用的模型 ID (`模型名称-版本`)。
+
+---
+
+
+**created** `integer`
+本次请求创建时间的 Unix 时间戳(秒)。
+
+---
+
+
+**data** `array`
+输出图像的信息。
+:::tip
+doubao\-seedream\-5.0\-lite/4.5/4.0 模型生成组图场景下,组图生成过程中某张图生成失败时:
+
+* 若失败原因为审核不通过:仍会继续请求下一个图片生成任务,即不影响同请求内其他图片的生成流程。
+* 若失败原因为内部服务异常(500):不会继续请求下一个图片生成任务。
+
+
+:::
+可能类型
+图片信息 `object`
+生成成功的图片信息。
+
+属性
+data.**url ** `string`
+图片的 url 信息,当 **response_format ** 指定为 `url` 时返回。该链接将在生成后 **24 小时内失效**,请务必及时保存图像。
+推荐配置火山引擎 TOS 提供的数据订阅功能,将您的模型推理产物自动转存到自己的 TOS 桶中,便于长期备份或二次加工。详细介绍请参见 [TOS 数据订阅](https://www.volcengine.com/docs/6349/2280949?lang=zh)。
+
+---
+
+
+data.**b64_json** `string`
+图片的 base64 信息,当 **response_format ** 指定为 `b64_json` 时返回。
+
+---
+
+
+data.**size** `string`
+> 仅 doubao\-seedream\-5.0\-lite/4.5/4.0 支持该字段。
+
+图像的宽高像素值,格式 `<宽像素>x<高像素>`,如`2048×2048`。
+
+
+---
+
+
+错误信息 `object`
+某张图片生成失败,错误信息。
+
+属性
+data.**error** `object`
+错误信息结构体。
+
+属性
+
+---
+
+
+data.error.**code**
+某张图片生成错误的错误码,请参见[错误码](https://www.volcengine.com/docs/82379/1299023)。
+
+---
+
+
+data.error.**message**
+某张图片生成错误的提示信息。
+
+
+
+
+---
+
+
+**tools** `array of object`
+本次请求,配置的模型调用工具
+
+属性
+
+---
+
+
+tools.**type ** `string`
+配置的调用工具类型。
+
+* web_search:联网搜索工具。
+
+
+---
+
+
+**usage** `object`
+本次请求的用量信息。
+
+属性
+
+---
+
+
+usage.**generated_images ** `integer`
+模型成功生成的图片张数,不包含生成失败的图片。
+仅对成功生成图片按张数进行计费。
+
+---
+
+
+usage.**output_tokens** `integer`
+模型生成的图片花费的 token 数量。
+计算逻辑为:计算 `sum(图片长*图片宽)/256` ,然后取整。
+
+---
+
+
+usage.**total_tokens** `integer`
+本次请求消耗的总 token 数量。
+当前不计算输入 token,故与 **output_tokens** 值一致。
+
+---
+
+
+usage.**tool_usage ** `object`
+使用工具的用量信息。
+
+属性
+
+---
+
+
+usage.tool_usage.**web_search ** `integer`
+调用联网搜索工具次数,仅开启联网搜索时返回。
+
+
+
+---
+
+
+**error** `object`
+本次请求,如发生错误,对应的错误信息。
+
+属性
+
+---
+
+
+error.**code** `string`
+请参见[错误码](https://www.volcengine.com/docs/82379/1299023)。
+
+---
+
+
+error.**message** `string`
+错误提示信息
+
+
+
+
diff --git a/docs/协议文档/豆包系列/seedream教程.md b/docs/协议文档/豆包系列/seedream教程.md
new file mode 100644
index 0000000..0d2a110
--- /dev/null
+++ b/docs/协议文档/豆包系列/seedream教程.md
@@ -0,0 +1,2155 @@
+Seedream 4.0\-5.0 原生支持文本、单图和多图输入,实现基于主体一致性的多图融合创作、图像编辑、组图生成等多样玩法,让图像创作更加自由可控。本文以 Seedream 5.0 lite 为例介绍如何调用 [Image generation API](https://www.volcengine.com/docs/82379/1541523) 进行图像创作。如需使用 Seedream 4.5/4.0 模型,将下文代码示例中的 model 字段替换为对应的 Model ID 即可。
+:::tip
+方舟平台的新用户?获取 API Key 及 开通模型等准备工作,请参见 [快速入门](/docs/82379/1399008)。
+:::
+
+# 模型效果
+更多效果示例见 [效果预览](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedream-5-0)。
+
+
+|场景 |输入 |输出 |
+|---|---|---|
+|文生图 `联网搜索`|制作一张上海未来5日的天气预报图,采用现代扁平化插画风格,清晰展示每日天气、温度和穿搭建议。 整体为横向排版,标题为“上海未来5日天气预报“,包含5个等宽的垂直卡片,从左到右依次排列。 整体风格为现代、干净、友好的扁平化矢量插画风格,线条清晰,色彩柔和。 人物形象采用年轻男女的卡通插画,表情自然,姿态放松,服装细节清晰。 | |\
+|> Seedream 5.0 lite 模型可通过联网搜索功能,融合实时网络信息,提升生图时效性。 | | |
+|多参考图生图| | |\
+|> 输入多张参考图,融合它们的风格、元素等特征来生成新图像。 |> 将图1的服装换为图2的服装 | |
+|组图生成| | |\
+|> 基于用户输入的文字和图片,生成一组内容关联的图像 |> 参考图1,生成四图片,图中人物分别带着墨镜,骑着摩托,带着帽子,拿着棒棒糖 | |
+
+
+# 模型能力
+
+
+|模型名称 ||[Doubao-Seedream-5.0-lite](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedream-5-0) |[Doubao-Seedream-4.5](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedream-4-5) |[Doubao-Seedream-4.0](https://console.volcengine.com/ark/region:ark+cn-beijing/model/detail?Id=doubao-seedream-4-0) |
+|---|---|---|---|---|
+|模型 ID (Model ID) ||doubao\-seedream\-5\-0\-260128 (同时支持:doubao\-seedream\-5\-0\-lite\-260128) |doubao\-seedream\-4\-5\-251128 |doubao\-seedream\-4\-0\-250828 |
+|文生图 || | | |
+|文生组图 || | | |
+|单 / 多图生图 || | | |
+|单 / 多图生组图 || | | |
+|流式输出 || | | |
+|联网搜索 || | | |
+|模型参数 |分辨率 |2K, 3K, 4K |2K, 4K |1K, 2K, 4K |
+|^^|输出格式 |png, jpeg |jpeg |jpeg |
+|^^|提示词优化模式 |标准模式 |标准模式 |标准模式, 极速模式 |
+|^^|生成数量 |输入的参考图数量 + 最终生成的图片数量 ≤ 15张 |||
+|限流 IPM(张 / 分钟) ||500 |500 |500 |
+
+
+# 快速体验
+您可在火山方舟平台 [API Explorer](https://api.volcengine.com/api-explorer/?action=ImageGenerations&groupName=%E5%9B%BE%E7%89%87%E7%94%9F%E6%88%90API&serviceCode=ark&tab=2&version=2024-01-01#N4IgTgpgzgDg9gOyhA+gMzmAtgQwC4gBcIArmADYgA0IUAlgF4REgBMA0tSAO74TY4wAayJoc5ZDSxwAJhErEZcEgCMccALTIIMyDiwaALBoAMG1gFYTADlbWuMMHCwwCxQPhmgUTTA-l6Ao2MAw-4CLeYB4tkHBgDOJgE2KgF+KgABygGHxgNf6gPSmgN2egCwegHEegCFugLCagCfKgOhKgGbx-oBFRoBjkYCTkZGA34qA2Ur+gKyugI76gOSagOJO-oDU5oCnpoBHphWA+Ib+gBVKI4Cf2oAr1oBOQf5wAMaATHaAy+b+gJKKgP1+gL-xgFRxY4CABoCEVoBTPv6A9maAj7b+gKGxgA3OgHnagNxygJJy-peAuyH+gNyugEbpgFgJgHH4wBjfoBvQOygAY5QAz2tkZoBLfUAQjqAQmtAIoagAIEp6AZXlAHBygC51c7+QAUsUNAPjuD38gHSzQKAOYzADMB52y6xagAlTQA55oBSELR0UA2DaAF7V-IAXU0xgB9FQDuioAvIMA9OaAbz1AM8GI0AHJqAAn1soB-PUAS5GAeASKmz-IAAAPW-kAs8qAEB1-IBA80AL4GMlr+QBc+oBUfUagDwVQA2aiAAL5AA),快速体验图片生成功能,支持自定义参数(例如设置图片水印、控制输出图片大小等),方便您直观感受其效果和性能。
+
+
+# 基础使用
+
+## 文生图(纯文本输入单图输出)
+通过给模型提供清晰准确的文字指令,即可快速获得符合描述的高质量单张图片。
+
+
+|提示词 |输出 |
+|---|---|
+|充满活力的特写编辑肖像,模特眼神犀利,头戴雕塑感帽子,色彩拼接丰富,眼部焦点锐利,景深较浅,具有Vogue杂志封面的美学风格,采用中画幅拍摄,工作室灯光效果强烈。 | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+## 图文生图(单图输入单图输出)
+基于已有图片,结合文字指令进行图像编辑,包括图像元素增删、风格转化、材质替换、色调迁移、改变背景/视角/尺寸等。
+
+
+|提示词 |输入图 |输出 |
+|---|---|---|
+|保持模特姿势和液态服装的流动形状不变。将服装材质从银色金属改为完全透明的清水(或玻璃)。透过液态水流,可以看到模特的皮肤细节。光影从反射变为折射。 | | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+## 多图融合(多图输入单图输出)
+根据您输入的文本描述和多张参考图片,融合它们的风格、元素等特征来生成新图像。如衣裤鞋帽与模特图融合成穿搭图,人物与风景融合为人物风景图等。
+
+
+|提示词 |输入图1 |输入图2 |输出 |
+|---|---|---|---|
+|将图1的服装换为图2的服装 | | | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+## 组图输出(多图输出)
+支持通过一张或者多张图片和文字信息,生成漫画分镜、品牌视觉等一组内容关联的图片。
+需指定参数 **sequential_image_generation** 为`auto`。
+
+### 文生组图
+
+
+|提示词 |输出(实际会输出4张图片) |
+|---|---|
+|生成一组电影级科幻写实风的4张影视分镜:| |\
+|场景1为宇航员在空间站维修飞船,空间站外部精密机械结构,深邃星空 + 银河背景,宇航员身穿细节完整的白色宇航服,手持专业维修工具,专注检修飞船外壳,中全景构图,侧逆光勾勒轮廓,冷色调科幻光影,空间站灯光点缀,失重环境,金属质感细腻,画面静谧严谨。| |\
+|场景2为:突然遇到陨石带袭击,广角史诗镜头,大量大小不一的陨石高速袭来,陨石表面纹理清晰,带燃烧尾焰,动态模糊体现速度感,陨石带压迫感拉满,飞船与空间站在画面一侧,太空黑暗深邃,光影强烈对比,紧张灾难氛围,画面冲击力十足。| |\
+|场景3为:宇航员紧急躲避,近景动态抓拍,宇航员失重状态下极速侧身躲避,肢体动作张力拉满,伸手抓握固定扶手,背景陨石飞掠而过,轻微镜头晃动增强临场感,宇航服褶皱、管线细节清晰,急促紧张,冷冽光影,主体突出不杂乱。| |\
+|场景4为:受伤后惊险逃回飞船,中近景叙事镜头,宇航员宇航服带轻微破损划痕,略显狼狈却坚毅,踉跄冲向开启的飞船舱门,舱内暖光与太空冷光形成对比,背景陨石逐渐远去,惊险逃生氛围,细节真实,情绪饱满。 | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+### 单张图生组图
+
+
+|提示词 |输入图 |输出(实际会输出4张图片) |
+|---|---|---|
+|参考这个LOGO,做一套户外运动品牌视觉设计,品牌名称为“GREEN”,包括包装袋、帽子、卡片、挂绳等。绿色视觉主色调,趣味、简约现代风格。 | | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+### 多参考图生组图
+
+
+|提示词 |输入图1 |输入图2 |输出(实际会输出3张图片) |
+|---|---|---|---|
+|生成3张女孩和奶牛玩偶在游乐园开心地坐过山车的图片,涵盖早晨、中午、晚上 | | | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+## **提示词建议**
+
+* 建议用**简洁连贯**的自然语言写明 **主体 + 行为 + 环境**,若对画面美学有要求,可用自然语言或短语补充 **风格**、**色彩**、**光影**、**构图** 等美学元素。详情可参见 [Seedream 4.0-5.0 提示词指南](/docs/82379/1829186)。
+* 文本提示词(prompt)建议不超过300个汉字或600个英文单词。字数过多信息容易分散,模型可能因此忽略细节,只关注重点,造成图片缺失部分元素。
+
+
+# 进阶使用
+
+## 联网搜索
+Seedream 5.0 lite 新增支持调用联网搜索工具,通过配置 tools.**type** 参数为 `web_search` 即可开启联网搜索。
+
+* 开启联网搜索后,模型会根据用户的提示词自主判断是否搜索互联网内容(如商品、天气等),提升生成图片的时效性,但也会增加一定的时延。
+* 实际搜索次数可通过字段 usage.tool_usage.**web_search** 查询,如果为 **0** 表示未搜索。
+
+
+
+|提示词 |输出 |
+|---|---|
+|制作一张上海未来5日的天气预报图,采用现代扁平化插画风格,清晰展示每日天气、温度和穿搭建议。 整体为横向排版,标题为“上海未来5日天气预报”,包含5个等宽的垂直卡片,从左到右依次排列。 整体风格为现代、干净、友好的扁平化矢量插画风格,线条清晰,色彩柔和。人物形象采用年轻男女的卡通插画,表情自然,姿态放松,服装细节清晰。 | |
+
+
+```mixin-react
+return (
+
+
+ tools = Arrays.asList(contentGenerationTool);
+
+ GenerateImagesRequest generateRequest = GenerateImagesRequest.builder()
+ .model("doubao-seedream-5-0-260128") // Replace with Model ID
+ .prompt("制作一张上海未来5日的天气预报图,采用现代扁平化插画风格,清晰展示每日天气、温度和穿搭建议。 整体为横向排版,标题为“上海未来5日天气预报”,包含5个等宽的垂直卡片,从左到右依次排列。 整体风格为现代、干净、友好的扁平化矢量插画风格,线条清晰,色彩柔和。人物形象采用年轻男女的卡通插画,表情自然,姿态放松,服装细节清晰。")
+ .size("2048x2048")
+ .outputFormat("png")
+ .responseFormat("url")
+ .watermark(false)
+ .tools(tools)
+ .build();
+
+ ImagesResponse imagesResponse = service.generateImages(generateRequest);
+ System.out.println(imagesResponse.getData().get(0).getUrl());
+
+ service.shutdownExecutor();
+ \}
+\}
+\`\`\`
+
+`}>
+);
+```
+
+
+## 流式输出
+模型支持流式图像生成,当生成完任一图片后即返回结果,让您能更快浏览到生成的图像,改善等待体验。
+通过配置 **stream** 参数为`true`,即可开启流式输出模式。
+
+
+```mixin-react
+return (
+
+
+ \{
+ if (choice == null) return;
+ if ("image_generation.partial_failed".equals(choice.getType())) \{
+ if (choice.getError() != null) \{
+ System.err.println("Stream generate images error: " + choice.getError());
+ if (choice.getError().getCode() != null && choice.getError().getCode().equals("InternalServiceError")) \{
+ throw new RuntimeException("Server error, terminating stream.");
+ \}
+ \}
+ \}
+ else if ("image_generation.partial_succeeded".equals(choice.getType())) \{
+ if (choice.getError() == null && choice.getUrl() != null && !choice.getUrl().isEmpty()) \{
+ System.out.printf("recv.Size: %s, recv.Url: %s%n", choice.getSize(), choice.getUrl());
+ \}
+ \}
+ else if ("image_generation.completed".equals(choice.getType())) \{
+ if (choice.getError() == null && choice.getUsage() != null) \{
+ System.out.println("recv.Usage: " + choice.getUsage().toString());
+ \}
+ \}
+ \}
+ );
+ service.shutdownExecutor();
+ \}
+\}
+\`\`\`
+
+`}>
+
+);
+```
+
+
+## 提示词优化控制
+通过设置 **optimize_prompt_options.mode** 参数,您可以在 `standard` 模式和 `fast` 模式之间进行选择,以根据自身对图片质量和生成速度的不同需求来优化提示词。
+
+* 为平衡生成速度与图像质量,Seedream 4.0 支持将 **optimize_prompt_options.mode** 设置为 `fast` 模式以显著提升生成速度,但会在一定程度上牺牲图片质量。
+* Seedream 5.0 lite/4.5 专注于高质量图片输出,仅支持 `standard` 模式。
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+## 自定义图片输出规格
+您可以配置以下参数来控制图片输出规格:
+
+* **size** :指定输出图像的尺寸大小。
+* **response_format** :指定生成图像的返回格式。
+* **output_format:** 指定生成图像的文件格式。
+* **watermark** :指定是否为输出图片添加水印。
+
+
+### 图像输出尺寸
+支持两种尺寸设置方式,不可混用。
+
+* 方式 1 :指定生成图像的分辨率,并在 prompt 中用自然语言描述图片宽高比、图片形状或图片用途,最终由模型判断生成图片的大小。可选值:
+ * Seedream 5.0 lite:`2K`、`3K`、`4K`
+ * Seedream 4.5:`2K`、`4K`
+ * Seedream 4.0:`1K`、`2K`、`4K`
+* 方式2 :指定生成图像的宽高像素值。
+ * 默认值:`2048x2048`
+ * 宽高比取值范围:[1/16, 16]
+ * 总像素取值范围:
+ * Seedream 5.0 lite:[`2560x1440=3686400`, `4096x4096=16777216`]
+ * Seedream 4.5:[`2560x1440=3686400`, `4096x4096=16777216`]
+ * Seedream 4.0:[`1280x720=921600`, `4096x4096=16777216`]
+
+
+
+|方式1 |方式2 |
+|---|---|
+|```JSON|```JSON|\
+|{|{|\
+| "prompt": "生成一组共4张海报,核心为同一庭院一角的四季变迁,以统一风格展现四季独特色彩、元素与氛围", // prompt 中用自然语言描述图片宽高比、图片形状或图片用途| "prompt": "生成一组共4张连贯插画,核心为同一庭院一角的四季变迁,以统一风格展现四季独特色彩、元素与氛围",|\
+| "size": "2K" // 通过参数 size 指定生成图像的分辨率| "size": "2048x2048" // 通过参数 size 指定生成图像的宽高像素值|\
+|}|}|\
+|```|```|\
+| | |
+
+推荐宽高像素值:
+
+
+| |1K |2K |3K |4K |
+|---|---|---|---|---|
+|Seedream 5.0 lite |暂不支持 |`1:1`:2048x2048|`1:1`:3072x3072|`1:1`:4096x4096|\
+| | |`3:4`:1728x2304|`3:4`:2592x3456|`3:4`:3520x4704|\
+| | |`4:3`:2304x1728|`4:3`:3456x2592|`4:3`:4704x3520|\
+| | |`16:9`:2848x1600|`16:9`:4096x2304|`16:9`:5504x3040|\
+| | |`9:16`:1600x2848|`9:16`:2304x4096|`9:16`:3040x5504|\
+| | |`3:2`:2496x1664|`2:3`:2496x3744|`2:3`:3328x4992|\
+| | |`2:3`:1664x2496|`3:2`:3744x2496|`3:2`:4992x3328|\
+| | |`21:9`:3136x1344 |`21:9`:4704x2016 |`21:9`:6240x2656 |
+|Seedream 4.5 |暂不支持 |`1:1`:2048x2048|暂不支持 |`1:1`:4096x4096|\
+| | |`3:4`:1728x2304| |`3:4`:3520x4704|\
+| | |`4:3`:2304x1728| |`4:3`:4704x3520|\
+| | |`16:9`:2848x1600| |`16:9`:5504x3040|\
+| | |`9:16`:1600x2848| |`9:16`:3040x5504|\
+| | |`3:2`:2496x1664| |`2:3`:3328x4992|\
+| | |`2:3`:1664x2496| |`3:2`:4992x3328|\
+| | |`21:9`:3136x1344 | |`21:9`:6240x2656 |
+|Seedream 4.0 |`1:1`:1024x1024|`1:1`:2048x2048|暂不支持 |`1:1`:4096x4096|\
+| |`3:4`:864x1152|`3:4`:1728x2304| |`3:4`:3520x4704|\
+| |`4:3`:1152x864|`4:3`:2304x1728| |`4:3`:4704x3520|\
+| |`16:9`:1312x736|`16:9`:2848x1600| |`16:9`:5504x3040|\
+| |`9:16`:736x1312|`9:16`:1600x2848| |`9:16`:3040x5504|\
+| |`2:3`:832x1248|`3:2`:2496x1664| |`2:3`:3328x4992|\
+| |`3:2`:1248x832|`2:3`:1664x2496| |`3:2`:4992x3328|\
+| |`21:9`:1568x672 |`21:9`:3136x1344 | |`21:9`:6240x2656 |
+
+
+### 图像输出方式
+通过设置 **response_format** 参数,可以指定生成图像的返回方式:
+
+* `url`:返回图片下载链接。
+* `b64_json`:以 Base64 编码字符串的 JSON 格式返回图像数据。
+
+```JSON
+{
+ "response_format": "url"
+}
+```
+
+
+### 图像文件格式
+Seedream 4.5/4.0 生成的图像格式默认为`jpeg`,不支持自定义设置。
+Seedream 5.0 lite 可通过设置 **output_format** 参数,指定生成图像文件的格式:
+
+* `png`
+* `jpeg`
+
+```JSON
+{
+ "output_format": "png"
+}
+```
+
+
+### 图像中添加水印
+通过设置 **watermark** 参数,来控制是否在生成的图片中添加水印。
+
+* `false`:不添加水印。
+* `true`:在图片右下角添加“AI生成”字样的水印标识。
+
+```JSON
+{
+ "watermark": true
+}
+```
+
+
+# 使用限制
+**SDK 版本升级**
+为保证模型功能的正常使用,请务必升级至最新 SDK 版本。相关步骤可参考 [安装及升级 SDK](/docs/82379/1541595)。
+**图片传入限制**
+
+* 图片格式:jpeg、png、webp、bmp、tiff、gif、heic、heif
+* 图片传入方式:
+ * 图片 URL:请确保图片 URL 可被访问。
+ 示例:`https://ark-project.tos-cn-beijing.volces.com/doc_image/seedream4_5_imageToimage.png`
+ * Base64 编码:请遵循格式`data:image/<图片格式>;base64,`。注意:`<图片格式>` 必须采用小写字母,例如 `data:image/png;base64,`。
+ 如需获得图片的 Base64 编码,可使用第三方工具,例如 https://base64.guru/converter/encode/image。
+* 宽高比(宽/高)范围:[1/16, 16]
+* 宽高长度(px) \> 14
+* 大小:不超过 30 MB
+* 总像素:不超过 `6000x6000=36000000` px (对单张图宽度和高度的像素乘积限制,而不是对宽度或高度的单独值进行限制)
+* 最多支持传入 14 张参考图
+
+**保存时间**
+任务数据(如任务状态、图片URL等)仅保留24小时,超时后会被自动清除。请您务必及时保存生成的图片。
+**限流说明**
+
+* RPM 限流:账号下同模型(区分模型版本)每分钟生成图片数量上限。若超过该限制,生成图片时会报错。
+* 不同模型的限制值不同,详见 [图片生成能力](/docs/82379/1330310#d3e5e0eb)。
+
+
+# 附:故事书/连环画制作
+[火山方舟大模型体验中心](https://www.volcengine.com/experience/ark?mode=vision&model=doubao-seedream-4-0-250828) 提供了故事书和连环画功能,该功能结合了 doubao\-seed\-1.6 模型和 doubao\-seedream\-4.0 模型,可实现一句话生成动漫、连环画、故事书,满足用户多样化的创作需求。
+连环画的实现过程与故事书类似,本文以故事书为例,为您介绍生成故事书的工作流和技术实现步骤,方便您在本地快速复现。
+
+
+## 工作流
+故事书生成的工作流如下:
+
+
+## 技术实现
+
+1. 根据用户提供的提示词和参考图,调用 doubao\-seed\-1.6 模型,进行故事创作 \> 故事分镜拆解 \> 生成分镜的文案和画面描述 \> 生成书名 \> 生成故事总结,并汇总成 JSON 格式输出。
+
+System Prompt 如下:
+```Plain
+# 角色
+
+你是一位**绘本创作大师**。
+
+## 任务
+
+贴合用户指定的**读者群(儿童/青少年/成人/全年龄)**,创作**情节线性连贯的、生动有趣的、充满情绪价值和温度的、有情感共鸣的、分镜-文案-画面严格顺序对应的绘本内容**:
+- 核心约束:**分镜拆分→文案(scenes)→画面描述(scenes_detail)必须1:1顺序绑定**,从故事开头到结尾,像「放电影」一样按时间线推进,绝无错位。
+
+## 工作流程
+
+1. 充分理解用户诉求。 优先按照用户的创作细节要求执行(如果有)
+2. **故事构思:** 创作一个能够精准回应用户诉求、提供情感慰藉的故事脉络。整个故事必须围绕“共情”和“情绪价值”展开。
+3. **分镜结构与数量:**
+ * 将故事浓缩成 **5~10** 个关键分镜,最多10个(不能超过10个)。
+ * 必须遵循清晰的叙事弧线:开端 → 发展 → 高潮 → 结局。
+4. **文案与画面 (一一对应):**
+ * **文案 ("scenes"字段):** 为每个分镜创作具备情感穿透力的文案。文案必须与画面描述紧密贴合,共同服务于情绪的传递。**禁止在文案中使用任何英文引号 ("")**。不能超过10个。
+ * **画面 ("scenes_detail"字段):** 为每个分镜构思详细的画面。画风必须贴合用户诉求和故事氛围。描述需包含构图、光影、色彩、角色神态等关键视觉要素,达到可直接用于图片生成的标准。
+5. **书名 ("title"字段):**
+ * 构思一个简洁、好记、有创意的书名。
+ * 书名必须能巧妙地概括故事精髓,并能瞬间“戳中”目标用户的情绪共鸣点。
+6. **故事总结 ("summary"字段):**
+ * 创作一句**不超过30个汉字**的总结。
+ * 总结需高度凝练故事的核心思想与情感价值。
+7. 整合输出:将所有内容按指定 JSON 格式整理输出。
+
+## 安全限制
+生成的内容必须严格遵守以下规定:
+1. **禁止暴力与血腥:** 不得包含任何详细的暴力、伤害、血腥或令人不适的画面描述。
+2. **禁止色情内容:** 不得包含任何色情、性暗示或不适宜的裸露内容。
+3. **禁止仇恨与歧视:** 不得包含针对任何群体(基于种族、宗教、性别、性取向等)的仇恨、歧视或攻击性言论。
+4. **禁止违法与危险行为:** 不得描绘或鼓励任何非法活动、自残或危险行为。
+5. **确保普遍适宜性:** 整体内容应保持在社会普遍接受的艺术创作范围内,避免极端争议性话题。
+
+## 输出格式要求
+整理成以下JSON格式,scenes 和 scenes_detail 要与分镜保持顺序一致,一一对应,最多10个(不能超过10个):
+{
+ "title": "书名",
+ "summary": "30字内的总结",
+ "scenes": [
+ "分镜1的文案,用50字篇幅传递情绪和情感,引发读者共鸣,语言风格需符合设定。",
+ "分镜2的文案"
+ ],
+ "scenes_detail": [
+ "图片1:这是第一页的画面描述。必须以'图片'+序号开头。要有强烈的视觉感,详细描述构图(如特写、远景)、光影、色彩、角色表情、动作和环境细节,符合生图提示词的要求。",
+ "图片2:"
+ ]
+}
+```
+
+
+2. 提取返回结果 JSON 中的 scenes_detail 字段,作为图片生成的 Prompt 。
+3. 处理图片生成的 Prompt:
+ 1. 将数组转化成字符串
+ 2. 在 prompt 末尾补充"最后,为故事书创作一个封面。 再检查所有图片,去除图片中的文字"。
+ 3. 在 prompt 开头添加用户输入的提示词。
+4. 根据图片生成的 Prompt 和用户提供的参考图,调用 doubao\-seedream\-4.0 模型的生成组图能力,为故事的所有分镜文案生成配图。
+5. 按照顺序拼装图片和文字即可得到故事书内容 ,用户按需进行展示即可。
+
+
+
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index 0551a57..da3c3f5 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -358,6 +358,13 @@ func registerRelayRoutes(server *ghttp.Server) {
group.GET("/video/generations/{task_id}", relay.HandleTaskFetch)
})
+ // 异步图片生成端点(阿里云 DashScope 等)
+ server.Group("/v1", func(group *ghttp.RouterGroup) {
+ group.Middleware(middleware.ApiMaintenance, middleware.MaintenanceMode, middleware.ApiKeyAuth, middleware.ContentFilter)
+ group.POST("/images/generations/async", relay.HandleAliImageSubmit)
+ group.GET("/images/generations/async/{task_id}", relay.HandleTaskFetch)
+ })
+
// Suno 端点
server.Group("/suno", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.ApiMaintenance, middleware.MaintenanceMode, middleware.ApiKeyAuth, middleware.ContentFilter)
diff --git a/internal/handler/relay/task.go b/internal/handler/relay/task.go
index 1caffe9..5a3b265 100644
--- a/internal/handler/relay/task.go
+++ b/internal/handler/relay/task.go
@@ -154,6 +154,40 @@ func HandleMjSubmit(r *ghttp.Request) {
)
}
+// HandleAliImageSubmit 处理异步图片生成任务提交(POST /v1/images/generations/async)
+func HandleAliImageSubmit(r *ghttp.Request) {
+ body := r.GetBody()
+ if len(body) == 0 {
+ r.Response.WriteStatus(400, g.Map{
+ "error": g.Map{"type": "invalid_request_error", "message": "request body is empty"},
+ })
+ return
+ }
+
+ rc := &relay_handler.TaskRelayContext{
+ TenantID: middleware.GetTenantID(r.Context()),
+ UserID: middleware.GetUserID(r.Context()),
+ ApiKeyID: middleware.GetApiKeyID(r.Context()),
+ RequestID: r.GetCtxVar("RequestId").String(),
+ Writer: r.Response.Writer,
+ Scope: r.GetCtxVar("ApiKeyScope").String(),
+ ClientIP: r.GetClientIp(),
+ }
+
+ channelMeta, err := selectTaskChannel(r, body)
+ if err != nil {
+ r.Response.WriteStatus(503, g.Map{
+ "error": g.Map{"type": "server_error", "message": err.Error()},
+ })
+ return
+ }
+
+ relay_handler.HandleTaskSubmit(
+ r.Context(), body, r.URL.Path, r.Header,
+ rc, taskDataProvider, taskBillingProvider, channelMeta,
+ )
+}
+
// HandleMjFetch 处理 Midjourney 任务查询(GET /mj/task/:id/fetch)
func HandleMjFetch(r *ghttp.Request) {
taskID := r.Get("task_id").String()
diff --git a/internal/logic/billing/task_billing.go b/internal/logic/billing/task_billing.go
index ae99aac..ff071d2 100644
--- a/internal/logic/billing/task_billing.go
+++ b/internal/logic/billing/task_billing.go
@@ -16,28 +16,47 @@ func NewTaskBillingProvider() common.TaskBillingProvider {
}
// EstimateTaskCost 估算任务费用
-// ratios 包含计费比率(如 duration_ratio, resolution_ratio)
+// ratios 包含计费比率(如 video_input 折扣)和预估参数(如 duration 秒数、resolution 乘数)
func (b *TaskBillingProviderImpl) EstimateTaskCost(ctx context.Context, tenantID int64, modelName string, ratios map[string]float64) (float64, error) {
- // 基础价格从模型定价表获取
pricing, err := GetModelPrice(ctx, tenantID, modelName)
if err != nil {
return 0.01, nil
}
- baseCost := pricing.PerRequestPrice
- if err != nil {
- return 0, fmt.Errorf("estimate task cost: %w", err)
+
+ var cost float64
+
+ // 如果配了 token 单价且有 duration 预估,用 token 模式估算
+ if pricing.OutputPrice > 0 {
+ duration := 5.0 // 默认 5 秒
+ if d, ok := ratios["duration"]; ok && d > 0 {
+ duration = d
+ }
+ resolutionMul := 2.25 // 默认 720p
+ if r, ok := ratios["resolution"]; ok && r > 0 {
+ resolutionMul = r
+ }
+
+ // 预估 tokens ≈ base_tokens_per_second × duration × resolution_multiplier
+ // base_tokens_per_second 根据模型定价反推:
+ // 火山方舟 completion_tokens 约 100 tokens/s (480p),用于预扣估算
+ estimatedTokens := 100.0 * duration * resolutionMul
+ cost = estimatedTokens / 1_000_000.0 * pricing.OutputPrice * pricing.TenantMultiplier
+ } else {
+ cost = pricing.PerRequestPrice
}
- // 应用比率乘数
- for _, ratio := range ratios {
- baseCost *= ratio
+ // 应用附加比率(video_input 折扣等)
+ for k, ratio := range ratios {
+ if k == "duration" || k == "resolution" {
+ continue
+ }
+ cost *= ratio
}
- // 最低消费保障
- if baseCost < 0.01 {
- baseCost = 0.01
+ if cost < 0.01 {
+ cost = 0.01
}
- return baseCost, nil
+ return cost, nil
}
// PreDeductTask 预扣任务费用
@@ -102,3 +121,40 @@ func (b *TaskBillingProviderImpl) AdjustTaskBilling(ctx context.Context, tenantI
}
return newEstimatedCost, nil
}
+
+// RecalculateByTokens 根据上游返回的 total_tokens 重算费用。
+// 公式:totalTokens / 1M × output_price × ratios_product
+// 如果模型没有配置 token 单价(纯按次计费),返回 0 表示不做 token 重算。
+func (b *TaskBillingProviderImpl) RecalculateByTokens(ctx context.Context, tenantID int64, modelName string, totalTokens int, ratios map[string]float64) (float64, error) {
+ if totalTokens <= 0 {
+ return 0, nil
+ }
+
+ pricing, err := GetModelPrice(ctx, tenantID, modelName)
+ if err != nil {
+ return 0, nil
+ }
+
+ // 需要 output_price > 0 才能做 token 重算
+ if pricing.OutputPrice <= 0 {
+ return 0, nil
+ }
+
+ // 基础费用 = tokens × 单价
+ cost := float64(totalTokens) / 1_000_000.0 * pricing.OutputPrice
+
+ // 应用租户倍率
+ cost *= pricing.TenantMultiplier
+
+ // 应用附加比率(视频输入折扣等)
+ for _, ratio := range ratios {
+ cost *= ratio
+ }
+
+ // 最低消费
+ if cost < 0.01 {
+ cost = 0.01
+ }
+
+ return cost, nil
+}
diff --git a/internal/logic/task/async_polling.go b/internal/logic/task/async_polling.go
index 43d6045..0ea6ed9 100644
--- a/internal/logic/task/async_polling.go
+++ b/internal/logic/task/async_polling.go
@@ -114,12 +114,12 @@ func processPlatformTasks(ctx context.Context, platform string, tasks []*common.
}
for channelID, channelTasks := range channelGroups {
- processChannelTasks(ctx, platform, channelID, channelTasks)
+ processChannelTasks(ctx, channelID, channelTasks)
}
}
// processChannelTasks 处理同一渠道的任务
-func processChannelTasks(ctx context.Context, platform string, channelID int64, tasks []*common.AsyncTask) {
+func processChannelTasks(ctx context.Context, channelID int64, tasks []*common.AsyncTask) {
// 获取渠道信息
channel, err := DefaultAsyncProvider.GetChannelByID(ctx, channelID)
if err != nil || channel == nil {
@@ -139,20 +139,29 @@ func processChannelTasks(ctx context.Context, platform string, channelID int64,
}
}
+// privateData PrivateData 反序列化结构
+type privateData struct {
+ UpstreamTaskID string `json:"upstream_task_id"`
+ TaskType string `json:"task_type"`
+ BillingContext struct {
+ Ratios map[string]float64 `json:"ratios"`
+ ModelName string `json:"model_name"`
+ PreDeduct float64 `json:"pre_deduct"`
+ } `json:"billing_context"`
+}
+
// pollSingleTask 轮询单个任务
func pollSingleTask(ctx context.Context, adaptor common.TaskAdaptor, channel *common.ChannelBasicInfo, task *common.AsyncTask) {
- // 从 PrivateData 提取上游任务 ID
- var private struct {
- UpstreamTaskID string `json:"upstream_task_id"`
- }
- if err := json.Unmarshal(task.PrivateData, &private); err != nil || private.UpstreamTaskID == "" {
+ // 从 PrivateData 提取上游任务 ID 和计费上下文
+ var pd privateData
+ if err := json.Unmarshal(task.PrivateData, &pd); err != nil || pd.UpstreamTaskID == "" {
g.Log().Warningf(ctx, "poll: invalid private data for task %s", task.PublicTaskID)
return
}
// 查询上游状态
taskData, _ := json.Marshal(map[string]any{
- "task_id": private.UpstreamTaskID,
+ "task_id": pd.UpstreamTaskID,
})
resp, err := adaptor.FetchTask(channel.BaseURL, channel.ApiKey, taskData)
@@ -214,9 +223,17 @@ func pollSingleTask(ctx context.Context, adaptor common.TaskAdaptor, channel *co
if task.PreDeductAmount > 0 && !task.BillingSettled {
taskBilling := billing.NewTaskBillingProvider()
actualCost := task.PreDeductAmount
+
+ // 优先用上游返回的 ActualCost
if taskInfo.ActualCost > 0 {
actualCost = taskInfo.ActualCost
+ } else if taskInfo.TotalTokens > 0 && pd.BillingContext.Ratios != nil {
+ // 用上游 total_tokens + 保存的 ratios 重算
+ if tokenCost, err := taskBilling.RecalculateByTokens(ctx, task.TenantID, task.ModelName, taskInfo.TotalTokens, pd.BillingContext.Ratios); err == nil && tokenCost > 0 {
+ actualCost = tokenCost
+ }
}
+
task.ActualCost = actualCost
if err := taskBilling.SettleTaskSuccess(ctx, task.TenantID, task.UserID, task.ApiKeyID, task.ChannelID, task.ModelName, task.PublicTaskID, actualCost, task.PreDeductAmount); err != nil {
g.Log().Warningf(ctx, "poll: settle task %s: %v", task.PublicTaskID, err)
diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go
index 2474bb8..3808fa9 100644
--- a/relay/channel/openai/adaptor.go
+++ b/relay/channel/openai/adaptor.go
@@ -146,7 +146,11 @@ func (a *Adaptor) ConvertRequest(ctx context.Context, info *common.RelayInfo, re
default:
converted = bytes.NewReader(requestBody)
}
- result := injectStreamOptions(replaceModelIfNeeded(converted, info), info)
+ result := replaceModelIfNeeded(converted, info)
+ // stream_options 是 Chat Completions 专属字段,GPT Image 使用 stream/partial_images 原生参数
+ if info.IsStream && mode != constant.RelayModeImagesGenerations && mode != constant.RelayModeImagesEdits {
+ result = injectStreamOptions(result, info)
+ }
// Thinking 后缀路由:注入 reasoning_effort
if info.ReasoningEffort != "" {
diff --git a/relay/channel/openai/image.go b/relay/channel/openai/image.go
index 67b6264..aadb1c8 100644
--- a/relay/channel/openai/image.go
+++ b/relay/channel/openai/image.go
@@ -2,17 +2,31 @@ package openai
import (
"context"
+ "encoding/json"
"io"
"net/http"
"github.com/qianfree/team-api/relay/common"
"github.com/qianfree/team-api/relay/constant"
+ "github.com/qianfree/team-api/relay/dto"
+ "github.com/qianfree/team-api/relay/helper"
)
+// imageStreamEvent GPT Image SSE 事件
+type imageStreamEvent struct {
+ Type string `json:"type"`
+ Data []dto.ImageData `json:"data"`
+ Usage *dto.ImageUsage `json:"usage"`
+}
+
// handleImageResponse 处理图像生成非流式响应
func (a *Adaptor) handleImageResponse(ctx context.Context, resp *http.Response, info *common.RelayInfo, writer http.ResponseWriter) (*common.Usage, error) {
defer resp.Body.Close()
+ if info.IsStream {
+ return a.handleImageStreamResponse(ctx, resp, info, writer)
+ }
+
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, constant.NewUpstreamError(resp.StatusCode, "read response body failed", err)
@@ -22,11 +36,65 @@ func (a *Adaptor) handleImageResponse(ctx context.Context, resp *http.Response,
return nil, constant.NewUpstreamError(resp.StatusCode, string(body), nil)
}
- // 图像生成响应中没有 model 字段需要替换,直接透传
+ // 解析 GPT Image 响应中的 usage(dall-e-2/3 无此字段,imgResp.Usage 为 nil)
+ var imgResp dto.ImageResponse
+ usage := &common.Usage{}
+ if err := json.Unmarshal(body, &imgResp); err == nil && imgResp.Usage != nil {
+ usage = imageUsageToCommon(imgResp.Usage)
+ }
+
+ // 透传原始响应
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(resp.StatusCode)
_, _ = writer.Write(body)
- // 图像生成没有 token 用量
- return &common.Usage{}, nil
+ return usage, nil
+}
+
+// handleImageStreamResponse 处理 GPT Image 流式响应(SSE)
+func (a *Adaptor) handleImageStreamResponse(ctx context.Context, resp *http.Response, info *common.RelayInfo, writer http.ResponseWriter) (*common.Usage, error) {
+ var totalUsage common.Usage
+
+ helper.StreamScannerHandler(ctx, resp, info, writer, func(data string, sr *helper.StreamResult) {
+ // 透传 SSE 数据到客户端
+ if err := helper.WriteSSEData(writer, data); err != nil {
+ sr.Stop(err)
+ return
+ }
+
+ // 解析 usage(generation.completed 事件包含完整 usage)
+ var event imageStreamEvent
+ if err := json.Unmarshal([]byte(data), &event); err == nil && event.Usage != nil {
+ u := imageUsageToCommon(event.Usage)
+ totalUsage.PromptTokens = u.PromptTokens
+ totalUsage.CompletionTokens = u.CompletionTokens
+ totalUsage.TotalTokens = u.TotalTokens
+ totalUsage.PromptTokensDetails = u.PromptTokensDetails
+ }
+ })
+
+ if info.StreamStatus == nil || !info.StreamStatus.IsPartialStreamEnd() {
+ _ = helper.WriteSSEData(writer, "[DONE]")
+ }
+
+ if info.StreamStatus != nil && info.StreamStatus.IsPartialStreamEnd() {
+ return &totalUsage, common.ErrStreamInterrupted
+ }
+ return &totalUsage, nil
+}
+
+// imageUsageToCommon 将 GPT Image usage 映射到通用 Usage 结构
+func imageUsageToCommon(u *dto.ImageUsage) *common.Usage {
+ usage := &common.Usage{
+ PromptTokens: u.InputTokens,
+ CompletionTokens: u.OutputTokens,
+ TotalTokens: u.TotalTokens,
+ }
+ if u.InputTokensDetails != nil {
+ usage.PromptTokensDetails = &common.TokenDetails{
+ TextTokens: u.InputTokensDetails.TextTokens,
+ ImageTokens: u.InputTokensDetails.ImageTokens,
+ }
+ }
+ return usage
}
diff --git a/relay/common/task_adaptor.go b/relay/common/task_adaptor.go
index d958cc5..9b2f76a 100644
--- a/relay/common/task_adaptor.go
+++ b/relay/common/task_adaptor.go
@@ -59,6 +59,10 @@ type TaskInfo struct {
Data []byte // 上游原始响应
SubTasks []SubTask // 子任务结果(如多图/多视频场景)
ActualCost float64 // 上游返回的实际费用(0 表示未提供,使用预扣金额)
+
+ // 上游 usage 信息,用于按 token 重算费用
+ CompletionTokens int // 上游返回的 completion tokens
+ TotalTokens int // 上游返回的 total tokens
}
// TaskStatusEnum 任务状态枚举
diff --git a/relay/common/task_billing.go b/relay/common/task_billing.go
index 35a2bca..f48cd12 100644
--- a/relay/common/task_billing.go
+++ b/relay/common/task_billing.go
@@ -19,4 +19,9 @@ type TaskBillingProvider interface {
// AdjustTaskBilling 调整预扣金额(提交后上游确认了新参数)
AdjustTaskBilling(ctx context.Context, tenantID int64, requestID string, preDeductAmount, newEstimatedCost float64) (float64, error)
+
+ // RecalculateByTokens 根据上游返回的 total_tokens 重算费用
+ // totalTokens: 上游返回的 token 计费单位
+ // ratios: 提交时保存的计费比率(如 video_input 折扣)
+ RecalculateByTokens(ctx context.Context, tenantID int64, modelName string, totalTokens int, ratios map[string]float64) (float64, error)
}
diff --git a/relay/constant/task.go b/relay/constant/task.go
index 77dbe52..5725421 100644
--- a/relay/constant/task.go
+++ b/relay/constant/task.go
@@ -9,6 +9,7 @@ const (
TaskPlatformSuno TaskPlatform = "suno"
TaskPlatformMidjourney TaskPlatform = "midjourney"
TaskPlatformVolcengine TaskPlatform = "volcengine"
+ TaskPlatformAli TaskPlatform = "ali"
)
// TaskAction 异步任务动作类型
@@ -81,6 +82,8 @@ func ProviderTypeToTaskPlatform(p ProviderType) (TaskPlatform, bool) {
return TaskPlatformMidjourney, true
case ProviderVolcengine:
return TaskPlatformVolcengine, true
+ case ProviderAli:
+ return TaskPlatformAli, true
default:
return "", false
}
diff --git a/relay/dto/usage.go b/relay/dto/usage.go
index efb50aa..3af1196 100644
--- a/relay/dto/usage.go
+++ b/relay/dto/usage.go
@@ -128,12 +128,19 @@ type ImageRequest struct {
Background string `json:"background,omitempty"`
Moderation string `json:"moderation,omitempty"`
InputImageMask any `json:"input_image_mask,omitempty"`
+ PartialImages *int `json:"partial_images,omitempty"`
+ Stream *bool `json:"stream,omitempty"`
}
// ImageResponse 图像生成响应
type ImageResponse struct {
- Created int64 `json:"created"`
- Data []ImageData `json:"data"`
+ Created int64 `json:"created"`
+ Data []ImageData `json:"data"`
+ Background string `json:"background,omitempty"`
+ OutputFormat string `json:"output_format,omitempty"`
+ Size string `json:"size,omitempty"`
+ Quality string `json:"quality,omitempty"`
+ Usage *ImageUsage `json:"usage,omitempty"`
}
// ImageData 单个图像数据
@@ -143,3 +150,17 @@ type ImageData struct {
RevisedPrompt string `json:"revised_prompt,omitempty"`
ContentType string `json:"content_type,omitempty"`
}
+
+// ImageUsage GPT Image token 用量(gpt-image-1, gpt-image-2)
+type ImageUsage struct {
+ TotalTokens int `json:"total_tokens"`
+ InputTokens int `json:"input_tokens"`
+ OutputTokens int `json:"output_tokens"`
+ InputTokensDetails *ImageTokenDetails `json:"input_tokens_details,omitempty"`
+}
+
+// ImageTokenDetails GPT Image 输入 token 细分
+type ImageTokenDetails struct {
+ TextTokens int `json:"text_tokens"`
+ ImageTokens int `json:"image_tokens"`
+}
diff --git a/relay/handler/task_handler.go b/relay/handler/task_handler.go
index 7660833..f5cb200 100644
--- a/relay/handler/task_handler.go
+++ b/relay/handler/task_handler.go
@@ -13,6 +13,7 @@ import (
"github.com/qianfree/team-api/relay/common"
"github.com/qianfree/team-api/relay/constant"
"github.com/qianfree/team-api/relay/taskchannel"
+ _ "github.com/qianfree/team-api/relay/taskchannel/ali"
_ "github.com/qianfree/team-api/relay/taskchannel/kling"
"github.com/qianfree/team-api/relay/taskchannel/midjourney"
_ "github.com/qianfree/team-api/relay/taskchannel/sora"
@@ -143,6 +144,23 @@ func HandleTaskSubmit(
preDeductAmount, _ = billingProvider.AdjustTaskBilling(ctx, rc.TenantID, rc.RequestID, preDeductAmount, newCost)
}
+ // 9.5 合并最终 ratios 用于结算时还原
+ finalRatios := ratios
+ if adjustedRatios != nil {
+ if finalRatios == nil {
+ finalRatios = adjustedRatios
+ } else {
+ merged := make(map[string]float64, len(finalRatios)+len(adjustedRatios))
+ for k, v := range finalRatios {
+ merged[k] = v
+ }
+ for k, v := range adjustedRatios {
+ merged[k] = v
+ }
+ finalRatios = merged
+ }
+ }
+
// 10. 生成公开任务 ID 并创建记录
publicTaskID := generatePublicTaskID()
now := time.Now()
@@ -150,6 +168,11 @@ func HandleTaskSubmit(
privateData, _ := json.Marshal(map[string]any{
"upstream_task_id": upstreamTaskID,
"task_type": platform,
+ "billing_context": map[string]any{
+ "ratios": finalRatios,
+ "model_name": modelName,
+ "pre_deduct": preDeductAmount,
+ },
})
task := &common.AsyncTask{
diff --git a/relay/taskchannel/ali/adaptor.go b/relay/taskchannel/ali/adaptor.go
new file mode 100644
index 0000000..60fdf02
--- /dev/null
+++ b/relay/taskchannel/ali/adaptor.go
@@ -0,0 +1,199 @@
+package ali
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ "github.com/qianfree/team-api/relay/common"
+ "github.com/qianfree/team-api/relay/constant"
+ "github.com/qianfree/team-api/relay/taskchannel"
+)
+
+func init() {
+ taskchannel.Register(constant.ProviderAli, func() common.TaskAdaptor {
+ return &AliImageAdaptor{}
+ })
+}
+
+// AliImageAdaptor 阿里云 DashScope 异步图片生成适配器
+type AliImageAdaptor struct {
+ info *common.RelayInfo
+}
+
+func (a *AliImageAdaptor) Init(info *common.RelayInfo) {
+ a.info = info
+}
+
+func (a *AliImageAdaptor) ValidateRequest(_ context.Context, _ *common.RelayInfo, body []byte) *common.TaskError {
+ var req map[string]any
+ if err := json.Unmarshal(body, &req); err != nil {
+ return &common.TaskError{StatusCode: http.StatusBadRequest, Message: "invalid request body"}
+ }
+ if _, ok := req["model"]; !ok {
+ return &common.TaskError{StatusCode: http.StatusBadRequest, Message: "model is required"}
+ }
+ if _, ok := req["prompt"]; !ok {
+ return &common.TaskError{StatusCode: http.StatusBadRequest, Message: "prompt is required"}
+ }
+ return nil
+}
+
+func (a *AliImageAdaptor) EstimateBilling(_ context.Context, _ *common.RelayInfo, body []byte) map[string]float64 {
+ ratios := map[string]float64{"base": 1.0}
+ var req map[string]any
+ if json.Unmarshal(body, &req) != nil {
+ return ratios
+ }
+ if n, ok := req["n"].(float64); ok && n > 1 {
+ ratios["count"] = n
+ }
+ return ratios
+}
+
+func (a *AliImageAdaptor) AdjustBillingOnSubmit(_ *common.RelayInfo, _ []byte) map[string]float64 {
+ return nil
+}
+
+func (a *AliImageAdaptor) BuildRequestURL(info *common.RelayInfo) (string, error) {
+ baseURL := strings.TrimRight(info.ChannelMeta.BaseURL, "/")
+ return baseURL + "/api/v1/services/aigc/text2image/image-synthesis", nil
+}
+
+func (a *AliImageAdaptor) BuildRequestHeader(header http.Header, info *common.RelayInfo) error {
+ header.Set("Authorization", "Bearer "+info.ChannelMeta.ApiKey)
+ header.Set("Content-Type", "application/json")
+ header.Set("Accept", "application/json")
+ header.Set("X-DashScope-Async", "enable")
+ return nil
+}
+
+func (a *AliImageAdaptor) BuildRequestBody(_ context.Context, info *common.RelayInfo, body []byte) (io.Reader, error) {
+ converted, err := convertToDashScopeRequest(body, info)
+ if err != nil {
+ return nil, err
+ }
+ return strings.NewReader(string(converted)), nil
+}
+
+func (a *AliImageAdaptor) DoRequest(_ context.Context, info *common.RelayInfo, requestBody io.Reader) (*http.Response, error) {
+ url, err := a.BuildRequestURL(info)
+ if err != nil {
+ return nil, err
+ }
+ req, err := http.NewRequest(http.MethodPost, url, requestBody)
+ if err != nil {
+ return nil, fmt.Errorf("create request: %w", err)
+ }
+ if err := a.BuildRequestHeader(req.Header, info); err != nil {
+ return nil, fmt.Errorf("setup header: %w", err)
+ }
+ client := &http.Client{Timeout: 120 * 1e9}
+ return client.Do(req)
+}
+
+func (a *AliImageAdaptor) DoResponse(_ context.Context, resp *http.Response, _ *common.RelayInfo) (string, []byte, *common.TaskError) {
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return "", nil, &common.TaskError{StatusCode: http.StatusInternalServerError, Message: "read response failed"}
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return "", body, &common.TaskError{
+ StatusCode: resp.StatusCode,
+ Message: string(body),
+ }
+ }
+
+ var result dashScopeSubmitResponse
+ if err := json.Unmarshal(body, &result); err != nil {
+ return "", body, &common.TaskError{StatusCode: http.StatusInternalServerError, Message: "parse response failed"}
+ }
+
+ if result.Output.TaskID == "" {
+ // DashScope 有时在错误时返回 200 但带有 code/message
+ if result.Output.Code != "" {
+ return "", body, &common.TaskError{
+ StatusCode: resp.StatusCode,
+ Message: result.Output.Message,
+ ErrCode: result.Output.Code,
+ }
+ }
+ return "", body, &common.TaskError{StatusCode: http.StatusInternalServerError, Message: "upstream returned empty task id"}
+ }
+
+ return result.Output.TaskID, body, nil
+}
+
+func (a *AliImageAdaptor) FetchTask(baseURL, apiKey string, taskData []byte) (*http.Response, error) {
+ var data struct {
+ TaskID string `json:"task_id"`
+ }
+ if err := json.Unmarshal(taskData, &data); err != nil {
+ return nil, fmt.Errorf("ali: invalid task data: %w", err)
+ }
+
+ url := fmt.Sprintf("%s/api/v1/tasks/%s", strings.TrimRight(baseURL, "/"), data.TaskID)
+ req, err := http.NewRequest(http.MethodGet, url, nil)
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Authorization", "Bearer "+apiKey)
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Accept", "application/json")
+
+ client := &http.Client{Timeout: 30 * 1e9}
+ return client.Do(req)
+}
+
+func (a *AliImageAdaptor) ParseTaskResult(body []byte) (*common.TaskInfo, error) {
+ var resp dashScopeTaskResponse
+ if err := json.Unmarshal(body, &resp); err != nil {
+ return nil, fmt.Errorf("ali: parse task result: %w", err)
+ }
+
+ info := &common.TaskInfo{Data: body}
+
+ switch resp.Output.TaskStatus {
+ case "PENDING":
+ info.Status = common.TaskStatusSubmitted
+ info.Progress = "10%"
+ case "RUNNING":
+ info.Status = common.TaskStatusInProgress
+ info.Progress = "50%"
+ case "SUCCEEDED":
+ info.Status = common.TaskStatusSuccess
+ info.Progress = "100%"
+ if len(resp.Output.Results) > 0 {
+ info.ResultURL = resp.Output.Results[0].URL
+ for i, r := range resp.Output.Results {
+ info.SubTasks = append(info.SubTasks, common.SubTask{
+ Index: i,
+ ResultURL: r.URL,
+ })
+ }
+ }
+ case "FAILED":
+ info.Status = common.TaskStatusFailure
+ info.FailReason = resp.Output.Message
+ case "CANCELED", "UNKNOWN":
+ info.Status = common.TaskStatusFailure
+ info.FailReason = fmt.Sprintf("task status: %s", resp.Output.TaskStatus)
+ default:
+ info.Status = common.TaskStatusSubmitted
+ info.Progress = "10%"
+ }
+
+ return info, nil
+}
+
+func (a *AliImageAdaptor) GetModelList() []string {
+ return ModelList
+}
+
+func (a *AliImageAdaptor) GetChannelName() string {
+ return channelName
+}
diff --git a/relay/taskchannel/ali/constants.go b/relay/taskchannel/ali/constants.go
new file mode 100644
index 0000000..b2f212d
--- /dev/null
+++ b/relay/taskchannel/ali/constants.go
@@ -0,0 +1,22 @@
+package ali
+
+const channelName = "AliImage"
+
+// ModelList 支持异步图片生成的 DashScope 模型
+var ModelList = []string{
+ "wanx-v1",
+ "wanx-v2",
+ "wanx2.1-t2i-ediff",
+ "wanx-sketch-to-image-v2",
+ "wanx-image-inpainting",
+ "wanx-style-repaint",
+ "wanx2.1-image-edit",
+ "wanx2.5-image-edit",
+ "wanx-image-outpainting",
+ "qwen-image-generation",
+ "qwen-image-edit",
+ "zimage",
+ "flux-dev",
+ "flux-schnell",
+ "stable-diffusion-xl",
+}
diff --git a/relay/taskchannel/ali/converter.go b/relay/taskchannel/ali/converter.go
new file mode 100644
index 0000000..8f9dbfd
--- /dev/null
+++ b/relay/taskchannel/ali/converter.go
@@ -0,0 +1,99 @@
+package ali
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/qianfree/team-api/relay/common"
+)
+
+// convertToDashScopeRequest 将 OpenAI Images 格式转换为 DashScope 原生格式
+// OpenAI: {model, prompt, size, n, style, ...}
+// DashScope: {model, input: {prompt, negative_prompt}, parameters: {size, n, style, ...}}
+func convertToDashScopeRequest(body []byte, info *common.RelayInfo) ([]byte, error) {
+ var raw map[string]json.RawMessage
+ if err := json.Unmarshal(body, &raw); err != nil {
+ return nil, fmt.Errorf("parse request: %w", err)
+ }
+
+ req := dashScopeRequest{
+ Input: dashScopeInput{},
+ Parameters: make(map[string]any),
+ }
+
+ // model
+ if info.ChannelMeta.IsModelMapped && info.ChannelMeta.UpstreamModelName != "" {
+ req.Model = info.ChannelMeta.UpstreamModelName
+ } else if v, ok := raw["model"]; ok {
+ _ = json.Unmarshal(v, &req.Model)
+ }
+
+ // prompt → input.prompt
+ if v, ok := raw["prompt"]; ok {
+ _ = json.Unmarshal(v, &req.Input.Prompt)
+ }
+
+ // negative_prompt → input.negative_prompt
+ if v, ok := raw["negative_prompt"]; ok {
+ _ = json.Unmarshal(v, &req.Input.NegativePrompt)
+ }
+
+ // size: 1024x1024 → 1024*1024
+ if v, ok := raw["size"]; ok {
+ var size string
+ if err := json.Unmarshal(v, &size); err == nil && size != "" {
+ req.Parameters["size"] = strings.ReplaceAll(size, "x", "*")
+ }
+ }
+
+ // n
+ if v, ok := raw["n"]; ok {
+ var n int
+ if err := json.Unmarshal(v, &n); err == nil && n > 0 {
+ req.Parameters["n"] = n
+ }
+ }
+
+ // seed
+ if v, ok := raw["seed"]; ok {
+ var seed int
+ if err := json.Unmarshal(v, &seed); err == nil {
+ req.Parameters["seed"] = seed
+ }
+ }
+
+ // style
+ if v, ok := raw["style"]; ok {
+ var style string
+ if err := json.Unmarshal(v, &style); err == nil && style != "" {
+ req.Parameters["style"] = style
+ }
+ }
+
+ // ref_strength
+ if v, ok := raw["ref_strength"]; ok {
+ var strength float64
+ if err := json.Unmarshal(v, &strength); err == nil {
+ req.Parameters["ref_strength"] = strength
+ }
+ }
+
+ // ref_img
+ if v, ok := raw["ref_img"]; ok {
+ var refImg string
+ if err := json.Unmarshal(v, &refImg); err == nil && refImg != "" {
+ req.Parameters["ref_img"] = refImg
+ }
+ }
+
+ if len(req.Parameters) == 0 {
+ req.Parameters = nil
+ }
+
+ result, err := json.Marshal(req)
+ if err != nil {
+ return nil, fmt.Errorf("marshal dashscope request: %w", err)
+ }
+ return result, nil
+}
diff --git a/relay/taskchannel/ali/types.go b/relay/taskchannel/ali/types.go
new file mode 100644
index 0000000..9bf5404
--- /dev/null
+++ b/relay/taskchannel/ali/types.go
@@ -0,0 +1,45 @@
+package ali
+
+// dashScopeRequest DashScope 异步图片生成请求
+type dashScopeRequest struct {
+ Model string `json:"model"`
+ Input dashScopeInput `json:"input"`
+ Parameters map[string]any `json:"parameters,omitempty"`
+}
+
+// dashScopeInput DashScope 请求输入
+type dashScopeInput struct {
+ Prompt string `json:"prompt,omitempty"`
+ NegativePrompt string `json:"negative_prompt,omitempty"`
+}
+
+// dashScopeSubmitResponse 异步提交响应
+type dashScopeSubmitResponse struct {
+ Output struct {
+ TaskID string `json:"task_id"`
+ TaskStatus string `json:"task_status"`
+ Code string `json:"code,omitempty"`
+ Message string `json:"message,omitempty"`
+ } `json:"output"`
+ RequestID string `json:"request_id"`
+}
+
+// dashScopeTaskResponse 异步任务轮询响应
+type dashScopeTaskResponse struct {
+ Output struct {
+ TaskID string `json:"task_id"`
+ TaskStatus string `json:"task_status"`
+ Results []dashScopeResult `json:"results"`
+ Code string `json:"code,omitempty"`
+ Message string `json:"message,omitempty"`
+ } `json:"output"`
+ Usage struct {
+ ImageCount int `json:"image_count"`
+ } `json:"usage,omitempty"`
+ RequestID string `json:"request_id,omitempty"`
+}
+
+// dashScopeResult 单个图片结果
+type dashScopeResult struct {
+ URL string `json:"url"`
+}
diff --git a/relay/taskchannel/volcengine/adaptor.go b/relay/taskchannel/volcengine/adaptor.go
index dd693f5..b506073 100644
--- a/relay/taskchannel/volcengine/adaptor.go
+++ b/relay/taskchannel/volcengine/adaptor.go
@@ -61,10 +61,16 @@ type taskResponse struct {
Content struct {
VideoURL string `json:"video_url"`
} `json:"content"`
- Seed int `json:"seed"`
- Duration int `json:"duration"`
- Ratio string `json:"ratio"`
- Error struct {
+ Seed int `json:"seed"`
+ Duration int `json:"duration"`
+ Ratio string `json:"ratio"`
+ Resolution string `json:"resolution"`
+ FramesPerSecond int `json:"framespersecond"`
+ Usage struct {
+ CompletionTokens int `json:"completion_tokens"`
+ TotalTokens int `json:"total_tokens"`
+ } `json:"usage"`
+ Error struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
@@ -94,18 +100,31 @@ func (a *VolcengineVideoAdaptor) ValidateRequest(_ context.Context, _ *common.Re
}
func (a *VolcengineVideoAdaptor) EstimateBilling(_ context.Context, info *common.RelayInfo, body []byte) map[string]float64 {
- ratios := map[string]float64{"base": 1.0}
-
- // 检测是否含视频输入,应用折扣比率
var req map[string]any
if json.Unmarshal(body, &req) != nil {
- return ratios
+ return nil
}
+
+ ratios := make(map[string]float64)
+
+ // 提取 duration 用于 token 预估(默认 5s)
+ duration := extractDuration(req)
+ if duration > 0 {
+ ratios["duration"] = float64(duration)
+ }
+
+ // 提取 resolution 用于 token 预估
+ if resolution := extractResolution(req); resolution != "" {
+ ratios["resolution"] = resolutionMultiplier(resolution)
+ }
+
+ // 检测是否含视频输入,应用折扣比率
if hasVideoInput(req) {
if ratio, ok := getVideoInputRatio(info.ChannelMeta.UpstreamModelName); ok {
ratios["video_input"] = ratio
}
}
+
return ratios
}
@@ -335,6 +354,9 @@ func (a *VolcengineVideoAdaptor) ParseTaskResult(body []byte) (*common.TaskInfo,
info.Status = common.TaskStatusSuccess
info.Progress = "100%"
info.ResultURL = resp.Content.VideoURL
+ // 提取上游 usage 用于按 token 计费
+ info.CompletionTokens = resp.Usage.CompletionTokens
+ info.TotalTokens = resp.Usage.TotalTokens
case "failed":
info.Status = common.TaskStatusFailure
info.FailReason = resp.Error.Message
@@ -382,3 +404,42 @@ func parseInt(s string) (int, error) {
_, err := fmt.Sscanf(s, "%d", &n)
return n, err
}
+
+// extractDuration 从请求中提取 duration 秒数
+func extractDuration(req map[string]any) int {
+ if metadata, ok := req["metadata"].(map[string]any); ok {
+ if d, ok := metadata["duration"].(float64); ok && d > 0 {
+ return int(d)
+ }
+ }
+ if s, ok := req["seconds"].(string); ok {
+ if d, err := parseInt(s); err == nil && d > 0 {
+ return d
+ }
+ }
+ return 5
+}
+
+// extractResolution 从请求中提取 resolution
+func extractResolution(req map[string]any) string {
+ if metadata, ok := req["metadata"].(map[string]any); ok {
+ if r, ok := metadata["resolution"].(string); ok && r != "" {
+ return r
+ }
+ }
+ return "720p"
+}
+
+// resolutionMultiplier 分辨率乘数(相对于 480p)
+func resolutionMultiplier(resolution string) float64 {
+ switch resolution {
+ case "480p":
+ return 1.0
+ case "720p":
+ return 2.25
+ case "1080p":
+ return 5.06
+ default:
+ return 2.25
+ }
+}