Merge branch 'feat/image_model' into dev

This commit is contained in:
qian committed 2026-05-20 00:24:58 +08:00
commit 023952502d
24 files changed
+6994 -63

No files matched your search

@@ -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` 字段用于计费。
@@ -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** 可检索页面内所有内容。
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_cae7ddb0e1977b68b353f17897b8574c.png) </span>
```mixin-react
return (<Tabs>
<Tabs.TabPane title="在线调试" key="4rK5FhUg"><RenderMd content={`<APILink link="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" description="API Explorer 您可以通过 API Explorer 在线发起调用,无需关注签名生成过程,快速获取调用结果。">去调试</APILink>
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="鉴权说明" key="iRuPtuk6"><RenderMd content={`本接口仅支持 API Key 鉴权,请在 [获取 API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey) 页面,获取长效 API Key。
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="快速入口" key="5LZLMN0J"><RenderMd content={` [ ](#)[体验中心](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_2abecd05ca2779567c6d32f0ddc7874d.png =20x) </span>[模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#2705b333) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_a5fdd3028d35cc512a10bd71b982b6eb.png =20x) </span>[模型计费](https://www.volcengine.com/docs/82379/1544106?redirect=1&lang=zh#02affcb8) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_afbcf38bdec05c05089d5de5c3fd8fc8.png =20x) </span>[API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_57d0bca8e0d122ab1191b40101b5df75.png =20x) </span>[调用教程](https://www.volcengine.com/docs/82379/1366799) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_f45b5cd5863d1eed3bc3c81b9af54407.png =20x) </span>[接口文档](https://www.volcengine.com/docs/82379/1520758) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_1609c71a747f84df24be1e6421ce58f0.png =20x) </span>[常见问题](https://www.volcengine.com/docs/82379/1359411) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_bef4bc3de3535ee19d0c5d6c37b0ffdd.png =20x) </span>[开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
<span id="5qndT7DS"></span>
## 请求参数
> 跳转 [响应参数](#y2hhTyHB)
<span id="wsGzv1pD"></span>
### 请求体
---
**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,<Base64编码>`,注意 `<图片格式>` 需小写,如 `data:image/png;base64,{base64_image}`。
* 素材 ID:用于视频生成的预置素材及虚拟人像的 ID,遵循格式:asset://<ASSET_ID\>。可从 [素材&虚拟人像库](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://<ASSET_ID\>。可从[素材&虚拟人像库](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,<Base64编码>`,注意 `<音频格式>` 需小写,如 `data:audio/wav;base64,{base64_audio}`。
* 素材 ID:用于视频生成的虚拟人的音频素材 ID,遵循格式:asset://<ASSET_ID\>。可从[素材&虚拟人像库](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 或邮箱进行哈希处理后生成的字符串,避免泄露用户隐私信息。
---
&nbsp;
:::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 支持
* 其他模型仅图生视频场景支持
**取值规则:**
* 文生视频:根据输入的提示词,智能选择最合适的宽高比。
* 首帧 / 首尾帧生视频:根据上传的首帧图片比例,自动选择最接近的宽高比。
* 多模态参考生视频:根据用户提示词意图判断,如果是首帧生视频/编辑视频/延长视频,以该图片/视频为准选择最接近的宽高比;否则,以传入的第一个媒体文件为准(优先级:视频>图片)选择最接近的宽高比。
:::
&nbsp;
不同宽高比对应的宽高像素值
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 生成`水印。
---
<span id="oCS1tULg"></span>
## 响应参数
> 跳转 [请求参数](#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`。
@@ -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 (<Tabs>
<Tabs.TabPane title="快速入口" key="vI631gwS"><RenderMd content={` [ ](#)[体验中心](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_2abecd05ca2779567c6d32f0ddc7874d.png =20x) </span>[模型列表](https://www.volcengine.com/docs/82379/1330310) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_a5fdd3028d35cc512a10bd71b982b6eb.png =20x) </span>[模型计费](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) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_afbcf38bdec05c05089d5de5c3fd8fc8.png =20x) </span>[API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_57d0bca8e0d122ab1191b40101b5df75.png =20x) </span>[调用教程](https://www.volcengine.com/docs/82379/1366799) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_f45b5cd5863d1eed3bc3c81b9af54407.png =20x) </span>[接口文档](https://www.volcengine.com/docs/82379/1521675) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_1609c71a747f84df24be1e6421ce58f0.png =20x) </span>[常见问题](https://www.volcengine.com/docs/82379/1359411) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_bef4bc3de3535ee19d0c5d6c37b0ffdd.png =20x) </span>[开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="鉴权说明" key="L8aMwmZD"><RenderMd content={`本接口支持 API Key 鉴权,详见[鉴权认证方式](https://www.volcengine.com/docs/82379/1298459)。
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
<span id="RxN8G2nH"></span>
## 请求参数
> 跳转 [响应参数](#7mi8G8RI)
:::tip
下面参数为Query String Parameters,在URL String中传入。
:::
---
**id** `string` %%require%%
需要取消或者删除的视频生成任务。
任务状态不同,调用`DELETE`接口,执行的操作有所不同,具体说明如下:
|当前任务状态 |是否支持DELETE操作 |操作含义 |DELETE操作后任务状态 |
|---|---|---|---|
|queued |是 |任务取消排队,任务状态被变更为cancelled。 |cancelled |
|running |否 |\- |\- |
|succeeded |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
|failed |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
|cancelled |否 |\- |\- |
|expired |是 |删除视频生成任务记录,后续将不支持查询。 |\- |
---
<span id="7mi8G8RI"></span>
## 响应参数
> 跳转 [请求参数](#RxN8G2nH)
本接口无返回参数。
@@ -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 (<Tabs>
<Tabs.TabPane title="快速入口" key="fq9yXaKY"><RenderMd content={` [ ](#)[体验中心](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_2abecd05ca2779567c6d32f0ddc7874d.png =20x) </span>[模型列表](https://www.volcengine.com/docs/82379/1330310) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_a5fdd3028d35cc512a10bd71b982b6eb.png =20x) </span>[模型计费](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) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_afbcf38bdec05c05089d5de5c3fd8fc8.png =20x) </span>[API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_57d0bca8e0d122ab1191b40101b5df75.png =20x) </span>[调用教程](https://www.volcengine.com/docs/82379/1366799) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_f45b5cd5863d1eed3bc3c81b9af54407.png =20x) </span>[接口文档](https://www.volcengine.com/docs/82379/1521309) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_1609c71a747f84df24be1e6421ce58f0.png =20x) </span>[常见问题](https://www.volcengine.com/docs/82379/1359411) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_bef4bc3de3535ee19d0c5d6c37b0ffdd.png =20x) </span>[开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="鉴权说明" key="3vCxpwty"><RenderMd content={`本接口支持 API Key 鉴权,详见[鉴权认证方式](https://www.volcengine.com/docs/82379/1298459)。
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
<span id="RxN8G2nH"></span>
## 请求参数
> 跳转 [响应参数](#7mi8G8RI)
---
**id** `string` %%require%%
您需要查询的视频生成任务的 ID 。
:::tip
上面参数为Query String Parameters,在URL String中传入。
:::
---
&nbsp;
<span id="7mi8G8RI"></span>
## 响应参数
> 跳转 [请求参数](#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`
实际调用联网搜索工具的次数,仅开启联网搜索时返回。
@@ -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 (<Tabs>
<Tabs.TabPane title="快速入口" key="opV4RT2k"><RenderMd content={` [ ](#)[体验中心](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_2abecd05ca2779567c6d32f0ddc7874d.png =20x) </span>[模型列表](https://www.volcengine.com/docs/82379/1330310) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_a5fdd3028d35cc512a10bd71b982b6eb.png =20x) </span>[模型计费](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) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_afbcf38bdec05c05089d5de5c3fd8fc8.png =20x) </span>[API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_57d0bca8e0d122ab1191b40101b5df75.png =20x) </span>[调用教程](https://www.volcengine.com/docs/82379/1366799) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_f45b5cd5863d1eed3bc3c81b9af54407.png =20x) </span>[接口文档](https://www.volcengine.com/docs/82379/1521675) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_1609c71a747f84df24be1e6421ce58f0.png =20x) </span>[常见问题](https://www.volcengine.com/docs/82379/1359411) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_bef4bc3de3535ee19d0c5d6c37b0ffdd.png =20x) </span>[开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="鉴权说明" key="CPeW5vNl"><RenderMd content={`本接口支持 API Key 鉴权,详见[鉴权认证方式](https://www.volcengine.com/docs/82379/1298459)。
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
<span id="RxN8G2nH"></span>
## 请求参数
> 跳转 [响应参数](#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`:离线推理模式
<span id="7mi8G8RI"></span>
## 响应参数
> 跳转 [请求参数](#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`
符合筛选条件的任务数量。
File diff suppressed because it is too large. Load diff
@@ -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)++ ** +++文本提示词++ 生成单张图片。
* 单图生图,根据您输入的 ++单张参考图片+文本提示词++ 生成单张图片。
* 文生图,根据您输入的 ++文本提示词++ 生成单张图片。
&nbsp;
```mixin-react
return (<Tabs>
<Tabs.TabPane title="鉴权说明" key="oOTdY3Sn"><RenderMd content={`本接口仅支持 API Key 鉴权,请在 [获取 API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey) 页面,获取长效 API Key。
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="快速入门" key="HHCpvO5jKo"><RenderMd content={` [ ](#)[体验中心](https://console.volcengine.com/ark/region:ark+cn-beijing/experience/vision?type=GenImage) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_2abecd05ca2779567c6d32f0ddc7874d.png =20x) </span>[模型列表](https://www.volcengine.com/docs/82379/1330310?lang=zh#9df4d9fd) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_a5fdd3028d35cc512a10bd71b982b6eb.png =20x) </span>[模型计费](https://www.volcengine.com/docs/82379/1544106?lang=zh#457edfd0) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_afbcf38bdec05c05089d5de5c3fd8fc8.png =20x) </span>[API Key](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey?apikey=%7B%7D)
<span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_57d0bca8e0d122ab1191b40101b5df75.png =20x) </span>[调用教程](https://www.volcengine.com/docs/82379/1548482) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_f45b5cd5863d1eed3bc3c81b9af54407.png =20x) </span>[接口文档](https://www.volcengine.com/docs/82379/1666945) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_1609c71a747f84df24be1e6421ce58f0.png =20x) </span>[常见问题](https://www.volcengine.com/docs/82379/1359411) <span>![图片](https://portal.volccdn.com/obj/volcfe/cloud-universal-doc/upload_bef4bc3de3535ee19d0c5d6c37b0ffdd.png =20x) </span>[开通模型](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&OpenTokenDrawer=false)
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
<span id="7thx2dVa"></span>
## 请求参数
<span id="BFVUvDi6"></span>
### 请求体
---
**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,<Base64编码>`。注意 `<图片格式>` 需小写,如 `data:image/png;base64,<base64_image>`。
:::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 (<Tabs>
<Tabs.TabPane title="doubao-seedream-5.0-lite" key="BMB6AP1M"><RenderMd content={`指定生成图像的尺寸信息,支持以下两种方式,不可混用。
* 方式 1 | 指定生成图像的分辨率,并在prompt中用自然语言描述图片宽高比、图片形状或图片用途,最终由模型判断生成图片的大小。
* 可选值:\`2K\`、\`3K\`、\`4K\`
* 方式 2 | 指定生成图像的宽高像素值:
* 默认值:\`2048x2048\`
* 总像素取值范围:[\`2560x1440=3686400\`, \`4096x4096=16777216\`]
* 宽高比取值范围:[1/16, 16]
:::tip
采用方式 2 时,需同时满足总像素取值范围和宽高比取值范围。其中,总像素是对单张图宽度和高度的像素乘积限制,而不是对宽度或高度的单独值进行限制。
* **有效示例**:\`3750x1250\`
总像素值 3750x1250=4687500,符合 [3686400, 16777216] 的区间要求;宽高比 3750/1250=3,符合 [1/16, 16] 的区间要求,故该示例值有效。
* **无效示例**:\`1500x1500\`
总像素值 1500x1500=2250000,未达到 3686400 的最低要求;宽高 1500/1500=1,虽符合 [1/16, 16] 的区间要求,但因其未同时满足两项限制,故该示例值无效。
:::
推荐的宽高像素值:
|分辨率 |宽高比 |宽高像素值 |
|---|---|---|
|<div style="text-align: center">|1:1 |2048x2048 |\\
|2K</div>| | |\\
| | | |
|^^|4:3 |2304x1728 |
|^^|3:4 |1728x2304 |
|^^|16:9 |2848x1600 |
|^^|9:16 |1600x2848 |
|^^|3:2 |2496x1664 |
|^^|2:3 |1664x2496 |
|^^|21:9 |3136x1344 |
|<div style="text-align: center">|1:1 |3072x3072 |\\
|3K</div>| | |\\
| | | |
|^^|4:3 |3456x2592 |
|^^|3:4 |2592x3456 |
|^^|16:9 |4096x2304 |
|^^|9:16 |2304x4096 |
|^^|2:3 |2496x3744 |
|^^|3:2 |3744x2496 |
|^^|21:9 |4704x2016 |
|<div style="text-align: center">|1:1 |4096x4096 |\\
|4K</div>| | |\\
| | | |
|^^|3:4 |3520x4704 |
|^^|4:3 |4704x3520 |
|^^|16:9 |5504x3040 |
|^^|9:16 |3040x5504 |
|^^|2:3 |3328x4992 |
|^^|3:2 |4992x3328 |
|^^|21:9 |6240x2656 |
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="doubao-seedream-4.5" key="kghENadO"><RenderMd content={`指定生成图像的尺寸信息,支持以下两种方式,不可混用。
* 方式 1 | 指定生成图像的分辨率,并在prompt中用自然语言描述图片宽高比、图片形状或图片用途,最终由模型判断生成图片的大小。
* 可选值:\`2K\`、\`4K\`
* 方式 2 | 指定生成图像的宽高像素值:
* 默认值:\`2048x2048\`
* 总像素取值范围:[\`2560x1440=3686400\`, \`4096x4096=16777216\`]
* 宽高比取值范围:[1/16, 16]
:::tip
采用方式 2 时,需同时满足总像素取值范围和宽高比取值范围。其中,总像素是对单张图宽度和高度的像素乘积限制,而不是对宽度或高度的单独值进行限制。
* **有效示例**:\`3750x1250\`
总像素值 3750x1250=4687500,符合 [3686400, 16777216] 的区间要求;宽高比 3750/1250=3,符合 [1/16, 16] 的区间要求,故该示例值有效。
* **无效示例**:\`1500x1500\`
总像素值 1500x1500=2250000,未达到 3686400 的最低要求;宽高 1500/1500=1,虽符合 [1/16, 16] 的区间要求,但因其未同时满足两项限制,故该示例值无效。
:::
推荐的宽高像素值:
|分辨率 |宽高比 |宽高像素值 |
|---|---|---|
|<div style="text-align: center">|1:1 |2048x2048 |\\
|2K</div>| | |\\
| | | |
|^^|4:3 |2304x1728 |
|^^|3:4 |1728x2304 |
|^^|16:9 |2848x1600 |
|^^|9:16 |1600x2848 |
|^^|3:2 |2496x1664 |
|^^|2:3 |1664x2496 |
|^^|21:9 |3136x1344 |
|<div style="text-align: center">|1:1 |4096x4096 |\\
|4K</div>| | |\\
| | | |
|^^|3:4 |3520x4704 |
|^^|4:3 |4704x3520 |
|^^|16:9 |5504x3040 |
|^^|9:16 |3040x5504 |
|^^|2:3 |3328x4992 |
|^^|3:2 |4992x3328 |
|^^|21:9 |6240x2656 |
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="doubao-seedream-4.0" key="MKsftGMr"><RenderMd content={`指定生成图像的尺寸信息,支持以下两种方式,不可混用。
* 方式 1 | 指定生成图像的分辨率,并在prompt中用自然语言描述图片宽高比、图片形状或图片用途,最终由模型判断生成图片的大小。
* 可选值:\`1K\`、\`2K\`、\`4K\`
* 方式 2 | 指定生成图像的宽高像素值:
* 默认值:\`2048x2048\`
* 总像素取值范围:[\`1280x720=921600\`, \`4096x4096=16777216\`]
* 宽高比取值范围:[1/16, 16]
:::tip
采用方式 2 时,需同时满足总像素取值范围和宽高比取值范围。其中,总像素是对单张图宽度和高度的像素乘积限制,而不是对宽度或高度的单独值进行限制。
* **有效示例**:\`1600x600\`
总像素值 1600x600=960000,符合 [921600, 16777216] 的区间要求;宽高比 1600/600=8/3,符合 [1/16, 16] 的区间要求,故该示例值有效。
* **无效示例**:\`800x800\`
总像素值 800x800=640000,未达到 921600 的最低要求;宽高 800/800=1,虽符合 [1/16, 16] 的区间要求,但因其未同时满足两项限制,故该示例值无效。
:::
推荐的宽高像素值:
|分辨率 |宽高比 |宽高像素值 |
|---|---|---|
|<div style="text-align: center">|1:1 |1024x1024 |\\
|1K</div>| | |\\
| | | |
|^^|4:3 |1152x864 |
|^^|3:4 |864x1152 |
|^^|16:9 |1280x720 |
|^^|9:16 |720x1280 |
|^^|3:2 |1248x832 |
|^^|2:3 |832x1248 |
|^^|21:9 |1512x648 |
|<div style="text-align: center">|1:1 |2048x2048 |\\
|2K</div>| | |\\
| | | |
|^^|4:3 |2304x1728 |
|^^|3:4 |1728x2304 |
|^^|16:9 |2848x1600 |
|^^|9:16 |1600x2848 |
|^^|3:2 |2496x1664 |
|^^|2:3 |1664x2496 |
|^^|21:9 |3136x1344 |
|<div style="text-align: center">|1:1 |4096x4096 |\\
|4K</div>| | |\\
| | | |
|^^|3:4 |3520x4704 |
|^^|4:3 |4704x3520 |
|^^|16:9 |5504x3040 |
|^^|9:16 |3040x5504 |
|^^|2:3 |3328x4992 |
|^^|3:2 |4992x3328 |
|^^|21:9 |6240x2656 |
`}></RenderMd></Tabs.TabPane>
<Tabs.TabPane title="doubao-seedream-3.0-t2i" key="dUuqsxPhNL"><RenderMd content={`指定生成图像的宽高像素值。
* 默认值:\`1024x1024\`
* 单张图片像素取值范围: [\`512x512\`, \`2048x2048\`]
推荐的宽高像素值:
|宽高比 |宽高像素值 |
|---|---|
|1:1 |1024x1024 |
|4:3 |864x1152 |
|3:4 |1152x864 |
|16:9 |1280x720 |
|9:16 |720x1280 |
|3:2 |832x1248 |
|2:3 |1248x832 |
|21:9 |1512x648 |
`}></RenderMd></Tabs.TabPane></Tabs>);
```
---
**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 当前不支持。
---
&nbsp;
<span id="7P96iLnc"></span>
## 响应参数
<span id="Hrya4y9k"></span>
### 流式响应参数
请参见[文档](https://www.volcengine.com/docs/82379/1824137?lang=zh)。
&nbsp;
<span id="1AxnwQZN"></span>
### 非流式响应参数
---
**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`
错误提示信息
&nbsp;
File diff suppressed because it is too large. Load diff
+7
View File
@@ -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)
+34
View File
@@ -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()
+68 -12
View File
@@ -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
}
+25 -8
View File
@@ -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)
+5 -1
View File
@@ -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 != "" {
+71 -3
View File
@@ -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
}
+4
View File
@@ -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 任务状态枚举
+5
View File
@@ -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)
}
+3
View File
@@ -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
}
+23 -2
View File
@@ -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"`
}
+23
View File
@@ -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{
+199
View File
@@ -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
}
+22
View File
@@ -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",
}
+99
View File
@@ -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
}
+45
View File
@@ -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"`
}
+69 -8
View File
@@ -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
}
}