LoopToken
图片生成

Wan 2.6 Image

Wan 2.6 Image 图像编辑 API

wan2.6-image 通过参考图和文本指令完成图像编辑、多图融合、风格迁移、主体保持和图片文字生成。LoopToken 当前稳定提供同步图像编辑;图文混排与流式能力不在稳定合同内。

接口

路径为 POST /v1/images/generations,完整地址如下:

POST https://api.vibelab.me/v1/images/generations
Authorization: Bearer <your-api-key>
Content-Type: application/json

请求采用 input.messages + parameters 结构。当前只传一条 role: "user" 消息;其 content 必须且只能有一个 text 对象,并在其后放 1~4 个 image 对象。采用文字在前、图片在后的固定写法,便于审查图片编号与 content 数组中的顺序。

Wan 2.7

wan2.7-imagewan2.7-image-pro 使用扁平扩展格式,支持文生图、编辑、参考图与序列生成,不使用下文 Wan 2.6 的 DashScope 消息结构。常用字段包括 promptimage_urlsnsize / resolutionnegative_promptwatermarkseedthinking_modeenable_sequential。标准版最高 2K;Pro 版文生图最高 4K,编辑与序列生成最高 2K。

{
  "model": "wan2.7-image-pro",
  "prompt": "四季中的同一只橘猫,连续分镜",
  "size": "2K",
  "enable_sequential": true,
  "n": 4
}

Wan 2.7 按成功图片数计费,分辨率和宽高比不改变单张价格;响应统一为 OpenAI Images 格式。

能力边界

能力LoopToken 稳定支持约束
图像编辑1~4 张参考图,enable_interleave=false
多图融合“图1”“图2”按图片对象出现顺序编号
多张输出parameters.n 为 1~4;建议显式传 1
图文混排与流式边界模型服务要求流式专用请求头;当前网关不转发该头且缓冲响应
纯文生图编辑模式至少需要一张图;请改用文生图模型
Base64 输出响应图片是 PNG 临时 URL,不返回 b64_json

参数

参数类型必填说明
modelstring固定为 wan2.6-image
input.messagesarray仅一条用户消息,不支持多轮
input.messages[0].content[].textstring一个且仅一个;中英文,最多 2000 字符,超出部分可能被截断
input.messages[0].content[].imagestring1~4 个公网 URL 或 Base64 Data URL
parameters.enable_interleaveboolean建议固定传 false,选择同步图像编辑
parameters.ninteger1~4;模型服务文档默认 4,但 LoopToken 在省略或传非正数时按 1 预扣及兜底结算。请显式传值,低成本调用传 1
parameters.sizestring1K(默认)、2K宽*高
parameters.negative_promptstring反向提示词,最多 500 字符
parameters.prompt_extendboolean默认 true;仅编辑模式生效
parameters.watermarkboolean默认 falsetrue 添加“AI生成”水印
parameters.seedinteger0~2147483647;相同值不保证完全一致

输入图片限制

  • 格式:JPEG、JPG、PNG(不支持透明通道)、BMP、WEBP。
  • 单图宽、高均为 240~8000 像素;单图不超过 10 MB。
  • 公网 URL 必须允许模型服务直接下载,支持 HTTP/HTTPS;不要使用需登录、短时签名已过期或内网地址。
  • Base64 使用完整 Data URL:data:image/png;base64,<base64-data>。后端最多读取请求体前 20 MiB;这不是带专用错误码的可靠限额合同,超出后通常会因 JSON 被截断而返回 invalid_request。客户端应让编码后的整个 JSON 明显低于此边界。
  • 多图时,提示词中的“图1”“图2”严格对应 content 中图片对象的先后顺序。输出比例默认跟随最后一张输入图。

Wan 2.6 尺寸表

parameters.size像素规模宽高比
1K接近 1280*1280跟随最后一张输入图
2K接近 2048*2048跟随最后一张输入图
像素值768*7682048*2048 总像素1:44:1

像素值格式使用星号,例如 1280*720。实际宽高会调整为接近指定值的 16 的倍数。常用比例可选 1280*1280(1:1)、800*1200(2:3)、1200*800(3:2)、960*1280(3:4)、1280*960(4:3)、720*1280(9:16)、1280*720(16:9)和 1344*576(21:9)。

请求示例

最小 URL 引用

{
  "model": "wan2.6-image",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        {"text": "把背景改成夜晚的上海街景,保持人物不变"},
        {"image": "https://example.com/source.jpg"}
      ]
    }]
  },
  "parameters": {"enable_interleave": false, "n": 1}
}

Base64 Data URL

{
  "model": "wan2.6-image",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        {"text": "移除桌面杂物,保留产品和阴影"},
        {"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..."}
      ]
    }]
  },
  "parameters": {"enable_interleave": false, "n": 1, "prompt_extend": false}
}

多参考图、尺寸与数量

{
  "model": "wan2.6-image",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        {"text": "让图1的产品使用图2的配色,生成两张中文电商海报,标题为“夏日新色”"},
        {"image": "https://example.com/product.png"},
        {"image": "https://example.com/palette.jpg"}
      ]
    }]
  },
  "parameters": {
    "enable_interleave": false,
    "n": 2,
    "size": "2K",
    "negative_prompt": "文字模糊,产品变形",
    "prompt_extend": true,
    "watermark": false,
    "seed": 42
  }
}

完整 curl:上传本地图片并下载全部结果

IMAGE_DATA=$(base64 < source.png | tr -d '\n')
jq -n --arg image "data:image/png;base64,$IMAGE_DATA" '{
  model: "wan2.6-image",
  input: {messages: [{role: "user", content: [
    {text: "把背景改成纯白摄影棚,保留主体细节"},
    {image: $image}
  ]}]},
  parameters: {enable_interleave: false, n: 1, size: "1K"}
}' > request.json

curl -sS https://api.vibelab.me/v1/images/generations \
  -H "Authorization: Bearer $LOOPTOKEN_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @request.json > response.json

jq -e '.output.choices | type == "array"' response.json > /dev/null

jq -r '.output.choices[] | .message.content[] | select(.image) | .image' response.json |
while IFS= read -r url; do
  index=$(( ${index:-0} + 1 ))
  curl -L --fail --output "wan-$index.png" "$url"
done

响应

{
  "output": {
    "choices": [{
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": [{
          "image": "https://example.com/temporary-result.png",
          "type": "image"
        }]
      }
    }],
    "finished": true
  },
  "usage": {"image_count": 1, "size": "1280*1280"}
}

逐个遍历 output.choices[].message.content[],仅处理含 image 的项;结果按 choicescontent 的顺序返回。图片是临时 PNG URL,请立即下载并持久化。不要依赖 usage.image_count 替代数组遍历。

Python:下载全部图片

import os
from pathlib import Path
from urllib.request import Request, urlopen, urlretrieve
import json

payload = {"model": "wan2.6-image", "input": {"messages": [{"role": "user", "content": [
    {"text": "将图片改成水彩插画"}, {"image": "https://example.com/source.jpg"}
]}]}, "parameters": {"enable_interleave": False, "n": 1}}
request = Request("https://api.vibelab.me/v1/images/generations",
    data=json.dumps(payload).encode(), headers={"Authorization": f"Bearer {os.environ['LOOPTOKEN_API_KEY']}", "Content-Type": "application/json"})
with urlopen(request) as response:
    data = json.load(response)
index = 0
for choice in data["output"]["choices"]:
    for item in choice["message"]["content"]:
        if item.get("image"):
            index += 1
            urlretrieve(item["image"], Path(f"wan-{index}.png"))

Node.js:下载全部图片

import { writeFile } from 'node:fs/promises';

const response = await fetch('https://api.vibelab.me/v1/images/generations', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.LOOPTOKEN_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ model: 'wan2.6-image', input: { messages: [{ role: 'user', content: [
    { text: '将图片改成水彩插画' }, { image: 'https://example.com/source.jpg' }
  ] }] }, parameters: { enable_interleave: false, n: 1 } }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const data = await response.json();
let index = 0;
for (const choice of data.output.choices) {
  for (const item of choice.message.content) {
    if (!item.image) continue;
    const image = await fetch(item.image);
    if (!image.ok) throw new Error(`download failed: ${image.status}`);
    await writeFile(`wan-${++index}.png`, Buffer.from(await image.arrayBuffer()));
  }
}

错误与计费

LoopToken 在鉴权、余额、模型、JSON 等本地检查失败时返回统一错误对象:

{"error":{"message":"invalid image generation request","type":"invalid_request","code":"invalid_request","request_id":"req_example"}}

固定的本地状态包括:400 invalid_request(请求体无法读取、JSON 截断或格式错误、model 缺失)、401 API Key 缺失或无效、402 insufficient_credits、403 model_not_allowed、404 model_not_found,以及计费或请求构建异常时的 500 internal_error。参考图格式、数量、顺序、尺寸、输出数量和安全策略主要由模型服务校验;素材无效、参数不支持、内容拒绝或生成失败不会保留模型服务错误码。

模型服务终止性非 2xx 或网络失败通常返回 HTTP 502code: "upstream_error"。当前没有可处理请求的服务资源时返回 HTTP 503code: "no_available_channel";一次可重试失败后若也无法继续处理,同一次请求也可能最终返回 503。两者都会退款,客户端应把 502 和 503 都视为暂时不可完成,可采用带抖动的有限退避重试;不要依赖模型服务原始错误体。成功的 2xx 响应则原样返回,LoopToken 请求 ID 只位于 X-Request-Id 响应头。

本模型按图片计费,最终价格以模型列表为准。网关预扣时使用显式提交的正整数 parameters.n;省略或传非正数时,本地计费值回退为 1,但转发给模型服务的请求体不会因此补写 n: 1。正常 JSON 响应按 output.choices[].message.content[].image 中每张成功识别的输出图片结算;若 2xx 响应无法识别图片而得到 Count=0,网关使用上述本地计费值兜底结算。usage 只表示模型服务用量,不是 credit 金额。供应商文档所述默认输出 4 张与此本地回退值是两件事,因此务必显式设置 n,并完整保存响应用于排查。

本页内容