Wan 2.7 Image-to-Video
Validation
mkdir -p output/aliyun-wan-i2v
python -m py_compile skills/ai/video/aliyun-wan-i2v/scripts/generate_i2v.py && echo "py_compile_ok" > output/aliyun-wan-i2v/validate.txt
Pass criteria: command exits 0 and output/aliyun-wan-i2v/validate.txt is generated.
Output And Evidence
- Save task IDs, polling responses, and final video URLs to
output/aliyun-wan-i2v/.
- Keep at least one end-to-end run log for troubleshooting.
Prerequisites
- Install SDK (recommended in a venv):
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscope
- Set
DASHSCOPE_API_KEY in your environment, or add dashscope_api_key to ~/.alibabacloud/credentials.
Critical model names
wan2.7-i2v — supports first-frame, first+last frame, video continuation, and audio-driven generation
Capabilities
| Capability |
Description |
Required media types |
| First-frame video |
Generate video from a single image |
first_frame |
| First+last frame |
Interpolate video between two images |
first_frame + last_frame |
| Video continuation |
Extend an existing video clip |
first_clip |
| Audio-driven |
Drive video with audio (lip-sync, rhythm) |
first_frame + driving_audio |
API endpoint (async only)
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis
Required headers:
Authorization: Bearer $DASHSCOPE_API_KEY
Content-Type: application/json
X-DashScope-Async: enable
Singapore endpoint: replace dashscope.aliyuncs.com with dashscope-intl.aliyuncs.com.
Normalized interface
Request
prompt (string, optional) — up to 5000 characters, describes desired video content
negative_prompt (string, optional) — up to 500 characters
media (array, required) — media objects with type and url fields:
type: first_frame | last_frame | driving_audio | first_clip
url: public URL (HTTP/HTTPS) or OSS temporary URL
resolution (string, optional) — 720P or 1080P (default: 1080P)
duration (integer, optional) — video length in seconds, range [2, 15] (default: 5)
prompt_extend (boolean, optional) — AI prompt rewriting (default: true)
watermark (boolean, optional) — add "AI generated" watermark (default: false)
seed (integer, optional) — range [0, 2147483647]
Media input limits
Images (first_frame, last_frame):
- Formats: JPEG, JPG, PNG (no transparency), BMP, WEBP
- Resolution: [240, 8000] pixels per side
- Aspect ratio: 1:8 to 8:1
- Max size: 20MB
Audio (driving_audio):
- Formats: wav, mp3
- Duration: 2-30s
- Max size: 15MB
- Auto-truncated to
duration value if longer
Video (first_clip):
- Formats: mp4, mov
- Duration: 2-10s
- Resolution: [240, 4096] pixels per side
- Aspect ratio: 1:8 to 8:1
- Max size: 100MB
Response (task creation)
output.task_id (string) — use for polling, valid 24 hours
output.task_status (string) — PENDING | RUNNING | SUCCEEDED | FAILED | CANCELED
request_id (string)
Response (task result)
output.video_url (string) — generated video URL
output.orig_prompt (string) — original prompt
output.actual_prompt (string) — rewritten prompt (if prompt_extend enabled)
usage.video_count (integer)
usage.video_duration (integer) — duration in seconds
Quick start (Python + HTTP)
import os
import json
import time
import requests
API_KEY = os.getenv("DASHSCOPE_API_KEY")
BASE_URL = "https://dashscope.aliyuncs.com/api/v1"
def create_i2v_task(req: dict) -> str:
"""Create an image-to-video task and return task_id."""
payload = {
"model": "wan2.7-i2v",
"input": {
"prompt": req.get("prompt", ""),
"media": req["media"],
},
"parameters": {
"resolution": req.get("resolution", "1080P"),
"duration": req.get("duration", 5),
"prompt_extend": req.get("prompt_extend", True),
"watermark": req.get("watermark", False),
},
}
if req.get("negative_prompt"):
payload["input"]["negative_prompt"] = req["negative_prompt"]
if req.get("seed") is not None:
payload["parameters"]["seed"] = req["seed"]
resp = requests.post(
f"{BASE_URL}/services/aigc/video-generation/video-synthesis",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"X-DashScope-Async": "enable",
},
json=payload,
)
resp.raise_for_status()
data = resp.json()
return data["output"]["task_id"]
def poll_task(task_id: str, interval: int = 15) -> dict:
"""Poll until task completes. Returns final response."""
while True:
resp = requests.get(
f"{BASE_URL}/tasks/{task_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
resp.raise_for_status()
data = resp.json()
status = data["output"]["task_status"]
if status in ("SUCCEEDED", "FAILED", "CANCELED"):
return data
time.sleep(interval)
Media combination examples
# First-frame only
media = [{"type": "first_frame", "url": "https://example.com/image.jpg"}]
# First + last frame interpolation
media = [
{"type": "first_frame", "url": "https://example.com/start.jpg"},
{"type": "last_frame", "url": "https://example.com/end.jpg"},
]
# Audio-driven from first frame
media = [
{"type": "first_frame", "url": "https://example.com/face.jpg"},
{"type": "driving_audio", "url": "https://example.com/speech.mp3"},
]
# Video continuation
media = [{"type": "first_clip", "url": "https://example.com/clip.mp4"}]
Error handling
| Error |
Likely cause |
Action |
| 401/403 |
Missing or invalid DASHSCOPE_API_KEY |
Check env var or credentials file |
400 InvalidParameter |
Unsupported resolution, bad duration, missing media |
Validate parameters |
| "does not support synchronous calls" |
Missing X-DashScope-Async: enable header |
Add required header |
| 429 |
Rate limit or quota |
Retry with backoff |
Output location
- Default output:
output/aliyun-wan-i2v/videos/
- Override base dir with
OUTPUT_DIR.
Anti-patterns
- Do not use model names other than
wan2.7-i2v.
- Do not call this API synchronously — async header is required.
- Do not pass duplicate media types (e.g., two
first_frame entries).
- Video URLs expire after 24 hours; download and persist immediately.
- Do not use this API for video editing — use
aliyun-wan-videoedit instead.
Workflow
- Confirm user intent: first-frame, first+last frame, video continuation, or audio-driven.
- Prepare media array with correct types and valid URLs.
- Create async task and poll for results.
- Download and save generated video before URL expiration.
References
- See
references/api_reference.md for full HTTP API details.
- See
references/sources.md for source links.
1---2name: aliyun-wan-i2v3description: Use when generating videos from images with DashScope Wan 2.7 image-to-video model (wan2.7-i2v). Use when implementing first-frame video generation, first+last frame interpolation, video continuation, or audio-driven video synthesis via the video-synthesis async API.4---5
6# Wan 2.7 Image-to-Video
7
8## Validation
9
10```bash
11mkdir -p output/aliyun-wan-i2v
12python -m py_compile skills/ai/video/aliyun-wan-i2v/scripts/generate_i2v.py && echo "py_compile_ok" > output/aliyun-wan-i2v/validate.txt
13```
14
15Pass criteria: command exits 0 and `output/aliyun-wan-i2v/validate.txt` is generated.
16
17## Output And Evidence
18
19- Save task IDs, polling responses, and final video URLs to `output/aliyun-wan-i2v/`.
20- Keep at least one end-to-end run log for troubleshooting.
21
22## Prerequisites
23
24- Install SDK (recommended in a venv):
25
26```bash
27python3 -m venv .venv
28. .venv/bin/activate
29python -m pip install dashscope
30```
31- Set `DASHSCOPE_API_KEY` in your environment, or add `dashscope_api_key` to `~/.alibabacloud/credentials`.
32
33## Critical model names
34
35- `wan2.7-i2v` — supports first-frame, first+last frame, video continuation, and audio-driven generation
36
37## Capabilities
38
39| Capability | Description | Required media types |
40|---|---|---|
41| First-frame video | Generate video from a single image | `first_frame` |
42| First+last frame | Interpolate video between two images | `first_frame` + `last_frame` |
43| Video continuation | Extend an existing video clip | `first_clip` |
44| Audio-driven | Drive video with audio (lip-sync, rhythm) | `first_frame` + `driving_audio` |
45
46## API endpoint (async only)
47
48```
49POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis
50```
51
52Required headers:
53- `Authorization: Bearer $DASHSCOPE_API_KEY`
54- `Content-Type: application/json`
55- `X-DashScope-Async: enable`
56
57Singapore endpoint: replace `dashscope.aliyuncs.com` with `dashscope-intl.aliyuncs.com`.
58
59## Normalized interface
60
61### Request
62- `prompt` (string, optional) — up to 5000 characters, describes desired video content
63- `negative_prompt` (string, optional) — up to 500 characters
64- `media` (array, required) — media objects with `type` and `url` fields:
65 - `type`: `first_frame` | `last_frame` | `driving_audio` | `first_clip`
66 - `url`: public URL (HTTP/HTTPS) or OSS temporary URL
67- `resolution` (string, optional) — `720P` or `1080P` (default: `1080P`)
68- `duration` (integer, optional) — video length in seconds, range [2, 15] (default: 5)
69- `prompt_extend` (boolean, optional) — AI prompt rewriting (default: true)
70- `watermark` (boolean, optional) — add "AI generated" watermark (default: false)
71- `seed` (integer, optional) — range [0, 2147483647]
72
73### Media input limits
74
75**Images** (first_frame, last_frame):
76- Formats: JPEG, JPG, PNG (no transparency), BMP, WEBP
77- Resolution: [240, 8000] pixels per side
78- Aspect ratio: 1:8 to 8:1
79- Max size: 20MB
80
81**Audio** (driving_audio):
82- Formats: wav, mp3
83- Duration: 2-30s
84- Max size: 15MB
85- Auto-truncated to `duration` value if longer
86
87**Video** (first_clip):
88- Formats: mp4, mov
89- Duration: 2-10s
90- Resolution: [240, 4096] pixels per side
91- Aspect ratio: 1:8 to 8:1
92- Max size: 100MB
93
94### Response (task creation)
95- `output.task_id` (string) — use for polling, valid 24 hours
96- `output.task_status` (string) — PENDING | RUNNING | SUCCEEDED | FAILED | CANCELED
97- `request_id` (string)
98
99### Response (task result)
100- `output.video_url` (string) — generated video URL
101- `output.orig_prompt` (string) — original prompt
102- `output.actual_prompt` (string) — rewritten prompt (if prompt_extend enabled)
103- `usage.video_count` (integer)
104- `usage.video_duration` (integer) — duration in seconds
105
106## Quick start (Python + HTTP)
107
108```python
109import os
110import json
111import time
112import requests
113
114API_KEY = os.getenv("DASHSCOPE_API_KEY")
115BASE_URL = "https://dashscope.aliyuncs.com/api/v1"
116
117def create_i2v_task(req: dict) -> str:
118 """Create an image-to-video task and return task_id."""
119 payload = {
120 "model": "wan2.7-i2v",
121 "input": {
122 "prompt": req.get("prompt", ""),
123 "media": req["media"],
124 },
125 "parameters": {
126 "resolution": req.get("resolution", "1080P"),
127 "duration": req.get("duration", 5),
128 "prompt_extend": req.get("prompt_extend", True),
129 "watermark": req.get("watermark", False),
130 },
131 }
132 if req.get("negative_prompt"):
133 payload["input"]["negative_prompt"] = req["negative_prompt"]
134 if req.get("seed") is not None:
135 payload["parameters"]["seed"] = req["seed"]
136
137 resp = requests.post(
138 f"{BASE_URL}/services/aigc/video-generation/video-synthesis",
139 headers={
140 "Authorization": f"Bearer {API_KEY}",
141 "Content-Type": "application/json",
142 "X-DashScope-Async": "enable",
143 },
144 json=payload,
145 )
146 resp.raise_for_status()
147 data = resp.json()
148 return data["output"]["task_id"]
149
150
151def poll_task(task_id: str, interval: int = 15) -> dict:
152 """Poll until task completes. Returns final response."""
153 while True:
154 resp = requests.get(
155 f"{BASE_URL}/tasks/{task_id}",
156 headers={"Authorization": f"Bearer {API_KEY}"},
157 )
158 resp.raise_for_status()
159 data = resp.json()
160 status = data["output"]["task_status"]
161 if status in ("SUCCEEDED", "FAILED", "CANCELED"):
162 return data
163 time.sleep(interval)
164```
165
166## Media combination examples
167
168```python
169# First-frame only
170media = [{"type": "first_frame", "url": "https://example.com/image.jpg"}]
171
172# First + last frame interpolation
173media = [
174 {"type": "first_frame", "url": "https://example.com/start.jpg"},
175 {"type": "last_frame", "url": "https://example.com/end.jpg"},
176]
177
178# Audio-driven from first frame
179media = [
180 {"type": "first_frame", "url": "https://example.com/face.jpg"},
181 {"type": "driving_audio", "url": "https://example.com/speech.mp3"},
182]
183
184# Video continuation
185media = [{"type": "first_clip", "url": "https://example.com/clip.mp4"}]
186```
187
188## Error handling
189
190| Error | Likely cause | Action |
191|---|---|---|
192| 401/403 | Missing or invalid `DASHSCOPE_API_KEY` | Check env var or credentials file |
193| 400 `InvalidParameter` | Unsupported resolution, bad duration, missing media | Validate parameters |
194| "does not support synchronous calls" | Missing `X-DashScope-Async: enable` header | Add required header |
195| 429 | Rate limit or quota | Retry with backoff |
196
197## Output location
198
199- Default output: `output/aliyun-wan-i2v/videos/`
200- Override base dir with `OUTPUT_DIR`.
201
202## Anti-patterns
203
204- Do not use model names other than `wan2.7-i2v`.
205- Do not call this API synchronously — async header is required.
206- Do not pass duplicate media types (e.g., two `first_frame` entries).
207- Video URLs expire after 24 hours; download and persist immediately.
208- Do not use this API for video editing — use `aliyun-wan-videoedit` instead.
209
210## Workflow
211
2121) Confirm user intent: first-frame, first+last frame, video continuation, or audio-driven.
2132) Prepare media array with correct types and valid URLs.
2143) Create async task and poll for results.
2154) Download and save generated video before URL expiration.
216
217## References
218
219- See `references/api_reference.md` for full HTTP API details.
220- See `references/sources.md` for source links.