@@ -28,6 +28,12 @@ class Text2ImageInputField(BaseModel):
2828 negative_prompt : Optional [str ] = Field (None )
2929
3030
31+ class Image2ImageInputField (BaseModel ):
32+ prompt : str = Field (...)
33+ negative_prompt : Optional [str ] = Field (None )
34+ images : list [str ] = Field (..., min_length = 1 , max_length = 2 )
35+
36+
3137class Text2VideoInputField (BaseModel ):
3238 prompt : str = Field (...)
3339 negative_prompt : Optional [str ] = Field (None )
@@ -49,6 +55,13 @@ class Txt2ImageParametersField(BaseModel):
4955 watermark : bool = Field (True )
5056
5157
58+ class Image2ImageParametersField (BaseModel ):
59+ size : Optional [str ] = Field (None )
60+ n : int = Field (1 , description = "Number of images to generate." ) # we support only value=1
61+ seed : int = Field (..., ge = 0 , le = 2147483647 )
62+ watermark : bool = Field (True )
63+
64+
5265class Text2VideoParametersField (BaseModel ):
5366 size : str = Field (...)
5467 seed : int = Field (..., ge = 0 , le = 2147483647 )
@@ -73,6 +86,12 @@ class Text2ImageTaskCreationRequest(BaseModel):
7386 parameters : Txt2ImageParametersField = Field (...)
7487
7588
89+ class Image2ImageTaskCreationRequest (BaseModel ):
90+ model : str = Field (...)
91+ input : Image2ImageInputField = Field (...)
92+ parameters : Image2ImageParametersField = Field (...)
93+
94+
7695class Text2VideoTaskCreationRequest (BaseModel ):
7796 model : str = Field (...)
7897 input : Text2VideoInputField = Field (...)
@@ -135,7 +154,12 @@ async def process_task(
135154 url : str ,
136155 request_model : Type [T ],
137156 response_model : Type [R ],
138- payload : Union [Text2ImageTaskCreationRequest , Text2VideoTaskCreationRequest , Image2VideoTaskCreationRequest ],
157+ payload : Union [
158+ Text2ImageTaskCreationRequest ,
159+ Image2ImageTaskCreationRequest ,
160+ Text2VideoTaskCreationRequest ,
161+ Image2VideoTaskCreationRequest ,
162+ ],
139163 node_id : str ,
140164 estimated_duration : int ,
141165 poll_interval : int ,
@@ -288,6 +312,128 @@ async def execute(
288312 return comfy_io .NodeOutput (await download_url_to_image_tensor (str (response .output .results [0 ].url )))
289313
290314
315+ class WanImageToImageApi (comfy_io .ComfyNode ):
316+ @classmethod
317+ def define_schema (cls ):
318+ return comfy_io .Schema (
319+ node_id = "WanImageToImageApi" ,
320+ display_name = "Wan Image to Image" ,
321+ category = "api node/image/Wan" ,
322+ description = "Generates an image from one or two input images and a text prompt. "
323+ "The output image is currently fixed at 1.6 MP; its aspect ratio matches the input image(s)." ,
324+ inputs = [
325+ comfy_io .Combo .Input (
326+ "model" ,
327+ options = ["wan2.5-i2i-preview" ],
328+ default = "wan2.5-i2i-preview" ,
329+ tooltip = "Model to use." ,
330+ ),
331+ comfy_io .Image .Input (
332+ "image" ,
333+ tooltip = "Single-image editing or multi-image fusion, maximum 2 images." ,
334+ ),
335+ comfy_io .String .Input (
336+ "prompt" ,
337+ multiline = True ,
338+ default = "" ,
339+ tooltip = "Prompt used to describe the elements and visual features, supports English/Chinese." ,
340+ ),
341+ comfy_io .String .Input (
342+ "negative_prompt" ,
343+ multiline = True ,
344+ default = "" ,
345+ tooltip = "Negative text prompt to guide what to avoid." ,
346+ optional = True ,
347+ ),
348+ # redo this later as an optional combo of recommended resolutions
349+ # comfy_io.Int.Input(
350+ # "width",
351+ # default=1280,
352+ # min=384,
353+ # max=1440,
354+ # step=16,
355+ # optional=True,
356+ # ),
357+ # comfy_io.Int.Input(
358+ # "height",
359+ # default=1280,
360+ # min=384,
361+ # max=1440,
362+ # step=16,
363+ # optional=True,
364+ # ),
365+ comfy_io .Int .Input (
366+ "seed" ,
367+ default = 0 ,
368+ min = 0 ,
369+ max = 2147483647 ,
370+ step = 1 ,
371+ display_mode = comfy_io .NumberDisplay .number ,
372+ control_after_generate = True ,
373+ tooltip = "Seed to use for generation." ,
374+ optional = True ,
375+ ),
376+ comfy_io .Boolean .Input (
377+ "watermark" ,
378+ default = True ,
379+ tooltip = "Whether to add an \" AI generated\" watermark to the result." ,
380+ optional = True ,
381+ ),
382+ ],
383+ outputs = [
384+ comfy_io .Image .Output (),
385+ ],
386+ hidden = [
387+ comfy_io .Hidden .auth_token_comfy_org ,
388+ comfy_io .Hidden .api_key_comfy_org ,
389+ comfy_io .Hidden .unique_id ,
390+ ],
391+ is_api_node = True ,
392+ )
393+
394+ @classmethod
395+ async def execute (
396+ cls ,
397+ model : str ,
398+ image : torch .Tensor ,
399+ prompt : str ,
400+ negative_prompt : str = "" ,
401+ # width: int = 1024,
402+ # height: int = 1024,
403+ seed : int = 0 ,
404+ watermark : bool = True ,
405+ ):
406+ n_images = get_number_of_images (image )
407+ if n_images not in (1 , 2 ):
408+ raise ValueError (f"Expected 1 or 2 input images, got { n_images } ." )
409+ images = []
410+ for i in image :
411+ images .append ("data:image/png;base64," + tensor_to_base64_string (i , total_pixels = 4096 * 4096 ))
412+ payload = Image2ImageTaskCreationRequest (
413+ model = model ,
414+ input = Image2ImageInputField (prompt = prompt , negative_prompt = negative_prompt , images = images ),
415+ parameters = Image2ImageParametersField (
416+ # size=f"{width}*{height}",
417+ seed = seed ,
418+ watermark = watermark ,
419+ ),
420+ )
421+ response = await process_task (
422+ {
423+ "auth_token" : cls .hidden .auth_token_comfy_org ,
424+ "comfy_api_key" : cls .hidden .api_key_comfy_org ,
425+ },
426+ "/proxy/wan/api/v1/services/aigc/image2image/image-synthesis" ,
427+ request_model = Image2ImageTaskCreationRequest ,
428+ response_model = ImageTaskStatusResponse ,
429+ payload = payload ,
430+ node_id = cls .hidden .unique_id ,
431+ estimated_duration = 9 ,
432+ poll_interval = 3 ,
433+ )
434+ return comfy_io .NodeOutput (await download_url_to_image_tensor (str (response .output .results [0 ].url )))
435+
436+
291437class WanTextToVideoApi (comfy_io .ComfyNode ):
292438 @classmethod
293439 def define_schema (cls ):
@@ -593,6 +739,7 @@ class WanApiExtension(ComfyExtension):
593739 async def get_node_list (self ) -> list [type [comfy_io .ComfyNode ]]:
594740 return [
595741 WanTextToImageApi ,
742+ WanImageToImageApi ,
596743 WanTextToVideoApi ,
597744 WanImageToVideoApi ,
598745 ]
0 commit comments