mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-10 09:12:31 +08:00
[Partner Nodes] new ByteDanceSeedreamNodeV2 node with DynamicCombo and autogrow
Signed-off-by: bigcat88 <bigcat88@icloud.com>
This commit is contained in:
parent
7bbf1e8169
commit
7884677027
@ -198,6 +198,62 @@ RECOMMENDED_PRESETS_SEEDREAM_4 = [
|
|||||||
("Custom", None, None),
|
("Custom", None, None),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
_PRESETS_SEEDREAM_1K = [
|
||||||
|
("(1K) 1024x1024 (1:1)", 1024, 1024),
|
||||||
|
("(1K) 864x1152 (3:4)", 864, 1152),
|
||||||
|
("(1K) 1152x864 (4:3)", 1152, 864),
|
||||||
|
("(1K) 1312x736 (16:9)", 1312, 736),
|
||||||
|
("(1K) 736x1312 (9:16)", 736, 1312),
|
||||||
|
("(1K) 832x1248 (2:3)", 832, 1248),
|
||||||
|
("(1K) 1248x832 (3:2)", 1248, 832),
|
||||||
|
("(1K) 1568x672 (21:9)", 1568, 672),
|
||||||
|
]
|
||||||
|
|
||||||
|
_PRESETS_SEEDREAM_2K = [
|
||||||
|
("(2K) 2048x2048 (1:1)", 2048, 2048),
|
||||||
|
("(2K) 1728x2304 (3:4)", 1728, 2304),
|
||||||
|
("(2K) 2304x1728 (4:3)", 2304, 1728),
|
||||||
|
("(2K) 2848x1600 (16:9)", 2848, 1600),
|
||||||
|
("(2K) 1600x2848 (9:16)", 1600, 2848),
|
||||||
|
("(2K) 1664x2496 (2:3)", 1664, 2496),
|
||||||
|
("(2K) 2496x1664 (3:2)", 2496, 1664),
|
||||||
|
("(2K) 3136x1344 (21:9)", 3136, 1344),
|
||||||
|
]
|
||||||
|
|
||||||
|
_PRESETS_SEEDREAM_3K = [
|
||||||
|
("(3K) 3072x3072 (1:1)", 3072, 3072),
|
||||||
|
("(3K) 2592x3456 (3:4)", 2592, 3456),
|
||||||
|
("(3K) 3456x2592 (4:3)", 3456, 2592),
|
||||||
|
("(3K) 4096x2304 (16:9)", 4096, 2304),
|
||||||
|
("(3K) 2304x4096 (9:16)", 2304, 4096),
|
||||||
|
("(3K) 2496x3744 (2:3)", 2496, 3744),
|
||||||
|
("(3K) 3744x2496 (3:2)", 3744, 2496),
|
||||||
|
("(3K) 4704x2016 (21:9)", 4704, 2016),
|
||||||
|
]
|
||||||
|
|
||||||
|
_PRESETS_SEEDREAM_4K = [
|
||||||
|
("(4K) 4096x4096 (1:1)", 4096, 4096),
|
||||||
|
("(4K) 3520x4704 (3:4)", 3520, 4704),
|
||||||
|
("(4K) 4704x3520 (4:3)", 4704, 3520),
|
||||||
|
("(4K) 5504x3040 (16:9)", 5504, 3040),
|
||||||
|
("(4K) 3040x5504 (9:16)", 3040, 5504),
|
||||||
|
("(4K) 3328x4992 (2:3)", 3328, 4992),
|
||||||
|
("(4K) 4992x3328 (3:2)", 4992, 3328),
|
||||||
|
("(4K) 6240x2656 (21:9)", 6240, 2656),
|
||||||
|
]
|
||||||
|
|
||||||
|
_CUSTOM_PRESET = [("Custom", None, None)]
|
||||||
|
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_5_LITE = (
|
||||||
|
_PRESETS_SEEDREAM_2K + _PRESETS_SEEDREAM_3K + _PRESETS_SEEDREAM_4K + _CUSTOM_PRESET
|
||||||
|
)
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_4_5 = (
|
||||||
|
_PRESETS_SEEDREAM_2K + _PRESETS_SEEDREAM_4K + _CUSTOM_PRESET
|
||||||
|
)
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_4_0 = (
|
||||||
|
_PRESETS_SEEDREAM_1K + _PRESETS_SEEDREAM_2K + _PRESETS_SEEDREAM_4K + _CUSTOM_PRESET
|
||||||
|
)
|
||||||
|
|
||||||
# Seedance 2.0 reference video pixel count limits per model and output resolution.
|
# Seedance 2.0 reference video pixel count limits per model and output resolution.
|
||||||
SEEDANCE2_REF_VIDEO_PIXEL_LIMITS = {
|
SEEDANCE2_REF_VIDEO_PIXEL_LIMITS = {
|
||||||
"dreamina-seedance-2-0-260128": {
|
"dreamina-seedance-2-0-260128": {
|
||||||
|
|||||||
@ -10,6 +10,9 @@ from comfy_api.latest import IO, ComfyExtension, Input
|
|||||||
from comfy_api_nodes.apis.bytedance import (
|
from comfy_api_nodes.apis.bytedance import (
|
||||||
RECOMMENDED_PRESETS,
|
RECOMMENDED_PRESETS,
|
||||||
RECOMMENDED_PRESETS_SEEDREAM_4,
|
RECOMMENDED_PRESETS_SEEDREAM_4,
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_4_0,
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_4_5,
|
||||||
|
RECOMMENDED_PRESETS_SEEDREAM_5_LITE,
|
||||||
SEEDANCE2_PRICE_PER_1K_TOKENS,
|
SEEDANCE2_PRICE_PER_1K_TOKENS,
|
||||||
SEEDANCE2_REF_VIDEO_PIXEL_LIMITS,
|
SEEDANCE2_REF_VIDEO_PIXEL_LIMITS,
|
||||||
VIDEO_TASKS_EXECUTION_TIME,
|
VIDEO_TASKS_EXECUTION_TIME,
|
||||||
@ -68,6 +71,12 @@ SEEDREAM_MODELS = {
|
|||||||
"seedream-4-0-250828": "seedream-4-0-250828",
|
"seedream-4-0-250828": "seedream-4-0-250828",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SEEDREAM_PRESETS = {
|
||||||
|
"seedream-5-0-260128": RECOMMENDED_PRESETS_SEEDREAM_5_LITE,
|
||||||
|
"seedream-4-5-251128": RECOMMENDED_PRESETS_SEEDREAM_4_5,
|
||||||
|
"seedream-4-0-250828": RECOMMENDED_PRESETS_SEEDREAM_4_0,
|
||||||
|
}
|
||||||
|
|
||||||
# Long-running tasks endpoints(e.g., video)
|
# Long-running tasks endpoints(e.g., video)
|
||||||
BYTEPLUS_TASK_ENDPOINT = "/proxy/byteplus/api/v3/contents/generations/tasks"
|
BYTEPLUS_TASK_ENDPOINT = "/proxy/byteplus/api/v3/contents/generations/tasks"
|
||||||
BYTEPLUS_TASK_STATUS_ENDPOINT = "/proxy/byteplus/api/v3/contents/generations/tasks" # + /{task_id}
|
BYTEPLUS_TASK_STATUS_ENDPOINT = "/proxy/byteplus/api/v3/contents/generations/tasks" # + /{task_id}
|
||||||
@ -562,6 +571,7 @@ class ByteDanceSeedreamNode(IO.ComfyNode):
|
|||||||
)
|
)
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
|
is_deprecated=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -651,6 +661,226 @@ class ByteDanceSeedreamNode(IO.ComfyNode):
|
|||||||
return IO.NodeOutput(torch.cat([await download_url_to_image_tensor(i) for i in urls]))
|
return IO.NodeOutput(torch.cat([await download_url_to_image_tensor(i) for i in urls]))
|
||||||
|
|
||||||
|
|
||||||
|
def _seedream_model_inputs(*, max_ref_images: int, presets: list):
|
||||||
|
return [
|
||||||
|
IO.Combo.Input(
|
||||||
|
"size_preset",
|
||||||
|
options=[label for label, _, _ in presets],
|
||||||
|
tooltip="Pick a recommended size. Select Custom to use the width and height below.",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"width",
|
||||||
|
default=2048,
|
||||||
|
min=1024,
|
||||||
|
max=6240,
|
||||||
|
step=2,
|
||||||
|
tooltip="Custom width for image. Value is working only if `size_preset` is set to `Custom`",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"height",
|
||||||
|
default=2048,
|
||||||
|
min=1024,
|
||||||
|
max=4992,
|
||||||
|
step=2,
|
||||||
|
tooltip="Custom height for image. Value is working only if `size_preset` is set to `Custom`",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"max_images",
|
||||||
|
default=1,
|
||||||
|
min=1,
|
||||||
|
max=max_ref_images,
|
||||||
|
step=1,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
tooltip="Maximum number of images to generate. With 1, exactly one image is produced. "
|
||||||
|
"With >1, the model generates between 1 and max_images related images "
|
||||||
|
"(e.g., story scenes, character variations). "
|
||||||
|
"Total images (input + generated) cannot exceed 15.",
|
||||||
|
),
|
||||||
|
IO.Autogrow.Input(
|
||||||
|
"images",
|
||||||
|
template=IO.Autogrow.TemplateNames(
|
||||||
|
IO.Image.Input("image"),
|
||||||
|
names=[f"image_{i}" for i in range(1, max_ref_images + 1)],
|
||||||
|
min=0,
|
||||||
|
),
|
||||||
|
tooltip=f"Optional reference image(s) for image-to-image or multi-reference generation. "
|
||||||
|
f"Up to {max_ref_images} images.",
|
||||||
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"fail_on_partial",
|
||||||
|
default=False,
|
||||||
|
tooltip="If enabled, abort execution if any requested images are missing or return an error.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ByteDanceSeedreamNodeV2(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="ByteDanceSeedreamNodeV2",
|
||||||
|
display_name="ByteDance Seedream 4.5 & 5.0",
|
||||||
|
category="api node/image/ByteDance",
|
||||||
|
description="Unified text-to-image generation and precise single-sentence editing at up to 4K resolution.",
|
||||||
|
inputs=[
|
||||||
|
IO.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Text prompt for creating or editing an image.",
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Input(
|
||||||
|
"model",
|
||||||
|
options=[
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"seedream 5.0 lite",
|
||||||
|
_seedream_model_inputs(max_ref_images=14, presets=RECOMMENDED_PRESETS_SEEDREAM_5_LITE),
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"seedream-4-5-251128",
|
||||||
|
_seedream_model_inputs(max_ref_images=10, presets=RECOMMENDED_PRESETS_SEEDREAM_4_5),
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Option(
|
||||||
|
"seedream-4-0-250828",
|
||||||
|
_seedream_model_inputs(max_ref_images=10, presets=RECOMMENDED_PRESETS_SEEDREAM_4_0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
step=1,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed to use for generation.",
|
||||||
|
),
|
||||||
|
IO.Boolean.Input(
|
||||||
|
"watermark",
|
||||||
|
default=False,
|
||||||
|
tooltip='Whether to add an "AI generated" watermark to the image.',
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.Image.Output(),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
depends_on=IO.PriceBadgeDepends(widgets=["model"]),
|
||||||
|
expr="""
|
||||||
|
(
|
||||||
|
$price := $contains(widgets.model, "5.0 lite") ? 0.035 :
|
||||||
|
$contains(widgets.model, "4-5") ? 0.04 : 0.03;
|
||||||
|
{
|
||||||
|
"type":"usd",
|
||||||
|
"usd": $price,
|
||||||
|
"format": { "suffix":" x images/Run", "approximate": true }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
prompt: str,
|
||||||
|
model: dict,
|
||||||
|
seed: int = 0,
|
||||||
|
watermark: bool = False,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
|
model_id = SEEDREAM_MODELS[model["model"]]
|
||||||
|
presets = SEEDREAM_PRESETS[model_id]
|
||||||
|
|
||||||
|
size_preset = model.get("size_preset", presets[0][0])
|
||||||
|
width = model.get("width", 2048)
|
||||||
|
height = model.get("height", 2048)
|
||||||
|
max_images = model.get("max_images", 1)
|
||||||
|
sequential_image_generation = "disabled" if max_images == 1 else "auto"
|
||||||
|
images_dict = model.get("images") or {}
|
||||||
|
fail_on_partial = model.get("fail_on_partial", False)
|
||||||
|
|
||||||
|
w = h = None
|
||||||
|
for label, tw, th in presets:
|
||||||
|
if label == size_preset:
|
||||||
|
w, h = tw, th
|
||||||
|
break
|
||||||
|
if w is None or h is None:
|
||||||
|
w, h = width, height
|
||||||
|
|
||||||
|
out_num_pixels = w * h
|
||||||
|
mp_provided = out_num_pixels / 1_000_000.0
|
||||||
|
if ("seedream-4-5" in model_id or "seedream-5-0" in model_id) and out_num_pixels < 3686400:
|
||||||
|
raise ValueError(
|
||||||
|
f"Minimum image resolution for the selected model is 3.68MP, but {mp_provided:.2f}MP provided."
|
||||||
|
)
|
||||||
|
if "seedream-4-0" in model_id and out_num_pixels < 921600:
|
||||||
|
raise ValueError(
|
||||||
|
f"Minimum image resolution that the selected model can generate is 0.92MP, "
|
||||||
|
f"but {mp_provided:.2f}MP provided."
|
||||||
|
)
|
||||||
|
if out_num_pixels > 16_777_216:
|
||||||
|
raise ValueError(
|
||||||
|
f"Maximum image resolution for the selected model is 16.78MP, but {mp_provided:.2f}MP provided."
|
||||||
|
)
|
||||||
|
|
||||||
|
image_tensors: list[Input.Image] = [t for t in images_dict.values() if t is not None]
|
||||||
|
n_input_images = sum(get_number_of_images(t) for t in image_tensors)
|
||||||
|
max_num_of_images = 14 if model_id == "seedream-5-0-260128" else 10
|
||||||
|
if n_input_images > max_num_of_images:
|
||||||
|
raise ValueError(
|
||||||
|
f"Maximum of {max_num_of_images} reference images are supported, but {n_input_images} received."
|
||||||
|
)
|
||||||
|
if sequential_image_generation == "auto" and n_input_images + max_images > 15:
|
||||||
|
raise ValueError(
|
||||||
|
"The maximum number of generated images plus the number of reference images cannot exceed 15."
|
||||||
|
)
|
||||||
|
|
||||||
|
reference_images_urls: list[str] = []
|
||||||
|
if image_tensors:
|
||||||
|
for tensor in image_tensors:
|
||||||
|
validate_image_aspect_ratio(tensor, (1, 3), (3, 1))
|
||||||
|
reference_images_urls = await upload_images_to_comfyapi(
|
||||||
|
cls,
|
||||||
|
image_tensors,
|
||||||
|
max_images=n_input_images,
|
||||||
|
mime_type="image/png",
|
||||||
|
wait_label="Uploading reference images",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path=BYTEPLUS_IMAGE_ENDPOINT, method="POST"),
|
||||||
|
response_model=ImageTaskCreationResponse,
|
||||||
|
data=Seedream4TaskCreationRequest(
|
||||||
|
model=model_id,
|
||||||
|
prompt=prompt,
|
||||||
|
image=reference_images_urls,
|
||||||
|
size=f"{w}x{h}",
|
||||||
|
seed=seed,
|
||||||
|
sequential_image_generation=sequential_image_generation,
|
||||||
|
sequential_image_generation_options=Seedream4Options(max_images=max_images),
|
||||||
|
watermark=watermark,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if len(response.data) == 1:
|
||||||
|
return IO.NodeOutput(await download_url_to_image_tensor(get_image_url_from_response(response)))
|
||||||
|
urls = [str(d["url"]) for d in response.data if isinstance(d, dict) and "url" in d]
|
||||||
|
if fail_on_partial and len(urls) < len(response.data):
|
||||||
|
raise RuntimeError(f"Only {len(urls)} of {len(response.data)} images were generated before error.")
|
||||||
|
return IO.NodeOutput(torch.cat([await download_url_to_image_tensor(i) for i in urls]))
|
||||||
|
|
||||||
|
|
||||||
class ByteDanceTextToVideoNode(IO.ComfyNode):
|
class ByteDanceTextToVideoNode(IO.ComfyNode):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -2105,6 +2335,7 @@ class ByteDanceExtension(ComfyExtension):
|
|||||||
return [
|
return [
|
||||||
ByteDanceImageNode,
|
ByteDanceImageNode,
|
||||||
ByteDanceSeedreamNode,
|
ByteDanceSeedreamNode,
|
||||||
|
ByteDanceSeedreamNodeV2,
|
||||||
ByteDanceTextToVideoNode,
|
ByteDanceTextToVideoNode,
|
||||||
ByteDanceImageToVideoNode,
|
ByteDanceImageToVideoNode,
|
||||||
ByteDanceFirstLastFrameNode,
|
ByteDanceFirstLastFrameNode,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user