@@ -77,6 +77,22 @@ class Image2ImageTaskCreationRequest(BaseModel):
7777 watermark : Optional [bool ] = Field (True )
7878
7979
80+ class Seedream4Options (BaseModel ):
81+ max_images : int = Field (15 )
82+
83+
84+ class Seedream4TaskCreationRequest (BaseModel ):
85+ model : str = Field ("seedream-4-0-250828" )
86+ prompt : str = Field (...)
87+ response_format : str = Field ("url" )
88+ image : Optional [list [str ]] = Field (None , description = "Image URLs" )
89+ size : str = Field (...)
90+ seed : int = Field (..., ge = 0 , le = 2147483647 )
91+ sequential_image_generation : str = Field ("disabled" )
92+ sequential_image_generation_options : Seedream4Options = Field (Seedream4Options (max_images = 15 ))
93+ watermark : bool = Field (True )
94+
95+
8096class ImageTaskCreationResponse (BaseModel ):
8197 model : str = Field (...)
8298 created : int = Field (..., description = "Unix timestamp (in seconds) indicating time when the request was created." )
@@ -143,6 +159,19 @@ class TaskStatusResponse(BaseModel):
143159 ("Custom" , None , None ),
144160]
145161
162+ RECOMMENDED_PRESETS_SEEDREAM_4 = [
163+ ("2048x2048 (1:1)" , 2048 , 2048 ),
164+ ("2304x1728 (4:3)" , 2304 , 1728 ),
165+ ("1728x2304 (3:4)" , 1728 , 2304 ),
166+ ("2560x1440 (16:9)" , 2560 , 1440 ),
167+ ("1440x2560 (9:16)" , 1440 , 2560 ),
168+ ("2496x1664 (3:2)" , 2496 , 1664 ),
169+ ("1664x2496 (2:3)" , 1664 , 2496 ),
170+ ("3024x1296 (21:9)" , 3024 , 1296 ),
171+ ("4096x4096 (1:1)" , 4096 , 4096 ),
172+ ("Custom" , None , None ),
173+ ]
174+
146175# The time in this dictionary are given for 10 seconds duration.
147176VIDEO_TASKS_EXECUTION_TIME = {
148177 "seedance-1-0-lite-t2v-250428" : {
@@ -348,7 +377,7 @@ def define_schema(cls):
348377 return comfy_io .Schema (
349378 node_id = "ByteDanceImageEditNode" ,
350379 display_name = "ByteDance Image Edit" ,
351- category = "api node/video /ByteDance" ,
380+ category = "api node/image /ByteDance" ,
352381 description = "Edit images using ByteDance models via api based on prompt" ,
353382 inputs = [
354383 comfy_io .Combo .Input (
@@ -451,6 +480,182 @@ async def execute(
451480 return comfy_io .NodeOutput (await download_url_to_image_tensor (get_image_url_from_response (response )))
452481
453482
483+ class ByteDanceSeedreamNode (comfy_io .ComfyNode ):
484+
485+ @classmethod
486+ def define_schema (cls ):
487+ return comfy_io .Schema (
488+ node_id = "ByteDanceSeedreamNode" ,
489+ display_name = "ByteDance Seedream 4" ,
490+ category = "api node/image/ByteDance" ,
491+ description = "Unified text-to-image generation and precise single-sentence editing at up to 4K resolution." ,
492+ inputs = [
493+ comfy_io .Combo .Input (
494+ "model" ,
495+ options = ["seedream-4-0-250828" ],
496+ tooltip = "Model name" ,
497+ ),
498+ comfy_io .String .Input (
499+ "prompt" ,
500+ multiline = True ,
501+ default = "" ,
502+ tooltip = "Text prompt for creating or editing an image." ,
503+ ),
504+ comfy_io .Image .Input (
505+ "image" ,
506+ tooltip = "Input image(s) for image-to-image generation. "
507+ "List of 1-10 images for single or multi-reference generation." ,
508+ optional = True ,
509+ ),
510+ comfy_io .Combo .Input (
511+ "size_preset" ,
512+ options = [label for label , _ , _ in RECOMMENDED_PRESETS_SEEDREAM_4 ],
513+ tooltip = "Pick a recommended size. Select Custom to use the width and height below." ,
514+ ),
515+ comfy_io .Int .Input (
516+ "width" ,
517+ default = 2048 ,
518+ min = 1024 ,
519+ max = 4096 ,
520+ step = 64 ,
521+ tooltip = "Custom width for image. Value is working only if `size_preset` is set to `Custom`" ,
522+ optional = True ,
523+ ),
524+ comfy_io .Int .Input (
525+ "height" ,
526+ default = 2048 ,
527+ min = 1024 ,
528+ max = 4096 ,
529+ step = 64 ,
530+ tooltip = "Custom height for image. Value is working only if `size_preset` is set to `Custom`" ,
531+ optional = True ,
532+ ),
533+ comfy_io .Combo .Input (
534+ "sequential_image_generation" ,
535+ options = ["disabled" , "auto" ],
536+ tooltip = "Group image generation mode. "
537+ "'disabled' generates a single image. "
538+ "'auto' lets the model decide whether to generate multiple related images "
539+ "(e.g., story scenes, character variations)." ,
540+ optional = True ,
541+ ),
542+ comfy_io .Int .Input (
543+ "max_images" ,
544+ default = 1 ,
545+ min = 1 ,
546+ max = 15 ,
547+ step = 1 ,
548+ display_mode = comfy_io .NumberDisplay .number ,
549+ tooltip = "Maximum number of images to generate when sequential_image_generation='auto'. "
550+ "Total images (input + generated) cannot exceed 15." ,
551+ optional = True ,
552+ ),
553+ comfy_io .Int .Input (
554+ "seed" ,
555+ default = 0 ,
556+ min = 0 ,
557+ max = 2147483647 ,
558+ step = 1 ,
559+ display_mode = comfy_io .NumberDisplay .number ,
560+ control_after_generate = True ,
561+ tooltip = "Seed to use for generation." ,
562+ optional = True ,
563+ ),
564+ comfy_io .Boolean .Input (
565+ "watermark" ,
566+ default = True ,
567+ tooltip = "Whether to add an \" AI generated\" watermark to the image." ,
568+ optional = True ,
569+ ),
570+ ],
571+ outputs = [
572+ comfy_io .Image .Output (),
573+ ],
574+ hidden = [
575+ comfy_io .Hidden .auth_token_comfy_org ,
576+ comfy_io .Hidden .api_key_comfy_org ,
577+ comfy_io .Hidden .unique_id ,
578+ ],
579+ is_api_node = True ,
580+ )
581+
582+ @classmethod
583+ async def execute (
584+ cls ,
585+ model : str ,
586+ prompt : str ,
587+ image : torch .Tensor = None ,
588+ size_preset : str = RECOMMENDED_PRESETS_SEEDREAM_4 [0 ][0 ],
589+ width : int = 2048 ,
590+ height : int = 2048 ,
591+ sequential_image_generation : str = "disabled" ,
592+ max_images : int = 1 ,
593+ seed : int = 0 ,
594+ watermark : bool = True ,
595+ ) -> comfy_io .NodeOutput :
596+ validate_string (prompt , strip_whitespace = True , min_length = 1 )
597+ w = h = None
598+ for label , tw , th in RECOMMENDED_PRESETS_SEEDREAM_4 :
599+ if label == size_preset :
600+ w , h = tw , th
601+ break
602+
603+ if w is None or h is None :
604+ w , h = width , height
605+ if not (1024 <= w <= 4096 ) or not (1024 <= h <= 4096 ):
606+ raise ValueError (
607+ f"Custom size out of range: { w } x{ h } . "
608+ "Both width and height must be between 1024 and 4096 pixels."
609+ )
610+ n_input_images = get_number_of_images (image ) if image is not None else 0
611+ if n_input_images > 10 :
612+ raise ValueError (f"Maximum of 10 reference images are supported, but { n_input_images } received." )
613+ if sequential_image_generation == "auto" and n_input_images + max_images > 15 :
614+ raise ValueError (
615+ "The maximum number of generated images plus the number of reference images cannot exceed 15."
616+ )
617+ auth_kwargs = {
618+ "auth_token" : cls .hidden .auth_token_comfy_org ,
619+ "comfy_api_key" : cls .hidden .api_key_comfy_org ,
620+ }
621+ reference_images_urls = []
622+ if n_input_images :
623+ for i in image :
624+ validate_image_aspect_ratio_range (i , (1 , 3 ), (3 , 1 ))
625+ reference_images_urls = (await upload_images_to_comfyapi (
626+ image ,
627+ max_images = n_input_images ,
628+ mime_type = "image/png" ,
629+ auth_kwargs = auth_kwargs ,
630+ ))
631+ payload = Seedream4TaskCreationRequest (
632+ model = model ,
633+ prompt = prompt ,
634+ image = reference_images_urls ,
635+ size = f"{ w } x{ h } " ,
636+ seed = seed ,
637+ sequential_image_generation = sequential_image_generation ,
638+ sequential_image_generation_options = Seedream4Options (max_images = max_images ),
639+ watermark = watermark ,
640+ )
641+ response = await SynchronousOperation (
642+ endpoint = ApiEndpoint (
643+ path = BYTEPLUS_IMAGE_ENDPOINT ,
644+ method = HttpMethod .POST ,
645+ request_model = Seedream4TaskCreationRequest ,
646+ response_model = ImageTaskCreationResponse ,
647+ ),
648+ request = payload ,
649+ auth_kwargs = auth_kwargs ,
650+ ).execute ()
651+
652+ if len (response .data ) == 1 :
653+ return comfy_io .NodeOutput (await download_url_to_image_tensor (get_image_url_from_response (response )))
654+ return comfy_io .NodeOutput (
655+ torch .cat ([await download_url_to_image_tensor (str (i ["url" ])) for i in response .data ])
656+ )
657+
658+
454659class ByteDanceTextToVideoNode (comfy_io .ComfyNode ):
455660
456661 @classmethod
@@ -1001,6 +1206,7 @@ async def get_node_list(self) -> list[type[comfy_io.ComfyNode]]:
10011206 return [
10021207 ByteDanceImageNode ,
10031208 ByteDanceImageEditNode ,
1209+ ByteDanceSeedreamNode ,
10041210 ByteDanceTextToVideoNode ,
10051211 ByteDanceImageToVideoNode ,
10061212 ByteDanceFirstLastFrameNode ,
0 commit comments