@@ -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