Skip to content

Commit ca8225e

Browse files
feat(image): add 1280x1280 size support for wan-2.6 (#49)
* feat(image): add 1280x1280 size support for wan-2.6 - add 1280x1280 to supported sizes - update tests and documentation * docs: remove redundant docs/image_models.md README.md already contains all image model documentation including: - Supported models table - Model-specific notes (WAN 2.6, Pruna, Nano Banana Pro, Qwen) - Provider options - Examples The separate file was outdated and created maintenance burden.
1 parent 2852273 commit ca8225e

File tree

4 files changed

+358
-73
lines changed

4 files changed

+358
-73
lines changed

README.md

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -278,22 +278,22 @@ Check out our [examples](https://github.com/runpod/examples/tree/main/ai-sdk/get
278278

279279
### Supported Models
280280

281-
| Model ID | Type | Resolution | Aspect Ratios |
282-
| -------------------------------------- | ---- | --------------- | ----------------------------------------- |
283-
| `alibaba/wan-2.6` | t2i | 1024x1024 | 1:1, 4:3, 3:4 |
284-
| `pruna/p-image-t2i` | t2i | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
285-
| `pruna/p-image-edit` | edit | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
286-
| `google/nano-banana-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
287-
| `google/nano-banana-pro-edit` | edit | 1k, 2k, 4k | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9 |
288-
| `bytedance/seedream-3.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
289-
| `bytedance/seedream-4.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
290-
| `bytedance/seedream-4.0-edit` | edit | up to 4096x4096 | uses size |
291-
| `qwen/qwen-image` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
292-
| `qwen/qwen-image-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
293-
| `qwen/qwen-image-edit-2511` | edit | up to 1536x1536 | 1:1, 4:3, 3:4 |
294-
| `black-forest-labs/flux-1-schnell` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
295-
| `black-forest-labs/flux-1-dev` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
296-
| `black-forest-labs/flux-1-kontext-dev` | edit | up to 2048x2048 | 1:1, 4:3, 3:4 |
281+
| Model ID | Type | Resolution | Aspect Ratios |
282+
| -------------------------------------- | ---- | ----------------- | ----------------------------------------------- |
283+
| `alibaba/wan-2.6` | t2i | 768x768–1280x1280 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9, 9:21 |
284+
| `pruna/p-image-t2i` | t2i | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
285+
| `pruna/p-image-edit` | edit | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
286+
| `google/nano-banana-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
287+
| `google/nano-banana-pro-edit` | edit | 1k, 2k, 4k | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9 |
288+
| `bytedance/seedream-3.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
289+
| `bytedance/seedream-4.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
290+
| `bytedance/seedream-4.0-edit` | edit | up to 4096x4096 | uses size |
291+
| `qwen/qwen-image` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
292+
| `qwen/qwen-image-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
293+
| `qwen/qwen-image-edit-2511` | edit | up to 1536x1536 | 1:1, 4:3, 3:4 |
294+
| `black-forest-labs/flux-1-schnell` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
295+
| `black-forest-labs/flux-1-dev` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
296+
| `black-forest-labs/flux-1-kontext-dev` | edit | up to 2048x2048 | 1:1, 4:3, 3:4 |
297297

298298
For the full list of models, see the [Runpod Public Endpoint Reference](https://docs.runpod.io/hub/public-endpoint-reference).
299299

@@ -355,6 +355,38 @@ const { image } = await generateImage({
355355
});
356356
```
357357

358+
#### Alibaba (WAN 2.6)
359+
360+
Text-to-image model with flexible resolution support.
361+
362+
**Resolution constraints:**
363+
364+
- Total pixels: 589,824 (768x768) to 1,638,400 (1280x1280)
365+
- Aspect ratio: 1:4 to 4:1
366+
- Default: 1280x1280
367+
368+
**Recommended resolutions for common aspect ratios:**
369+
370+
| Aspect Ratio | Resolution |
371+
| :----------- | :--------- |
372+
| 1:1 | 1280x1280 |
373+
| 2:3 | 800x1200 |
374+
| 3:2 | 1200x800 |
375+
| 3:4 | 960x1280 |
376+
| 4:3 | 1280x960 |
377+
| 9:16 | 720x1280 |
378+
| 16:9 | 1280x720 |
379+
| 21:9 | 1344x576 |
380+
| 9:21 | 576x1344 |
381+
382+
```ts
383+
const { image } = await generateImage({
384+
model: runpod.image('alibaba/wan-2.6'),
385+
prompt: 'A serene mountain landscape at dawn',
386+
aspectRatio: '16:9',
387+
});
388+
```
389+
358390
#### Google (Nano Banana Pro)
359391

360392
| Option | Values |

docs/image_models.md

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/runpod-image-model.test.ts

Lines changed: 229 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,197 @@ describe('RunpodImageModel', () => {
158158
});
159159
});
160160

161+
describe('WAN 2.6 parameter validation', () => {
162+
let wanModel: RunpodImageModel;
163+
164+
beforeEach(() => {
165+
wanModel = new RunpodImageModel('alibaba/wan-2.6', {
166+
provider: 'runpod',
167+
baseURL: 'https://api.runpod.ai/v2/wan-2-6-t2i',
168+
headers: () => ({ Authorization: 'Bearer test-key' }),
169+
fetch: mockFetch,
170+
});
171+
});
172+
173+
it('should accept valid WAN 2.6 sizes within pixel constraints', async () => {
174+
// Valid sizes within 768*768 to 1280*1280 pixel range
175+
const validSizes = [
176+
'1280x1280', // 1,638,400 pixels (max)
177+
'1024x1024', // 1,048,576 pixels
178+
'768x768', // 589,824 pixels (min)
179+
'1280x720', // 921,600 pixels (16:9)
180+
'720x1280', // 921,600 pixels (9:16)
181+
'1200x800', // 960,000 pixels (3:2)
182+
'800x1200', // 960,000 pixels (2:3)
183+
];
184+
185+
for (const size of validSizes) {
186+
// Should not throw - we just verify the size is passed through
187+
mockFetch.mockImplementationOnce(() =>
188+
Promise.resolve(
189+
new Response(
190+
JSON.stringify({
191+
id: 'test',
192+
status: 'COMPLETED',
193+
output: { result: 'https://test.com/img.png' },
194+
}),
195+
{ headers: { 'content-type': 'application/json' } }
196+
)
197+
)
198+
);
199+
mockFetch.mockImplementationOnce(() =>
200+
Promise.resolve(
201+
new Response(new Uint8Array([1, 2, 3]), {
202+
headers: { 'content-type': 'image/png' },
203+
})
204+
)
205+
);
206+
207+
await expect(
208+
wanModel.doGenerate({
209+
prompt: 'Test',
210+
n: 1,
211+
size,
212+
aspectRatio: undefined,
213+
seed: undefined,
214+
providerOptions: {},
215+
headers: {},
216+
abortSignal: undefined,
217+
})
218+
).resolves.toBeDefined();
219+
}
220+
});
221+
222+
it('should throw error for WAN 2.6 size exceeding max pixels', async () => {
223+
await expect(
224+
wanModel.doGenerate({
225+
prompt: 'Test',
226+
n: 1,
227+
size: '2048x2048', // 4,194,304 pixels - exceeds max
228+
aspectRatio: undefined,
229+
seed: undefined,
230+
providerOptions: {},
231+
headers: {},
232+
abortSignal: undefined,
233+
})
234+
).rejects.toThrow(InvalidArgumentError);
235+
});
236+
237+
it('should throw error for WAN 2.6 size below min pixels', async () => {
238+
await expect(
239+
wanModel.doGenerate({
240+
prompt: 'Test',
241+
n: 1,
242+
size: '512x512', // 262,144 pixels - below min
243+
aspectRatio: undefined,
244+
seed: undefined,
245+
providerOptions: {},
246+
headers: {},
247+
abortSignal: undefined,
248+
})
249+
).rejects.toThrow(InvalidArgumentError);
250+
});
251+
252+
it('should accept all WAN 2.6 supported aspect ratios', async () => {
253+
const supportedRatios = [
254+
'1:1',
255+
'2:3',
256+
'3:2',
257+
'3:4',
258+
'4:3',
259+
'9:16',
260+
'16:9',
261+
'21:9',
262+
'9:21',
263+
];
264+
265+
for (const ratio of supportedRatios) {
266+
mockFetch.mockImplementationOnce(() =>
267+
Promise.resolve(
268+
new Response(
269+
JSON.stringify({
270+
id: 'test',
271+
status: 'COMPLETED',
272+
output: { result: 'https://test.com/img.png' },
273+
}),
274+
{ headers: { 'content-type': 'application/json' } }
275+
)
276+
)
277+
);
278+
mockFetch.mockImplementationOnce(() =>
279+
Promise.resolve(
280+
new Response(new Uint8Array([1, 2, 3]), {
281+
headers: { 'content-type': 'image/png' },
282+
})
283+
)
284+
);
285+
286+
await expect(
287+
wanModel.doGenerate({
288+
prompt: 'Test',
289+
n: 1,
290+
size: undefined,
291+
aspectRatio: ratio,
292+
seed: undefined,
293+
providerOptions: {},
294+
headers: {},
295+
abortSignal: undefined,
296+
})
297+
).resolves.toBeDefined();
298+
}
299+
});
300+
301+
it('should throw error for unsupported WAN 2.6 aspect ratio', async () => {
302+
await expect(
303+
wanModel.doGenerate({
304+
prompt: 'Test',
305+
n: 1,
306+
size: undefined,
307+
aspectRatio: '5:4', // Not in WAN supported list
308+
seed: undefined,
309+
providerOptions: {},
310+
headers: {},
311+
abortSignal: undefined,
312+
})
313+
).rejects.toThrow(InvalidArgumentError);
314+
});
315+
316+
it('should default to 1280x1280 for WAN 2.6 when no size or aspect ratio provided', async () => {
317+
let capturedBody: any;
318+
mockFetch.mockImplementationOnce(async (_input: any, init?: any) => {
319+
capturedBody = JSON.parse(init?.body ?? '{}');
320+
return new Response(
321+
JSON.stringify({
322+
id: 'test',
323+
status: 'COMPLETED',
324+
output: { result: 'https://test.com/img.png' },
325+
}),
326+
{ headers: { 'content-type': 'application/json' } }
327+
);
328+
});
329+
mockFetch.mockImplementationOnce(() =>
330+
Promise.resolve(
331+
new Response(new Uint8Array([1, 2, 3]), {
332+
headers: { 'content-type': 'image/png' },
333+
})
334+
)
335+
);
336+
337+
await wanModel.doGenerate({
338+
prompt: 'Test',
339+
n: 1,
340+
size: undefined,
341+
aspectRatio: undefined,
342+
seed: undefined,
343+
providerOptions: {},
344+
headers: {},
345+
abortSignal: undefined,
346+
});
347+
348+
expect(capturedBody?.input?.size).toBe('1280*1280');
349+
});
350+
});
351+
161352
describe('parameter conversion', () => {
162353
it('should build correct payload for Qwen models', () => {
163354
const qwenModel = new RunpodImageModel('qwen/qwen-image', {
@@ -324,21 +515,57 @@ describe('RunpodImageModel', () => {
324515

325516
const payload = (wanModel as any).buildInputPayload(
326517
'A modern tea shop interior, warm afternoon light',
327-
'1024*1024',
518+
'1280*1280',
328519
42,
329520
{ enable_safety_checker: true }
330521
);
331522

332523
expect(payload).toMatchObject({
333524
prompt: 'A modern tea shop interior, warm afternoon light',
334-
size: '1024*1024',
525+
size: '1280*1280',
335526
seed: 42,
336527
enable_safety_checker: true,
337528
});
338529
// Should not have negative_prompt in the payload (Wan uses inline negative prompt)
339530
expect(payload.negative_prompt).toBeUndefined();
340531
});
341532

533+
it('should build correct payload for Alibaba Wan 2.6 with various aspect ratios', () => {
534+
const wanModel = new RunpodImageModel('alibaba/wan-2.6', {
535+
provider: 'runpod',
536+
baseURL: 'https://api.runpod.ai/v2/wan-2-6-t2i',
537+
headers: () => ({ Authorization: 'Bearer test-key' }),
538+
fetch: mockFetch,
539+
});
540+
541+
// Test 16:9 aspect ratio (1280*720)
542+
const payload16x9 = (wanModel as any).buildInputPayload(
543+
'Wide landscape',
544+
'1280*720',
545+
42,
546+
{}
547+
);
548+
expect(payload16x9.size).toBe('1280*720');
549+
550+
// Test 9:16 aspect ratio (720*1280)
551+
const payload9x16 = (wanModel as any).buildInputPayload(
552+
'Portrait mode',
553+
'720*1280',
554+
42,
555+
{}
556+
);
557+
expect(payload9x16.size).toBe('720*1280');
558+
559+
// Test 21:9 ultrawide (1344*576)
560+
const payload21x9 = (wanModel as any).buildInputPayload(
561+
'Ultrawide cinematic',
562+
'1344*576',
563+
42,
564+
{}
565+
);
566+
expect(payload21x9.size).toBe('1344*576');
567+
});
568+
342569
it('should build correct payload for Qwen Image Edit 2511 with LoRA', () => {
343570
const qwenEdit2511Model = new RunpodImageModel(
344571
'qwen/qwen-image-edit-2511',

0 commit comments

Comments
 (0)