
Aliyun Wan Image
- 62 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Generates and edits images with DashScope Wan 2.7 models, supporting text-to-image, instruction editing, region editing, group generation, and palette control.
About
This skill calls the Wan 2.7 image models to create images from text or edit existing ones with instructions, bounding boxes, sequential groups, and color palettes. A developer uses it for text-to-image and multi-image editing up to 4K.
- Models wan2.7-image (up to 2K) and wan2.7-image-pro (4K)
- Capabilities include bbox editing, sequential groups, and palette control
Aliyun Wan Image by the numbers
- 62 all-time installs (skills.sh)
- Ranked #861 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill aliyun-wan-imageAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Generates and edits images with DashScope Wan 2.7 models, supporting text-to-image, instruction editing, region editing, group generation, and palette control.
Files
Wan 2.7 Image Generation & Editing
Validation
mkdir -p output/aliyun-wan-image
python -m py_compile skills/ai/image/aliyun-wan-image/scripts/generate_image.py && echo "py_compile_ok" > output/aliyun-wan-image/validate.txtPass criteria: command exits 0 and output/aliyun-wan-image/validate.txt is generated.
Output And Evidence
- Write generated image URLs, prompts, and metadata to
output/aliyun-wan-image/. - Keep at least one sample JSON response per run.
Prerequisites
- Install SDK (recommended in a venv):
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscope- Set
DASHSCOPE_API_KEYin your environment, or adddashscope_api_keyto~/.alibabacloud/credentials.
Critical model names
wan2.7-image-pro— professional version, supports 4K outputwan2.7-image— faster generation, up to 2K
Capabilities
| Capability | Description |
|---|---|
| Text-to-image | Generate images from text prompts |
| Image editing | Edit images with text instructions (1-9 input images) |
| Interactive editing | Edit specific regions via bounding boxes (bbox_list) |
| Group generation | Generate consistent multi-image sequences (enable_sequential=true, up to 12 images) |
| Color palette | Control color theme with custom hex+ratio palette (3-10 colors) |
| Thinking mode | Enhanced reasoning for better quality (text-to-image only) |
API endpoint
Sync (recommended):
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generationAsync (for long tasks):
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/image-generation/generation
Header: X-DashScope-Async: enableNormalized interface (image.generate)
Request
prompt(string, required) — up to 5000 characterssize(string, optional) —1K,2K(default),4K(pro only), orWxHpixel valuesn(int, optional) — number of images, 1-4 (default 4), or 1-12 withenable_sequentialseed(int, optional) — range [0, 2147483647]reference_image(string/array, optional) — URL or base64, up to 9 imagesenable_sequential(bool, optional) — group image generation modethinking_mode(bool, optional, default true) — enhanced reasoning (text-to-image only)bbox_list(array, optional) — bounding boxes for interactive editingcolor_palette(array, optional) — custom color theme (3-10 colors with hex+ratio)watermark(bool, optional, default false)
Response
image_url(string) — PNG, valid for 24 hoursimage_count(int)size(string) — actual output resolutionseed(int)
Quick start (Python + DashScope SDK)
import os
from dashscope.aigc.image_generation import ImageGeneration
def generate_image(req: dict) -> dict:
messages = [
{
"role": "user",
"content": [{"text": req["prompt"]}],
}
]
# Add reference images if provided
ref_images = req.get("reference_images") or []
if req.get("reference_image"):
ref_images = [req["reference_image"]] + ref_images
for img in ref_images:
messages[0]["content"].append({"image": img})
params = {
"model": req.get("model", "wan2.7-image"),
"messages": messages,
"size": req.get("size", "2K"),
"n": req.get("n", 1),
"api_key": os.getenv("DASHSCOPE_API_KEY"),
"seed": req.get("seed"),
"watermark": req.get("watermark", False),
}
if req.get("enable_sequential"):
params["enable_sequential"] = True
if req.get("thinking_mode") is not None:
params["thinking_mode"] = req["thinking_mode"]
if req.get("bbox_list"):
params["bbox_list"] = req["bbox_list"]
if req.get("color_palette"):
params["color_palette"] = req["color_palette"]
response = ImageGeneration.call(**params)
content = response.output["choices"][0]["message"]["content"]
images = [item["image"] for item in content if isinstance(item, dict) and item.get("image")]
return {
"image_urls": images,
"image_count": response.usage.get("image_count"),
"size": response.usage.get("size"),
}Size reference
| Model | Supported sizes | Default |
|---|---|---|
| wan2.7-image-pro | 1K, 2K, 4K (text-to-image only), or [768, 4096] px | 2K |
| wan2.7-image | 1K, 2K, or [768, 2048] px | 2K |
Error handling
| Error | Likely cause | Action |
|---|---|---|
| 401/403 | Missing or invalid DASHSCOPE_API_KEY | Check env var or credentials file. |
400 InvalidParameter | Unsupported size, bad n value, or missing required image | Validate parameters against model limits. |
| 429 | Rate limit or quota | Retry with backoff. |
Output location
- Default output:
output/aliyun-wan-image/images/ - Override base dir with
OUTPUT_DIR.
Anti-patterns
- Do not invent model names; use
wan2.7-imageorwan2.7-image-proonly. - Do not use 4K size with
wan2.7-image(only pro supports 4K). - Do not use
enable_sequentialwithbbox_list— they are separate modes. - Image URLs expire after 24 hours; download and persist immediately.
Workflow
1) Confirm user intent: text-to-image, image editing, group generation, or interactive editing. 2) Select appropriate model (pro for 4K or higher quality, standard for speed). 3) Execute with explicit parameters and bounded scope. 4) Download and save generated images before URL expiration.
References
- See
references/api_reference.mdfor full HTTP API details. - See
references/sources.mdfor source links.
DashScope SDK Reference (Wan 2.7 Image)
Install
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscopeEnvironment
export DASHSCOPE_API_KEY=your_keyOr place dashscope_api_key under [default] in ~/.alibabacloud/credentials.
Models
| Model | Max Resolution | Speed |
|---|---|---|
| wan2.7-image-pro | 4K (4096x4096) | Standard |
| wan2.7-image | 2K (2048x2048) | Faster |
API endpoints
- Sync:
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation - Async: Same URL under
/image-generation/generationwith headerX-DashScope-Async: enable - Singapore: Replace
dashscope.aliyuncs.comwithdashscope-intl.aliyuncs.com
Request format
Uses messages format with role: "user" and content array containing text and optional image objects.
from dashscope.aigc.image_generation import ImageGeneration
response = ImageGeneration.call(
model="wan2.7-image",
messages=[{
"role": "user",
"content": [
{"text": "a beautiful sunset over mountains"},
]
}],
size="2K",
n=1,
watermark=False,
thinking_mode=True,
seed=42,
api_key=os.getenv("DASHSCOPE_API_KEY"),
)Parameters
| Parameter | Type | Notes |
|---|---|---|
| size | string | "1K", "2K", "4K" (pro only), or "WxH" pixels |
| n | int | 1-4 (standard), 1-12 (sequential mode) |
| enable_sequential | bool | Group image generation |
| thinking_mode | bool | Enhanced reasoning (text-to-image only, default true) |
| bbox_list | array | Bounding boxes for interactive editing |
| color_palette | array | Custom colors (3-10 items with hex+ratio) |
| watermark | bool | Add "AI generated" watermark |
| seed | int | [0, 2147483647] for reproducibility |
Image input limits
- Formats: JPEG, JPG, PNG (no transparency), BMP, WEBP
- Resolution: [240, 8000] px per side, aspect ratio [1:8, 8:1]
- File size: ≤ 20MB
- Count: 0-9 images
Response parsing
content = response.output["choices"][0]["message"]["content"]
images = [item["image"] for item in content if isinstance(item, dict) and item.get("image")]
image_count = response.usage.get("image_count")
size = response.usage.get("size")Notes
- Image URLs expire after 24 hours; download immediately.
thinking_modeonly applies to text-to-image (no image input, no sequential mode).color_paletteratios must sum to exactly 100.00%.- 4K output only available for
wan2.7-image-proin text-to-image mode.
#!/usr/bin/env python3
"""Generate or edit images using DashScope (wan2.7-image) from a normalized request.
Usage:
python scripts/generate_image.py --request '{"prompt":"a cat in a garden","size":"2K"}'
python scripts/generate_image.py --file request.json --output output/aliyun-wan-image/images/cat.png
"""
from __future__ import annotations
import argparse
import configparser
import json
import os
import sys
import urllib.request
from pathlib import Path
from typing import Any
def _find_repo_root(start: Path) -> Path | None:
for parent in [start] + list(start.parents):
if (parent / ".git").exists():
return parent
return None
def _load_dotenv(path: Path) -> None:
if not path.exists():
return
for line in path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def _load_env() -> None:
_load_dotenv(Path.cwd() / ".env")
repo_root = _find_repo_root(Path(__file__).resolve())
if repo_root:
_load_dotenv(repo_root / ".env")
def _load_dashscope_api_key_from_credentials() -> None:
if os.environ.get("DASHSCOPE_API_KEY"):
return
credentials_path = Path(os.path.expanduser("~/.alibabacloud/credentials"))
if not credentials_path.exists():
return
config = configparser.ConfigParser()
try:
config.read(credentials_path)
except configparser.Error:
return
profile = os.getenv("ALIBABA_CLOUD_PROFILE") or os.getenv("ALICLOUD_PROFILE") or "default"
if not config.has_section(profile):
return
key = config.get(profile, "dashscope_api_key", fallback="").strip()
if not key:
key = config.get(profile, "DASHSCOPE_API_KEY", fallback="").strip()
if key:
os.environ["DASHSCOPE_API_KEY"] = key
try:
from dashscope.aigc.image_generation import ImageGeneration
except ImportError:
print("Error: dashscope is not installed. Run: pip install dashscope", file=sys.stderr)
sys.exit(1)
MODEL_NAME = "wan2.7-image"
DEFAULT_SIZE = "2K"
def load_request(args: argparse.Namespace) -> dict[str, Any]:
if args.request:
return json.loads(args.request)
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
return json.load(f)
raise ValueError("Either --request or --file must be provided")
def _get_field(obj: Any, key: str, default: Any = None) -> Any:
if obj is None:
return default
if isinstance(obj, dict):
return obj.get(key, default)
getter = getattr(obj, "get", None)
if callable(getter):
try:
return getter(key, default)
except TypeError:
value = getter(key)
return default if value is None else value
try:
return obj[key]
except Exception:
return getattr(obj, key, default)
def call_generate(req: dict[str, Any]) -> dict[str, Any]:
prompt = req.get("prompt")
if not prompt:
raise ValueError("prompt is required")
messages = [{"role": "user", "content": [{"text": prompt}]}]
# Add reference images
ref_images = req.get("reference_images") or []
if req.get("reference_image"):
ref_images = [req["reference_image"]] + ref_images
for img in ref_images:
messages[0]["content"].append({"image": img})
params: dict[str, Any] = {
"model": req.get("model", MODEL_NAME),
"messages": messages,
"size": req.get("size", DEFAULT_SIZE),
"n": req.get("n", 1),
"api_key": os.getenv("DASHSCOPE_API_KEY"),
"watermark": req.get("watermark", False),
}
if req.get("seed") is not None:
params["seed"] = req["seed"]
if req.get("enable_sequential"):
params["enable_sequential"] = True
if req.get("thinking_mode") is not None:
params["thinking_mode"] = req["thinking_mode"]
if req.get("bbox_list"):
params["bbox_list"] = req["bbox_list"]
if req.get("color_palette"):
params["color_palette"] = req["color_palette"]
response = ImageGeneration.call(**params)
output = _get_field(response, "output", response.output if hasattr(response, "output") else None)
choices = _get_field(output, "choices", [])
if not choices:
raise RuntimeError(f"No choices returned by DashScope: {response}")
message = _get_field(choices[0], "message", {})
content = _get_field(message, "content", [])
image_urls = []
for item in content:
if isinstance(item, dict) and item.get("image"):
image_urls.append(item["image"])
if not image_urls:
raise RuntimeError("No image URL returned by DashScope")
usage = _get_field(response, "usage", response.usage if hasattr(response, "usage") else {})
return {
"image_urls": image_urls,
"image_count": _get_field(usage, "image_count"),
"size": _get_field(usage, "size"),
"seed": req.get("seed"),
}
def download_image(image_url: str, output_path: Path) -> None:
output_path.parent.mkdir(parents=True, exist_ok=True)
with urllib.request.urlopen(image_url) as response:
output_path.write_bytes(response.read())
def main() -> None:
parser = argparse.ArgumentParser(description="Generate/edit images with wan2.7-image")
parser.add_argument("--request", help="Inline JSON request string")
parser.add_argument("--file", help="Path to JSON request file")
default_output_dir = Path(os.getenv("OUTPUT_DIR", "output")) / "aliyun-wan-image" / "images"
parser.add_argument(
"--output",
default=str(default_output_dir / "output.png"),
help="Output image path (for first image; others get _N suffix)",
)
parser.add_argument("--print-response", action="store_true", help="Print normalized response JSON")
args = parser.parse_args()
_load_env()
_load_dashscope_api_key_from_credentials()
if not os.environ.get("DASHSCOPE_API_KEY"):
print(
"Error: DASHSCOPE_API_KEY is not set. Configure it via env/.env or ~/.alibabacloud/credentials.",
file=sys.stderr,
)
sys.exit(1)
req = load_request(args)
result = call_generate(req)
output_path = Path(args.output)
for i, url in enumerate(result["image_urls"]):
if i == 0:
path = output_path
else:
path = output_path.with_stem(f"{output_path.stem}_{i}")
download_image(url, path)
print(f"Saved: {path}")
if args.print_response:
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()