mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-06-19 22:39:24 +08:00
Merge branch 'trellis2' of https://github.com/yousef-rafat/ComfyUI into pr/12183
This commit is contained in:
commit
60f0ec8d69
@ -44,7 +44,14 @@ class BackgroundRemovalModel():
|
|||||||
comfy.model_management.load_model_gpu(self.patcher)
|
comfy.model_management.load_model_gpu(self.patcher)
|
||||||
H, W = image.shape[1], image.shape[2]
|
H, W = image.shape[1], image.shape[2]
|
||||||
pixel_values = comfy.clip_model.clip_preprocess(image.to(self.load_device), size=self.image_size, mean=self.image_mean, std=self.image_std, crop=False)
|
pixel_values = comfy.clip_model.clip_preprocess(image.to(self.load_device), size=self.image_size, mean=self.image_mean, std=self.image_std, crop=False)
|
||||||
out = self.model(pixel_values=pixel_values)
|
|
||||||
|
if pixel_values.shape[0] > 1:
|
||||||
|
out = torch.cat([
|
||||||
|
self.model(pixel_values=pixel_values[i:i+1])
|
||||||
|
for i in range(pixel_values.shape[0])
|
||||||
|
], dim=0)
|
||||||
|
else:
|
||||||
|
out = self.model(pixel_values=pixel_values)
|
||||||
out = torch.nn.functional.interpolate(out, size=(H, W), mode="bicubic", antialias=False)
|
out = torch.nn.functional.interpolate(out, size=(H, W), mode="bicubic", antialias=False)
|
||||||
|
|
||||||
mask = out.sigmoid().to(device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype())
|
mask = out.sigmoid().to(device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype())
|
||||||
|
|||||||
@ -150,6 +150,7 @@ class SD3(LatentFormat):
|
|||||||
class StableAudio1(LatentFormat):
|
class StableAudio1(LatentFormat):
|
||||||
latent_channels = 64
|
latent_channels = 64
|
||||||
latent_dimensions = 1
|
latent_dimensions = 1
|
||||||
|
temporal_downscale_ratio = 2048
|
||||||
|
|
||||||
class Flux(SD3):
|
class Flux(SD3):
|
||||||
latent_channels = 16
|
latent_channels = 16
|
||||||
@ -768,6 +769,7 @@ class ACEAudio(LatentFormat):
|
|||||||
class ACEAudio15(LatentFormat):
|
class ACEAudio15(LatentFormat):
|
||||||
latent_channels = 64
|
latent_channels = 64
|
||||||
latent_dimensions = 1
|
latent_dimensions = 1
|
||||||
|
temporal_downscale_ratio = 1764
|
||||||
|
|
||||||
class ChromaRadiance(LatentFormat):
|
class ChromaRadiance(LatentFormat):
|
||||||
latent_channels = 3
|
latent_channels = 3
|
||||||
|
|||||||
@ -1701,6 +1701,13 @@ class HiDreamO1(BaseModel):
|
|||||||
if text_input_ids is None or noise is None:
|
if text_input_ids is None or noise is None:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
# handle area conds
|
||||||
|
area = kwargs.get("area", None)
|
||||||
|
if area is not None:
|
||||||
|
crop_h = min(noise.shape[-2] - area[2], area[0])
|
||||||
|
crop_w = min(noise.shape[-1] - area[3], area[1])
|
||||||
|
noise = torch.empty((noise.shape[0], 3, crop_h, crop_w), dtype=noise.dtype, device=noise.device)
|
||||||
|
|
||||||
conds = build_extra_conds(
|
conds = build_extra_conds(
|
||||||
text_input_ids, noise,
|
text_input_ids, noise,
|
||||||
ref_images=kwargs.get("reference_latents", None),
|
ref_images=kwargs.get("reference_latents", None),
|
||||||
|
|||||||
@ -1493,27 +1493,30 @@ class ModelPatcher:
|
|||||||
self.unpatch_hooks()
|
self.unpatch_hooks()
|
||||||
self.clear_cached_hook_weights()
|
self.clear_cached_hook_weights()
|
||||||
|
|
||||||
def state_dict_for_saving(self, clip_state_dict=None, vae_state_dict=None, clip_vision_state_dict=None):
|
def model_state_dict_for_saving(self, model=None, prefix=""):
|
||||||
original_state_dict = self.model.diffusion_model.state_dict()
|
if model is None:
|
||||||
unet_state_dict = {}
|
model = self.model
|
||||||
|
|
||||||
|
original_state_dict = model.state_dict()
|
||||||
|
output_state_dict = {}
|
||||||
keys = list(original_state_dict)
|
keys = list(original_state_dict)
|
||||||
while len(keys) > 0:
|
while len(keys) > 0:
|
||||||
k = keys.pop(0)
|
k = keys.pop(0)
|
||||||
v = original_state_dict[k]
|
v = original_state_dict[k]
|
||||||
op_keys = k.rsplit('.', 1)
|
op_keys = k.rsplit('.', 1)
|
||||||
if (len(op_keys) < 2) or op_keys[1] not in ["weight", "bias"]:
|
if (len(op_keys) < 2) or op_keys[1] not in ["weight", "bias"]:
|
||||||
unet_state_dict[k] = v
|
output_state_dict[k] = v
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
op = comfy.utils.get_attr(self.model.diffusion_model, op_keys[0])
|
op = comfy.utils.get_attr(model, op_keys[0])
|
||||||
except:
|
except:
|
||||||
unet_state_dict[k] = v
|
output_state_dict[k] = v
|
||||||
continue
|
continue
|
||||||
if not op or not hasattr(op, "comfy_cast_weights") or \
|
if not op or not hasattr(op, "comfy_cast_weights") or \
|
||||||
(hasattr(op, "comfy_patched_weights") and op.comfy_patched_weights == True):
|
(hasattr(op, "comfy_patched_weights") and op.comfy_patched_weights == True):
|
||||||
unet_state_dict[k] = v
|
output_state_dict[k] = v
|
||||||
continue
|
continue
|
||||||
key = "diffusion_model." + k
|
key = prefix + k
|
||||||
weight = comfy.utils.get_attr(self.model, key)
|
weight = comfy.utils.get_attr(self.model, key)
|
||||||
if isinstance(weight, QuantizedTensor) and k in original_state_dict:
|
if isinstance(weight, QuantizedTensor) and k in original_state_dict:
|
||||||
qt_state_dict = weight.state_dict(k)
|
qt_state_dict = weight.state_dict(k)
|
||||||
@ -1521,10 +1524,14 @@ class ModelPatcher:
|
|||||||
for group_key in (x for x in qt_state_dict if x in original_state_dict):
|
for group_key in (x for x in qt_state_dict if x in original_state_dict):
|
||||||
if group_key in keys:
|
if group_key in keys:
|
||||||
keys.remove(group_key)
|
keys.remove(group_key)
|
||||||
unet_state_dict.pop(group_key, "")
|
output_state_dict.pop(group_key, "")
|
||||||
unet_state_dict[group_key] = LazyCastingParamPiece(caster, "diffusion_model." + group_key, original_state_dict[group_key])
|
output_state_dict[group_key] = LazyCastingParamPiece(caster, prefix + group_key, original_state_dict[group_key])
|
||||||
continue
|
continue
|
||||||
unet_state_dict[k] = LazyCastingParam(self, key, weight)
|
output_state_dict[k] = LazyCastingParam(self, key, weight)
|
||||||
|
return output_state_dict
|
||||||
|
|
||||||
|
def state_dict_for_saving(self, clip_state_dict=None, vae_state_dict=None, clip_vision_state_dict=None):
|
||||||
|
unet_state_dict = self.model_state_dict_for_saving(self.model.diffusion_model, "diffusion_model.")
|
||||||
return self.model.state_dict_for_saving(unet_state_dict, clip_state_dict=clip_state_dict, vae_state_dict=vae_state_dict, clip_vision_state_dict=clip_vision_state_dict)
|
return self.model.state_dict_for_saving(unet_state_dict, clip_state_dict=clip_state_dict, vae_state_dict=vae_state_dict, clip_vision_state_dict=clip_vision_state_dict)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
|||||||
@ -260,7 +260,7 @@ def resolve_cast_module_with_vbar(s, dtype, device, bias_dtype, compute_dtype, w
|
|||||||
|
|
||||||
|
|
||||||
def cast_bias_weight(s, input=None, dtype=None, device=None, bias_dtype=None, offloadable=False, compute_dtype=None, want_requant=False):
|
def cast_bias_weight(s, input=None, dtype=None, device=None, bias_dtype=None, offloadable=False, compute_dtype=None, want_requant=False):
|
||||||
# NOTE: offloadable=False is a a legacy and if you are a custom node author reading this please pass
|
# NOTE: offloadable=False is a legacy mode and if you are a custom node author reading this please pass
|
||||||
# offloadable=True and call uncast_bias_weight() after your last usage of the weight/bias. This
|
# offloadable=True and call uncast_bias_weight() after your last usage of the weight/bias. This
|
||||||
# will add async-offload support to your cast and improve performance.
|
# will add async-offload support to your cast and improve performance.
|
||||||
if input is not None:
|
if input is not None:
|
||||||
@ -1376,6 +1376,7 @@ def pick_operations(weight_dtype, compute_dtype, load_device=None, disable_fast_
|
|||||||
if not fp8_compute:
|
if not fp8_compute:
|
||||||
disabled.add("float8_e4m3fn")
|
disabled.add("float8_e4m3fn")
|
||||||
disabled.add("float8_e5m2")
|
disabled.add("float8_e5m2")
|
||||||
|
logging.info("Native ops: {} {}".format(", ".join(QUANT_ALGOS.keys() - disabled), ", emulated ops: {}".format(", ".join(disabled)) if len(disabled) > 0 else ""))
|
||||||
return mixed_precision_ops(model_config.quant_config, compute_dtype, disabled=disabled)
|
return mixed_precision_ops(model_config.quant_config, compute_dtype, disabled=disabled)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -37,11 +37,12 @@ def prepare_noise(latent_image, seed, noise_inds=None):
|
|||||||
|
|
||||||
return noises
|
return noises
|
||||||
|
|
||||||
def fix_empty_latent_channels(model, latent_image, downscale_ratio_spacial=None):
|
def fix_empty_latent_channels(model, latent_image, downscale_ratio_spacial=None, downscale_ratio_temporal=None):
|
||||||
if latent_image.is_nested:
|
if latent_image.is_nested:
|
||||||
return latent_image
|
return latent_image
|
||||||
latent_format = model.get_model_object("latent_format") #Resize the empty latent image so it has the right number of channels
|
latent_format = model.get_model_object("latent_format") #Resize the empty latent image so it has the right number of channels
|
||||||
if torch.count_nonzero(latent_image) == 0:
|
is_empty = torch.count_nonzero(latent_image) == 0
|
||||||
|
if is_empty:
|
||||||
if latent_format.latent_channels != latent_image.shape[1]:
|
if latent_format.latent_channels != latent_image.shape[1]:
|
||||||
latent_image = comfy.utils.repeat_to_batch_size(latent_image, latent_format.latent_channels, dim=1)
|
latent_image = comfy.utils.repeat_to_batch_size(latent_image, latent_format.latent_channels, dim=1)
|
||||||
if downscale_ratio_spacial is not None:
|
if downscale_ratio_spacial is not None:
|
||||||
@ -51,6 +52,13 @@ def fix_empty_latent_channels(model, latent_image, downscale_ratio_spacial=None)
|
|||||||
|
|
||||||
if latent_format.latent_dimensions == 3 and latent_image.ndim == 4:
|
if latent_format.latent_dimensions == 3 and latent_image.ndim == 4:
|
||||||
latent_image = latent_image.unsqueeze(2)
|
latent_image = latent_image.unsqueeze(2)
|
||||||
|
|
||||||
|
if is_empty and downscale_ratio_temporal is not None:
|
||||||
|
if downscale_ratio_temporal != latent_format.temporal_downscale_ratio:
|
||||||
|
ratio = downscale_ratio_temporal / latent_format.temporal_downscale_ratio
|
||||||
|
new_t = max(1, round(latent_image.shape[2] * ratio))
|
||||||
|
latent_image = comfy.utils.repeat_to_batch_size(latent_image, new_t, dim=2)
|
||||||
|
|
||||||
return latent_image
|
return latent_image
|
||||||
|
|
||||||
def prepare_sampling(model, noise_shape, positive, negative, noise_mask):
|
def prepare_sampling(model, noise_shape, positive, negative, noise_mask):
|
||||||
|
|||||||
@ -424,6 +424,13 @@ class CLIP:
|
|||||||
sd_clip[k] = sd_tokenizer[k]
|
sd_clip[k] = sd_tokenizer[k]
|
||||||
return sd_clip
|
return sd_clip
|
||||||
|
|
||||||
|
def state_dict_for_saving(self):
|
||||||
|
sd_clip = self.patcher.model_state_dict_for_saving()
|
||||||
|
sd_tokenizer = self.tokenizer.state_dict()
|
||||||
|
for k in sd_tokenizer:
|
||||||
|
sd_clip[k] = sd_tokenizer[k]
|
||||||
|
return sd_clip
|
||||||
|
|
||||||
def load_model(self, tokens={}):
|
def load_model(self, tokens={}):
|
||||||
memory_used = 0
|
memory_used = 0
|
||||||
if hasattr(self.cond_stage_model, "memory_estimation_function"):
|
if hasattr(self.cond_stage_model, "memory_estimation_function"):
|
||||||
@ -1921,7 +1928,7 @@ def save_checkpoint(output_path, model, clip=None, vae=None, clip_vision=None, m
|
|||||||
load_models = [model]
|
load_models = [model]
|
||||||
if clip is not None:
|
if clip is not None:
|
||||||
load_models.append(clip.load_model())
|
load_models.append(clip.load_model())
|
||||||
clip_sd = clip.get_sd()
|
clip_sd = clip.state_dict_for_saving()
|
||||||
vae_sd = None
|
vae_sd = None
|
||||||
if vae is not None:
|
if vae is not None:
|
||||||
vae_sd = vae.get_sd()
|
vae_sd = vae.get_sd()
|
||||||
|
|||||||
@ -760,7 +760,7 @@ class Qwen35ImageTokenizer(sd1_clip.SD1Tokenizer):
|
|||||||
def tokenize_with_weights(self, text, return_word_ids=False, llama_template=None, images=[], prevent_empty_text=False, thinking=False, **kwargs):
|
def tokenize_with_weights(self, text, return_word_ids=False, llama_template=None, images=[], prevent_empty_text=False, thinking=False, **kwargs):
|
||||||
image = kwargs.get("image", None)
|
image = kwargs.get("image", None)
|
||||||
if image is not None and len(images) == 0:
|
if image is not None and len(images) == 0:
|
||||||
images = [image]
|
images = [image[i:i + 1] for i in range(image.shape[0])]
|
||||||
|
|
||||||
skip_template = False
|
skip_template = False
|
||||||
if text.startswith('<|im_start|>'):
|
if text.startswith('<|im_start|>'):
|
||||||
@ -771,13 +771,16 @@ class Qwen35ImageTokenizer(sd1_clip.SD1Tokenizer):
|
|||||||
if skip_template:
|
if skip_template:
|
||||||
llama_text = text
|
llama_text = text
|
||||||
else:
|
else:
|
||||||
if llama_template is None:
|
if llama_template is not None:
|
||||||
if len(images) > 0:
|
template = llama_template
|
||||||
llama_text = self.llama_template_images.format(text)
|
elif len(images) == 0:
|
||||||
else:
|
template = self.llama_template
|
||||||
llama_text = self.llama_template.format(text)
|
|
||||||
else:
|
else:
|
||||||
llama_text = llama_template.format(text)
|
template = self.llama_template_images
|
||||||
|
if len(images) > 1:
|
||||||
|
vision_block = "<|vision_start|><|image_pad|><|vision_end|>"
|
||||||
|
template = template.replace(vision_block, vision_block * len(images), 1)
|
||||||
|
llama_text = template.format(text)
|
||||||
if not thinking:
|
if not thinking:
|
||||||
llama_text += "<think>\n</think>\n"
|
llama_text += "<think>\n</think>\n"
|
||||||
|
|
||||||
|
|||||||
101
comfy_api_nodes/apis/bytedance_llm.py
Normal file
101
comfy_api_nodes/apis/bytedance_llm.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
"""Pydantic models for BytePlus ModelArk Responses API.
|
||||||
|
|
||||||
|
See: https://docs.byteplus.com/en/docs/ModelArk/1585128 (request)
|
||||||
|
https://docs.byteplus.com/en/docs/ModelArk/1783703 (response)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusInputText(BaseModel):
|
||||||
|
type: Literal["input_text"] = "input_text"
|
||||||
|
text: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusInputImage(BaseModel):
|
||||||
|
type: Literal["input_image"] = "input_image"
|
||||||
|
image_url: str = Field(..., description="Image URL or `data:image/...;base64,...` payload")
|
||||||
|
detail: str = Field("auto", description="One of high, low, auto")
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusInputVideo(BaseModel):
|
||||||
|
type: Literal["input_video"] = "input_video"
|
||||||
|
video_url: str = Field(..., description="Video URL or `data:video/...;base64,...` payload")
|
||||||
|
fps: float | None = Field(None, ge=0.2, le=5.0)
|
||||||
|
|
||||||
|
|
||||||
|
BytePlusMessageContent = BytePlusInputText | BytePlusInputImage | BytePlusInputVideo
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusInputMessage(BaseModel):
|
||||||
|
type: Literal["message"] = "message"
|
||||||
|
role: str = Field(..., description="One of user, system, assistant, developer")
|
||||||
|
content: list[BytePlusMessageContent] = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusResponseCreateRequest(BaseModel):
|
||||||
|
model: str = Field(...)
|
||||||
|
input: list[BytePlusInputMessage] = Field(...)
|
||||||
|
instructions: str | None = Field(None)
|
||||||
|
max_output_tokens: int | None = Field(None, ge=1)
|
||||||
|
temperature: float | None = Field(None, ge=0.0, le=2.0)
|
||||||
|
store: bool | None = Field(False)
|
||||||
|
stream: bool | None = Field(False)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusOutputText(BaseModel):
|
||||||
|
type: Literal["output_text"] = "output_text"
|
||||||
|
text: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusOutputRefusal(BaseModel):
|
||||||
|
type: Literal["refusal"] = "refusal"
|
||||||
|
refusal: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusOutputContent(BaseModel):
|
||||||
|
type: str = Field(...)
|
||||||
|
text: str | None = Field(None)
|
||||||
|
refusal: str | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusOutputMessage(BaseModel):
|
||||||
|
type: str = Field(...)
|
||||||
|
id: str | None = Field(None)
|
||||||
|
role: str | None = Field(None)
|
||||||
|
status: str | None = Field(None)
|
||||||
|
content: list[BytePlusOutputContent] | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusInputTokensDetails(BaseModel):
|
||||||
|
cached_tokens: int | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusOutputTokensDetails(BaseModel):
|
||||||
|
reasoning_tokens: int | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusResponseUsage(BaseModel):
|
||||||
|
input_tokens: int | None = Field(None)
|
||||||
|
output_tokens: int | None = Field(None)
|
||||||
|
total_tokens: int | None = Field(None)
|
||||||
|
input_tokens_details: BytePlusInputTokensDetails | None = Field(None)
|
||||||
|
output_tokens_details: BytePlusOutputTokensDetails | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusResponseError(BaseModel):
|
||||||
|
code: str = Field(...)
|
||||||
|
message: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BytePlusResponseObject(BaseModel):
|
||||||
|
id: str | None = Field(None)
|
||||||
|
object: str | None = Field(None)
|
||||||
|
created_at: int | None = Field(None)
|
||||||
|
model: str | None = Field(None)
|
||||||
|
status: str | None = Field(None)
|
||||||
|
error: BytePlusResponseError | None = Field(None)
|
||||||
|
output: list[BytePlusOutputMessage] | None = Field(None)
|
||||||
|
usage: BytePlusResponseUsage | None = Field(None)
|
||||||
@ -49,7 +49,7 @@ def _claude_model_inputs():
|
|||||||
min=0.0,
|
min=0.0,
|
||||||
max=1.0,
|
max=1.0,
|
||||||
step=0.01,
|
step=0.01,
|
||||||
tooltip="Controls randomness. 0.0 is deterministic, 1.0 is most random.",
|
tooltip="Controls randomness. 0.0 is deterministic, 1.0 is most random. Ignored for Opus 4.7.",
|
||||||
advanced=True,
|
advanced=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -208,7 +208,7 @@ class ClaudeNode(IO.ComfyNode):
|
|||||||
validate_string(prompt, strip_whitespace=True, min_length=1)
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
model_label = model["model"]
|
model_label = model["model"]
|
||||||
max_tokens = model["max_tokens"]
|
max_tokens = model["max_tokens"]
|
||||||
temperature = model["temperature"]
|
temperature = None if model_label == "Opus 4.7" else model["temperature"]
|
||||||
|
|
||||||
image_tensors: list[Input.Image] = [t for t in (images or {}).values() if t is not None]
|
image_tensors: list[Input.Image] = [t for t in (images or {}).values() if t is not None]
|
||||||
if sum(get_number_of_images(t) for t in image_tensors) > CLAUDE_MAX_IMAGES:
|
if sum(get_number_of_images(t) for t in image_tensors) > CLAUDE_MAX_IMAGES:
|
||||||
|
|||||||
271
comfy_api_nodes/nodes_bytedance_llm.py
Normal file
271
comfy_api_nodes/nodes_bytedance_llm.py
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
"""API Nodes for ByteDance Seed LLM via the BytePlus ModelArk Responses API.
|
||||||
|
|
||||||
|
See: https://docs.byteplus.com/en/docs/ModelArk/1585128
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing_extensions import override
|
||||||
|
|
||||||
|
from comfy_api.latest import IO, ComfyExtension, Input
|
||||||
|
from comfy_api_nodes.apis.bytedance_llm import (
|
||||||
|
BytePlusInputImage,
|
||||||
|
BytePlusInputMessage,
|
||||||
|
BytePlusInputText,
|
||||||
|
BytePlusInputVideo,
|
||||||
|
BytePlusMessageContent,
|
||||||
|
BytePlusResponseCreateRequest,
|
||||||
|
BytePlusResponseObject,
|
||||||
|
)
|
||||||
|
from comfy_api_nodes.util import (
|
||||||
|
ApiEndpoint,
|
||||||
|
get_number_of_images,
|
||||||
|
sync_op,
|
||||||
|
upload_images_to_comfyapi,
|
||||||
|
upload_video_to_comfyapi,
|
||||||
|
validate_string,
|
||||||
|
)
|
||||||
|
|
||||||
|
BYTEPLUS_RESPONSES_ENDPOINT = "/proxy/byteplus/api/v3/responses"
|
||||||
|
SEED_MAX_IMAGES = 20
|
||||||
|
SEED_MAX_VIDEOS = 4
|
||||||
|
|
||||||
|
SEED_MODELS: dict[str, str] = {
|
||||||
|
"Seed 2.0 Pro": "seed-2-0-pro-260328",
|
||||||
|
"Seed 2.0 Lite": "seed-2-0-lite-260228",
|
||||||
|
"Seed 2.0 Mini": "seed-2-0-mini-260215",
|
||||||
|
}
|
||||||
|
|
||||||
|
# USD per 1M tokens: (input, cache_hit_input, output)
|
||||||
|
_SEED_PRICES_PER_MILLION: dict[str, tuple[float, float, float]] = {
|
||||||
|
"seed-2-0-pro-260328": (0.50, 0.10, 3.00),
|
||||||
|
"seed-2-0-lite-260228": (0.25, 0.05, 2.00),
|
||||||
|
"seed-2-0-mini-260215": (0.10, 0.02, 0.40),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _seed_model_inputs(max_images: int = SEED_MAX_IMAGES, max_videos: int = SEED_MAX_VIDEOS):
|
||||||
|
return [
|
||||||
|
IO.Autogrow.Input(
|
||||||
|
"images",
|
||||||
|
template=IO.Autogrow.TemplateNames(
|
||||||
|
IO.Image.Input("image"),
|
||||||
|
names=[f"image_{i}" for i in range(1, max_images + 1)],
|
||||||
|
min=0,
|
||||||
|
),
|
||||||
|
tooltip=f"Optional image(s) to use as context for the model. Up to {max_images} images.",
|
||||||
|
),
|
||||||
|
IO.Autogrow.Input(
|
||||||
|
"videos",
|
||||||
|
template=IO.Autogrow.TemplateNames(
|
||||||
|
IO.Video.Input("video"),
|
||||||
|
names=[f"video_{i}" for i in range(1, max_videos + 1)],
|
||||||
|
min=0,
|
||||||
|
),
|
||||||
|
tooltip=f"Optional video(s) to use as context for the model. Up to {max_videos} videos.",
|
||||||
|
),
|
||||||
|
IO.Float.Input(
|
||||||
|
"temperature",
|
||||||
|
default=1.0,
|
||||||
|
min=0.0,
|
||||||
|
max=2.0,
|
||||||
|
step=0.01,
|
||||||
|
tooltip="Controls randomness. 0.0 is deterministic, higher values are more random.",
|
||||||
|
advanced=True,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _calculate_price(model_id: str, response: BytePlusResponseObject) -> float | None:
|
||||||
|
"""Compute approximate USD price from response usage."""
|
||||||
|
if not response.usage:
|
||||||
|
return None
|
||||||
|
rates = _SEED_PRICES_PER_MILLION.get(model_id)
|
||||||
|
if rates is None:
|
||||||
|
return None
|
||||||
|
input_rate, cache_hit_rate, output_rate = rates
|
||||||
|
input_tokens = response.usage.input_tokens or 0
|
||||||
|
output_tokens = response.usage.output_tokens or 0
|
||||||
|
cached = 0
|
||||||
|
if response.usage.input_tokens_details:
|
||||||
|
cached = response.usage.input_tokens_details.cached_tokens or 0
|
||||||
|
fresh_input = max(0, input_tokens - cached)
|
||||||
|
total = fresh_input * input_rate + cached * cache_hit_rate + output_tokens * output_rate
|
||||||
|
return total / 1_000_000.0
|
||||||
|
|
||||||
|
|
||||||
|
def _get_text_from_response(response: BytePlusResponseObject) -> str:
|
||||||
|
"""Extract concatenated text from all assistant message output_text blocks."""
|
||||||
|
if not response.output:
|
||||||
|
return ""
|
||||||
|
chunks: list[str] = []
|
||||||
|
for item in response.output:
|
||||||
|
if item.type != "message" or not item.content:
|
||||||
|
continue
|
||||||
|
for block in item.content:
|
||||||
|
if block.type == "output_text" and block.text:
|
||||||
|
chunks.append(block.text)
|
||||||
|
elif block.type == "refusal" and block.refusal:
|
||||||
|
raise ValueError(f"Model refused to respond: {block.refusal}")
|
||||||
|
return "\n".join(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
async def _build_image_content_blocks(
|
||||||
|
cls: type[IO.ComfyNode],
|
||||||
|
image_tensors: list[Input.Image],
|
||||||
|
) -> list[BytePlusInputImage]:
|
||||||
|
urls = await upload_images_to_comfyapi(
|
||||||
|
cls,
|
||||||
|
image_tensors,
|
||||||
|
max_images=SEED_MAX_IMAGES,
|
||||||
|
wait_label="Uploading reference images",
|
||||||
|
)
|
||||||
|
return [BytePlusInputImage(image_url=url) for url in urls]
|
||||||
|
|
||||||
|
|
||||||
|
async def _build_video_content_blocks(
|
||||||
|
cls: type[IO.ComfyNode],
|
||||||
|
videos: list[Input.Video],
|
||||||
|
) -> list[BytePlusInputVideo]:
|
||||||
|
blocks: list[BytePlusInputVideo] = []
|
||||||
|
total = len(videos)
|
||||||
|
for idx, video in enumerate(videos):
|
||||||
|
label = "Uploading reference video"
|
||||||
|
if total > 1:
|
||||||
|
label = f"{label} ({idx + 1}/{total})"
|
||||||
|
url = await upload_video_to_comfyapi(cls, video, wait_label=label)
|
||||||
|
blocks.append(BytePlusInputVideo(video_url=url))
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
class ByteDanceSeedNode(IO.ComfyNode):
|
||||||
|
"""Generate text responses from a ByteDance Seed 2.0 model."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="ByteDanceSeedNode",
|
||||||
|
display_name="ByteDance Seed",
|
||||||
|
category="api node/text/ByteDance",
|
||||||
|
essentials_category="Text Generation",
|
||||||
|
description="Generate text responses with ByteDance's Seed 2.0 models. "
|
||||||
|
"Provide a text prompt and optionally one or more images or videos for multimodal context.",
|
||||||
|
inputs=[
|
||||||
|
IO.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Text input to the model.",
|
||||||
|
),
|
||||||
|
IO.DynamicCombo.Input(
|
||||||
|
"model",
|
||||||
|
options=[IO.DynamicCombo.Option(label, _seed_model_inputs()) for label in SEED_MODELS],
|
||||||
|
tooltip="The Seed model used to generate the response.",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed controls whether the node should re-run; "
|
||||||
|
"results are non-deterministic regardless of seed.",
|
||||||
|
),
|
||||||
|
IO.String.Input(
|
||||||
|
"system_prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
optional=True,
|
||||||
|
advanced=True,
|
||||||
|
tooltip="Foundational instructions that dictate the model's behavior.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[IO.String.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="""
|
||||||
|
(
|
||||||
|
$m := widgets.model;
|
||||||
|
$contains($m, "mini") ? {
|
||||||
|
"type": "list_usd",
|
||||||
|
"usd": [0.00025, 0.0009],
|
||||||
|
"format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" }
|
||||||
|
}
|
||||||
|
: $contains($m, "lite") ? {
|
||||||
|
"type": "list_usd",
|
||||||
|
"usd": [0.0003, 0.002],
|
||||||
|
"format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" }
|
||||||
|
}
|
||||||
|
: $contains($m, "pro") ? {
|
||||||
|
"type": "list_usd",
|
||||||
|
"usd": [0.0005, 0.003],
|
||||||
|
"format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" }
|
||||||
|
}
|
||||||
|
: {"type":"text", "text":"Token-based"}
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
prompt: str,
|
||||||
|
model: dict,
|
||||||
|
seed: int,
|
||||||
|
system_prompt: str = "",
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
validate_string(prompt, strip_whitespace=True, min_length=1)
|
||||||
|
model_label = model["model"]
|
||||||
|
temperature = model["temperature"]
|
||||||
|
model_id = SEED_MODELS[model_label]
|
||||||
|
|
||||||
|
image_tensors: list[Input.Image] = [t for t in (model.get("images") or {}).values() if t is not None]
|
||||||
|
if sum(get_number_of_images(t) for t in image_tensors) > SEED_MAX_IMAGES:
|
||||||
|
raise ValueError(f"Up to {SEED_MAX_IMAGES} images are supported per request.")
|
||||||
|
|
||||||
|
video_inputs: list[Input.Video] = [v for v in (model.get("videos") or {}).values() if v is not None]
|
||||||
|
if len(video_inputs) > SEED_MAX_VIDEOS:
|
||||||
|
raise ValueError(f"Up to {SEED_MAX_VIDEOS} videos are supported per request.")
|
||||||
|
|
||||||
|
content: list[BytePlusMessageContent] = []
|
||||||
|
if image_tensors:
|
||||||
|
content.extend(await _build_image_content_blocks(cls, image_tensors))
|
||||||
|
if video_inputs:
|
||||||
|
content.extend(await _build_video_content_blocks(cls, video_inputs))
|
||||||
|
content.append(BytePlusInputText(text=prompt))
|
||||||
|
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path=BYTEPLUS_RESPONSES_ENDPOINT, method="POST"),
|
||||||
|
response_model=BytePlusResponseObject,
|
||||||
|
data=BytePlusResponseCreateRequest(
|
||||||
|
model=model_id,
|
||||||
|
input=[BytePlusInputMessage(role="user", content=content)],
|
||||||
|
instructions=system_prompt or None,
|
||||||
|
temperature=temperature,
|
||||||
|
store=False,
|
||||||
|
stream=False,
|
||||||
|
),
|
||||||
|
price_extractor=lambda r: _calculate_price(model_id, r),
|
||||||
|
)
|
||||||
|
if response.error:
|
||||||
|
raise ValueError(f"Seed API error ({response.error.code}): {response.error.message}")
|
||||||
|
result = _get_text_from_response(response)
|
||||||
|
if not result:
|
||||||
|
raise ValueError("Empty response from Seed model.")
|
||||||
|
return IO.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
|
class ByteDanceLLMExtension(ComfyExtension):
|
||||||
|
@override
|
||||||
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
|
return [ByteDanceSeedNode]
|
||||||
|
|
||||||
|
|
||||||
|
async def comfy_entrypoint() -> ByteDanceLLMExtension:
|
||||||
|
return ByteDanceLLMExtension()
|
||||||
@ -104,7 +104,7 @@ class EmptyAceStep15LatentAudio(IO.ComfyNode):
|
|||||||
def execute(cls, seconds, batch_size) -> IO.NodeOutput:
|
def execute(cls, seconds, batch_size) -> IO.NodeOutput:
|
||||||
length = round((seconds * 48000 / 1920))
|
length = round((seconds * 48000 / 1920))
|
||||||
latent = torch.zeros([batch_size, 64, length], device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype())
|
latent = torch.zeros([batch_size, 64, length], device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype())
|
||||||
return IO.NodeOutput({"samples": latent, "type": "audio"})
|
return IO.NodeOutput({"samples": latent, "type": "audio", "downscale_ratio_temporal": 1764})
|
||||||
|
|
||||||
class ReferenceAudio(IO.ComfyNode):
|
class ReferenceAudio(IO.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class SamplerLCMUpscale(io.ComfyNode):
|
|||||||
def define_schema(cls) -> io.Schema:
|
def define_schema(cls) -> io.Schema:
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerLCMUpscale",
|
node_id="SamplerLCMUpscale",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("scale_ratio", default=1.0, min=0.1, max=20.0, step=0.01, advanced=True),
|
io.Float.Input("scale_ratio", default=1.0, min=0.1, max=20.0, step=0.01, advanced=True),
|
||||||
io.Int.Input("scale_steps", default=-1, min=-1, max=1000, step=1, advanced=True),
|
io.Int.Input("scale_steps", default=-1, min=-1, max=1000, step=1, advanced=True),
|
||||||
@ -123,7 +123,7 @@ class SamplerEulerCFGpp(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerEulerCFGpp",
|
node_id="SamplerEulerCFGpp",
|
||||||
display_name="SamplerEulerCFG++",
|
display_name="SamplerEulerCFG++",
|
||||||
category="experimental", # "sampling/custom_sampling/samplers"
|
category="experimental", # "sampling/samplers"
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("version", options=["regular", "alternative"], advanced=True),
|
io.Combo.Input("version", options=["regular", "alternative"], advanced=True),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class AlignYourStepsScheduler(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="AlignYourStepsScheduler",
|
node_id="AlignYourStepsScheduler",
|
||||||
search_aliases=["AYS scheduler"],
|
search_aliases=["AYS scheduler"],
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("model_type", options=["SD1", "SDXL", "SVD"]),
|
io.Combo.Input("model_type", options=["SD1", "SDXL", "SVD"]),
|
||||||
io.Int.Input("steps", default=10, min=1, max=10000),
|
io.Int.Input("steps", default=10, min=1, max=10000),
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class SamplerARVideo(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerARVideo",
|
node_id="SamplerARVideo",
|
||||||
display_name="Sampler AR Video",
|
display_name="Sampler AR Video",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input(
|
io.Int.Input(
|
||||||
"num_frame_per_block",
|
"num_frame_per_block",
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class EmptyLatentAudio(IO.ComfyNode):
|
|||||||
def execute(cls, seconds, batch_size) -> IO.NodeOutput:
|
def execute(cls, seconds, batch_size) -> IO.NodeOutput:
|
||||||
length = round((seconds * 44100 / 2048) / 2) * 2
|
length = round((seconds * 44100 / 2048) / 2) * 2
|
||||||
latent = torch.zeros([batch_size, 64, length], device=comfy.model_management.intermediate_device())
|
latent = torch.zeros([batch_size, 64, length], device=comfy.model_management.intermediate_device())
|
||||||
return IO.NodeOutput({"samples":latent, "type": "audio"})
|
return IO.NodeOutput({"samples": latent, "type": "audio", "downscale_ratio_temporal": 2048})
|
||||||
|
|
||||||
generate = execute # TODO: remove
|
generate = execute # TODO: remove
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class RemoveBackground(IO.ComfyNode):
|
|||||||
node_id="RemoveBackground",
|
node_id="RemoveBackground",
|
||||||
display_name="Remove Background",
|
display_name="Remove Background",
|
||||||
category="image/background removal",
|
category="image/background removal",
|
||||||
|
description="Generates a foreground mask to remove the background from an image using a background removal model.",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("image", tooltip="Input image to remove the background from"),
|
IO.Image.Input("image", tooltip="Input image to remove the background from"),
|
||||||
IO.BackgroundRemoval.Input("bg_removal_model", tooltip="Background removal model used to generate the mask")
|
IO.BackgroundRemoval.Input("bg_removal_model", tooltip="Background removal model used to generate the mask")
|
||||||
|
|||||||
@ -11,9 +11,9 @@ class Canny(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="Canny",
|
node_id="Canny",
|
||||||
display_name="Canny",
|
display_name="Detect Edges (Canny)",
|
||||||
search_aliases=["edge detection", "outline", "contour detection", "line art"],
|
search_aliases=["edge detection", "outline", "contour detection", "line art"],
|
||||||
category="image/preprocessors",
|
category="image/filters",
|
||||||
essentials_category="Image Tools",
|
essentials_category="Image Tools",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
|
|||||||
@ -111,7 +111,7 @@ class PorterDuffImageComposite(io.ComfyNode):
|
|||||||
node_id="PorterDuffImageComposite",
|
node_id="PorterDuffImageComposite",
|
||||||
search_aliases=["alpha composite", "blend modes", "layer blend", "transparency blend"],
|
search_aliases=["alpha composite", "blend modes", "layer blend", "transparency blend"],
|
||||||
display_name="Porter-Duff Image Composite",
|
display_name="Porter-Duff Image Composite",
|
||||||
category="mask/compositing",
|
category="image/compositing",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("source"),
|
io.Image.Input("source"),
|
||||||
io.Mask.Input("source_alpha"),
|
io.Mask.Input("source_alpha"),
|
||||||
@ -168,7 +168,7 @@ class SplitImageWithAlpha(io.ComfyNode):
|
|||||||
node_id="SplitImageWithAlpha",
|
node_id="SplitImageWithAlpha",
|
||||||
search_aliases=["extract alpha", "separate transparency", "remove alpha"],
|
search_aliases=["extract alpha", "separate transparency", "remove alpha"],
|
||||||
display_name="Split Image with Alpha",
|
display_name="Split Image with Alpha",
|
||||||
category="mask/compositing",
|
category="image/compositing",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
],
|
],
|
||||||
@ -192,7 +192,7 @@ class JoinImageWithAlpha(io.ComfyNode):
|
|||||||
node_id="JoinImageWithAlpha",
|
node_id="JoinImageWithAlpha",
|
||||||
search_aliases=["add transparency", "apply alpha", "composite alpha", "RGBA"],
|
search_aliases=["add transparency", "apply alpha", "composite alpha", "RGBA"],
|
||||||
display_name="Join Image with Alpha",
|
display_name="Join Image with Alpha",
|
||||||
category="mask/compositing",
|
category="image/compositing",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
io.Mask.Input("alpha"),
|
io.Mask.Input("alpha"),
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class BasicScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BasicScheduler",
|
node_id="BasicScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Combo.Input("scheduler", options=comfy.samplers.SCHEDULER_NAMES),
|
io.Combo.Input("scheduler", options=comfy.samplers.SCHEDULER_NAMES),
|
||||||
@ -47,7 +47,7 @@ class KarrasScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="KarrasScheduler",
|
node_id="KarrasScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
||||||
@ -69,7 +69,7 @@ class ExponentialScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ExponentialScheduler",
|
node_id="ExponentialScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
||||||
@ -90,7 +90,7 @@ class PolyexponentialScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="PolyexponentialScheduler",
|
node_id="PolyexponentialScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
||||||
@ -112,7 +112,7 @@ class LaplaceScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LaplaceScheduler",
|
node_id="LaplaceScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("sigma_max", default=14.614642, min=0.0, max=5000.0, step=0.01, round=False, advanced=True),
|
||||||
@ -136,7 +136,7 @@ class SDTurboScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SDTurboScheduler",
|
node_id="SDTurboScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Int.Input("steps", default=1, min=1, max=10),
|
io.Int.Input("steps", default=1, min=1, max=10),
|
||||||
@ -160,7 +160,7 @@ class BetaSamplingScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BetaSamplingScheduler",
|
node_id="BetaSamplingScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
@ -182,7 +182,7 @@ class VPScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="VPScheduler",
|
node_id="VPScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("beta_d", default=19.9, min=0.0, max=5000.0, step=0.01, round=False, advanced=True), #TODO: fix default values
|
io.Float.Input("beta_d", default=19.9, min=0.0, max=5000.0, step=0.01, round=False, advanced=True), #TODO: fix default values
|
||||||
@ -204,7 +204,7 @@ class SplitSigmas(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SplitSigmas",
|
node_id="SplitSigmas",
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Sigmas.Input("sigmas"),
|
io.Sigmas.Input("sigmas"),
|
||||||
io.Int.Input("step", default=0, min=0, max=10000),
|
io.Int.Input("step", default=0, min=0, max=10000),
|
||||||
@ -228,7 +228,7 @@ class SplitSigmasDenoise(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SplitSigmasDenoise",
|
node_id="SplitSigmasDenoise",
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Sigmas.Input("sigmas"),
|
io.Sigmas.Input("sigmas"),
|
||||||
io.Float.Input("denoise", default=1.0, min=0.0, max=1.0, step=0.01),
|
io.Float.Input("denoise", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||||
@ -254,7 +254,7 @@ class FlipSigmas(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="FlipSigmas",
|
node_id="FlipSigmas",
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[io.Sigmas.Input("sigmas")],
|
inputs=[io.Sigmas.Input("sigmas")],
|
||||||
outputs=[io.Sigmas.Output()]
|
outputs=[io.Sigmas.Output()]
|
||||||
)
|
)
|
||||||
@ -276,7 +276,7 @@ class SetFirstSigma(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SetFirstSigma",
|
node_id="SetFirstSigma",
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Sigmas.Input("sigmas"),
|
io.Sigmas.Input("sigmas"),
|
||||||
io.Float.Input("sigma", default=136.0, min=0.0, max=20000.0, step=0.001, round=False),
|
io.Float.Input("sigma", default=136.0, min=0.0, max=20000.0, step=0.001, round=False),
|
||||||
@ -298,7 +298,7 @@ class ExtendIntermediateSigmas(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ExtendIntermediateSigmas",
|
node_id="ExtendIntermediateSigmas",
|
||||||
search_aliases=["interpolate sigmas"],
|
search_aliases=["interpolate sigmas"],
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Sigmas.Input("sigmas"),
|
io.Sigmas.Input("sigmas"),
|
||||||
io.Int.Input("steps", default=2, min=1, max=100),
|
io.Int.Input("steps", default=2, min=1, max=100),
|
||||||
@ -351,7 +351,7 @@ class SamplingPercentToSigma(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplingPercentToSigma",
|
node_id="SamplingPercentToSigma",
|
||||||
category="sampling/custom_sampling/sigmas",
|
category="sampling/sigmas",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Float.Input("sampling_percent", default=0.0, min=0.0, max=1.0, step=0.0001),
|
io.Float.Input("sampling_percent", default=0.0, min=0.0, max=1.0, step=0.0001),
|
||||||
@ -379,7 +379,7 @@ class KSamplerSelect(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="KSamplerSelect",
|
node_id="KSamplerSelect",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[io.Combo.Input("sampler_name", options=comfy.samplers.SAMPLER_NAMES)],
|
inputs=[io.Combo.Input("sampler_name", options=comfy.samplers.SAMPLER_NAMES)],
|
||||||
outputs=[io.Sampler.Output()]
|
outputs=[io.Sampler.Output()]
|
||||||
)
|
)
|
||||||
@ -396,7 +396,7 @@ class SamplerDPMPP_3M_SDE(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerDPMPP_3M_SDE",
|
node_id="SamplerDPMPP_3M_SDE",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
@ -421,7 +421,7 @@ class SamplerDPMPP_2M_SDE(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerDPMPP_2M_SDE",
|
node_id="SamplerDPMPP_2M_SDE",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("solver_type", options=['midpoint', 'heun']),
|
io.Combo.Input("solver_type", options=['midpoint', 'heun']),
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
@ -448,7 +448,7 @@ class SamplerDPMPP_SDE(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerDPMPP_SDE",
|
node_id="SamplerDPMPP_SDE",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
@ -474,7 +474,7 @@ class SamplerDPMPP_2S_Ancestral(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerDPMPP_2S_Ancestral",
|
node_id="SamplerDPMPP_2S_Ancestral",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False),
|
||||||
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False),
|
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False),
|
||||||
@ -494,7 +494,7 @@ class SamplerEulerAncestral(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerEulerAncestral",
|
node_id="SamplerEulerAncestral",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("s_noise", default=1.0, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
@ -515,7 +515,7 @@ class SamplerEulerAncestralCFGPP(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerEulerAncestralCFGPP",
|
node_id="SamplerEulerAncestralCFGPP",
|
||||||
display_name="SamplerEulerAncestralCFG++",
|
display_name="SamplerEulerAncestralCFG++",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=1.0, step=0.01, round=False),
|
io.Float.Input("eta", default=1.0, min=0.0, max=1.0, step=0.01, round=False),
|
||||||
io.Float.Input("s_noise", default=1.0, min=0.0, max=10.0, step=0.01, round=False),
|
io.Float.Input("s_noise", default=1.0, min=0.0, max=10.0, step=0.01, round=False),
|
||||||
@ -537,7 +537,7 @@ class SamplerLMS(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerLMS",
|
node_id="SamplerLMS",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[io.Int.Input("order", default=4, min=1, max=100, advanced=True)],
|
inputs=[io.Int.Input("order", default=4, min=1, max=100, advanced=True)],
|
||||||
outputs=[io.Sampler.Output()]
|
outputs=[io.Sampler.Output()]
|
||||||
)
|
)
|
||||||
@ -554,7 +554,7 @@ class SamplerDPMAdaptative(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerDPMAdaptative",
|
node_id="SamplerDPMAdaptative",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("order", default=3, min=2, max=3, advanced=True),
|
io.Int.Input("order", default=3, min=2, max=3, advanced=True),
|
||||||
io.Float.Input("rtol", default=0.05, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("rtol", default=0.05, min=0.0, max=100.0, step=0.01, round=False, advanced=True),
|
||||||
@ -585,7 +585,7 @@ class SamplerER_SDE(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerER_SDE",
|
node_id="SamplerER_SDE",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("solver_type", options=["ER-SDE", "Reverse-time SDE", "ODE"]),
|
io.Combo.Input("solver_type", options=["ER-SDE", "Reverse-time SDE", "ODE"]),
|
||||||
io.Int.Input("max_stage", default=3, min=1, max=3, advanced=True),
|
io.Int.Input("max_stage", default=3, min=1, max=3, advanced=True),
|
||||||
@ -623,7 +623,7 @@ class SamplerSASolver(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerSASolver",
|
node_id="SamplerSASolver",
|
||||||
search_aliases=["sde"],
|
search_aliases=["sde"],
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=10.0, step=0.01, round=False, advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=10.0, step=0.01, round=False, advanced=True),
|
||||||
@ -668,7 +668,7 @@ class SamplerSEEDS2(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SamplerSEEDS2",
|
node_id="SamplerSEEDS2",
|
||||||
search_aliases=["sde", "exp heun"],
|
search_aliases=["sde", "exp heun"],
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("solver_type", options=["phi_1", "phi_2"]),
|
io.Combo.Input("solver_type", options=["phi_1", "phi_2"]),
|
||||||
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, tooltip="Stochastic strength", advanced=True),
|
io.Float.Input("eta", default=1.0, min=0.0, max=100.0, step=0.01, round=False, tooltip="Stochastic strength", advanced=True),
|
||||||
@ -750,7 +750,7 @@ class SamplerCustom(io.ComfyNode):
|
|||||||
latent = latent_image
|
latent = latent_image
|
||||||
latent_image = latent["samples"]
|
latent_image = latent["samples"]
|
||||||
latent = latent.copy()
|
latent = latent.copy()
|
||||||
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image, latent.get("downscale_ratio_spacial", None))
|
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image, latent.get("downscale_ratio_spacial", None), latent.get("downscale_ratio_temporal", None))
|
||||||
latent["samples"] = latent_image
|
latent["samples"] = latent_image
|
||||||
|
|
||||||
if not add_noise:
|
if not add_noise:
|
||||||
@ -770,6 +770,7 @@ class SamplerCustom(io.ComfyNode):
|
|||||||
|
|
||||||
out = latent.copy()
|
out = latent.copy()
|
||||||
out.pop("downscale_ratio_spacial", None)
|
out.pop("downscale_ratio_spacial", None)
|
||||||
|
out.pop("downscale_ratio_temporal", None)
|
||||||
out["samples"] = samples
|
out["samples"] = samples
|
||||||
if "x0" in x0_output:
|
if "x0" in x0_output:
|
||||||
x0_out = model.model.process_latent_out(x0_output["x0"].cpu())
|
x0_out = model.model.process_latent_out(x0_output["x0"].cpu())
|
||||||
@ -793,7 +794,8 @@ class BasicGuider(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BasicGuider",
|
node_id="BasicGuider",
|
||||||
category="sampling/custom_sampling/guiders",
|
display_name="Basic Guider",
|
||||||
|
category="sampling/guiders",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Conditioning.Input("conditioning"),
|
io.Conditioning.Input("conditioning"),
|
||||||
@ -814,7 +816,8 @@ class CFGGuider(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="CFGGuider",
|
node_id="CFGGuider",
|
||||||
category="sampling/custom_sampling/guiders",
|
display_name="CFG Guider",
|
||||||
|
category="sampling/guiders",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Conditioning.Input("positive"),
|
io.Conditioning.Input("positive"),
|
||||||
@ -868,7 +871,8 @@ class DualCFGGuider(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="DualCFGGuider",
|
node_id="DualCFGGuider",
|
||||||
search_aliases=["dual prompt guidance"],
|
search_aliases=["dual prompt guidance"],
|
||||||
category="sampling/custom_sampling/guiders",
|
display_name="Dual CFG Guider",
|
||||||
|
category="sampling/guiders",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
io.Conditioning.Input("cond1"),
|
io.Conditioning.Input("cond1"),
|
||||||
@ -896,7 +900,7 @@ class DisableNoise(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="DisableNoise",
|
node_id="DisableNoise",
|
||||||
search_aliases=["zero noise"],
|
search_aliases=["zero noise"],
|
||||||
category="sampling/custom_sampling/noise",
|
category="sampling/noise",
|
||||||
inputs=[],
|
inputs=[],
|
||||||
outputs=[io.Noise.Output()]
|
outputs=[io.Noise.Output()]
|
||||||
)
|
)
|
||||||
@ -913,7 +917,7 @@ class RandomNoise(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="RandomNoise",
|
node_id="RandomNoise",
|
||||||
category="sampling/custom_sampling/noise",
|
category="sampling/noise",
|
||||||
inputs=[io.Int.Input("noise_seed", default=0, min=0, max=0xffffffffffffffff, control_after_generate=True)],
|
inputs=[io.Int.Input("noise_seed", default=0, min=0, max=0xffffffffffffffff, control_after_generate=True)],
|
||||||
outputs=[io.Noise.Output()]
|
outputs=[io.Noise.Output()]
|
||||||
)
|
)
|
||||||
@ -949,7 +953,7 @@ class SamplerCustomAdvanced(io.ComfyNode):
|
|||||||
latent = latent_image
|
latent = latent_image
|
||||||
latent_image = latent["samples"]
|
latent_image = latent["samples"]
|
||||||
latent = latent.copy()
|
latent = latent.copy()
|
||||||
latent_image = comfy.sample.fix_empty_latent_channels(guider.model_patcher, latent_image, latent.get("downscale_ratio_spacial", None))
|
latent_image = comfy.sample.fix_empty_latent_channels(guider.model_patcher, latent_image, latent.get("downscale_ratio_spacial", None), latent.get("downscale_ratio_temporal", None))
|
||||||
latent["samples"] = latent_image
|
latent["samples"] = latent_image
|
||||||
|
|
||||||
noise_mask = None
|
noise_mask = None
|
||||||
@ -965,6 +969,7 @@ class SamplerCustomAdvanced(io.ComfyNode):
|
|||||||
|
|
||||||
out = latent.copy()
|
out = latent.copy()
|
||||||
out.pop("downscale_ratio_spacial", None)
|
out.pop("downscale_ratio_spacial", None)
|
||||||
|
out.pop("downscale_ratio_temporal", None)
|
||||||
out["samples"] = samples
|
out["samples"] = samples
|
||||||
if "x0" in x0_output:
|
if "x0" in x0_output:
|
||||||
x0_out = guider.model_patcher.model.process_latent_out(x0_output["x0"].cpu())
|
x0_out = guider.model_patcher.model.process_latent_out(x0_output["x0"].cpu())
|
||||||
|
|||||||
@ -215,7 +215,7 @@ class Flux2Scheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="Flux2Scheduler",
|
node_id="Flux2Scheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=4096),
|
io.Int.Input("steps", default=20, min=1, max=4096),
|
||||||
io.Int.Input("width", default=1024, min=16, max=nodes.MAX_RESOLUTION, step=1),
|
io.Int.Input("width", default=1024, min=16, max=nodes.MAX_RESOLUTION, step=1),
|
||||||
@ -263,7 +263,7 @@ class FluxKVCache(io.ComfyNode):
|
|||||||
node_id="FluxKVCache",
|
node_id="FluxKVCache",
|
||||||
display_name="Flux KV Cache",
|
display_name="Flux KV Cache",
|
||||||
description="Enables KV Cache optimization for reference images on Flux family models.",
|
description="Enables KV Cache optimization for reference images on Flux family models.",
|
||||||
category="",
|
category="experimental",
|
||||||
is_experimental=True,
|
is_experimental=True,
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model", tooltip="The model to use KV Cache on."),
|
io.Model.Input("model", tooltip="The model to use KV Cache on."),
|
||||||
|
|||||||
@ -340,7 +340,7 @@ class GITSScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="GITSScheduler",
|
node_id="GITSScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Float.Input("coeff", default=1.20, min=0.80, max=1.50, step=0.05, advanced=True),
|
io.Float.Input("coeff", default=1.20, min=0.80, max=1.50, step=0.05, advanced=True),
|
||||||
io.Int.Input("steps", default=10, min=2, max=1000),
|
io.Int.Input("steps", default=10, min=2, max=1000),
|
||||||
|
|||||||
@ -162,7 +162,7 @@ class ImageAddNoise(IO.ComfyNode):
|
|||||||
node_id="ImageAddNoise",
|
node_id="ImageAddNoise",
|
||||||
search_aliases=["film grain"],
|
search_aliases=["film grain"],
|
||||||
display_name="Add Noise to Image",
|
display_name="Add Noise to Image",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("image"),
|
IO.Image.Input("image"),
|
||||||
IO.Int.Input(
|
IO.Int.Input(
|
||||||
@ -194,7 +194,8 @@ class SaveAnimatedWEBP(IO.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="SaveAnimatedWEBP",
|
node_id="SaveAnimatedWEBP",
|
||||||
category="image/animation",
|
display_name="Save Animated WEBP",
|
||||||
|
category="image",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("images"),
|
IO.Image.Input("images"),
|
||||||
IO.String.Input("filename_prefix", default="ComfyUI"),
|
IO.String.Input("filename_prefix", default="ComfyUI"),
|
||||||
@ -231,7 +232,8 @@ class SaveAnimatedPNG(IO.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="SaveAnimatedPNG",
|
node_id="SaveAnimatedPNG",
|
||||||
category="image/animation",
|
display_name="Save Animated PNG",
|
||||||
|
category="image",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("images"),
|
IO.Image.Input("images"),
|
||||||
IO.String.Input("filename_prefix", default="ComfyUI"),
|
IO.String.Input("filename_prefix", default="ComfyUI"),
|
||||||
@ -493,7 +495,7 @@ class SaveSVGNode(IO.ComfyNode):
|
|||||||
search_aliases=["export vector", "save vector graphics"],
|
search_aliases=["export vector", "save vector graphics"],
|
||||||
display_name="Save SVG",
|
display_name="Save SVG",
|
||||||
description="Save SVG files on disk.",
|
description="Save SVG files on disk.",
|
||||||
category="image/save",
|
category="image",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.SVG.Input("svg"),
|
IO.SVG.Input("svg"),
|
||||||
IO.String.Input(
|
IO.String.Input(
|
||||||
|
|||||||
@ -77,7 +77,7 @@ class EmptyLTXVLatentVideo(io.ComfyNode):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, width, height, length, batch_size=1) -> io.NodeOutput:
|
def execute(cls, width, height, length, batch_size=1) -> io.NodeOutput:
|
||||||
latent = torch.zeros([batch_size, 128, ((length - 1) // 8) + 1, height // 32, width // 32], device=comfy.model_management.intermediate_device())
|
latent = torch.zeros([batch_size, 128, ((length - 1) // 8) + 1, height // 32, width // 32], device=comfy.model_management.intermediate_device())
|
||||||
return io.NodeOutput({"samples": latent})
|
return io.NodeOutput({"samples": latent, "downscale_ratio_spacial": 32})
|
||||||
|
|
||||||
generate = execute # TODO: remove
|
generate = execute # TODO: remove
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ class LTXVImgToVideoInplace(io.ComfyNode):
|
|||||||
generate = execute # TODO: remove
|
generate = execute # TODO: remove
|
||||||
|
|
||||||
|
|
||||||
def _append_guide_attention_entry(positive, negative, pre_filter_count, latent_shape, strength=1.0):
|
def _append_guide_attention_entry(positive, negative, pre_filter_count, latent_shape, strength=1.0, attention_mask=None):
|
||||||
"""Append a guide_attention_entry to both positive and negative conditioning.
|
"""Append a guide_attention_entry to both positive and negative conditioning.
|
||||||
|
|
||||||
Each entry tracks one guide reference for per-reference attention control.
|
Each entry tracks one guide reference for per-reference attention control.
|
||||||
@ -184,9 +184,10 @@ def _append_guide_attention_entry(positive, negative, pre_filter_count, latent_s
|
|||||||
new_entry = {
|
new_entry = {
|
||||||
"pre_filter_count": pre_filter_count,
|
"pre_filter_count": pre_filter_count,
|
||||||
"strength": strength,
|
"strength": strength,
|
||||||
"pixel_mask": None,
|
"pixel_mask": attention_mask.unsqueeze(0).unsqueeze(0) if attention_mask is not None else None, # reshape to (1, 1, F, H, W)
|
||||||
"latent_shape": latent_shape,
|
"latent_shape": latent_shape,
|
||||||
}
|
}
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for cond in (positive, negative):
|
for cond in (positive, negative):
|
||||||
# Read existing entries from this specific conditioning
|
# Read existing entries from this specific conditioning
|
||||||
@ -196,8 +197,7 @@ def _append_guide_attention_entry(positive, negative, pre_filter_count, latent_s
|
|||||||
if found is not None:
|
if found is not None:
|
||||||
existing = found
|
existing = found
|
||||||
break
|
break
|
||||||
# Shallow copy and append (no deepcopy needed — entries contain
|
# Shallow copy only and append (pixel_mask is never mutated).
|
||||||
# only scalars and None for pixel_mask at this call site).
|
|
||||||
entries = [*existing, new_entry]
|
entries = [*existing, new_entry]
|
||||||
results.append(node_helpers.conditioning_set_values(
|
results.append(node_helpers.conditioning_set_values(
|
||||||
cond, {"guide_attention_entries": entries}
|
cond, {"guide_attention_entries": entries}
|
||||||
@ -263,6 +263,12 @@ class LTXVAddGuide(io.ComfyNode):
|
|||||||
"down to the nearest multiple of 8. Negative values are counted from the end of the video.",
|
"down to the nearest multiple of 8. Negative values are counted from the end of the video.",
|
||||||
),
|
),
|
||||||
io.Float.Input("strength", default=1.0, min=0.0, max=10.0, step=0.01),
|
io.Float.Input("strength", default=1.0, min=0.0, max=10.0, step=0.01),
|
||||||
|
io.Mask.Input(
|
||||||
|
"attention_mask",
|
||||||
|
optional=True,
|
||||||
|
tooltip="Optional pixel-space spatial mask. Controls per-region "
|
||||||
|
"conditioning influence via self-attention, multiplied by strength.",
|
||||||
|
),
|
||||||
ICLoRAParameters.Input(
|
ICLoRAParameters.Input(
|
||||||
"iclora_parameters",
|
"iclora_parameters",
|
||||||
optional=True,
|
optional=True,
|
||||||
@ -410,7 +416,7 @@ class LTXVAddGuide(io.ComfyNode):
|
|||||||
return latent_image, noise_mask
|
return latent_image, noise_mask
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, positive, negative, vae, latent, image, frame_idx, strength, iclora_parameters=None) -> io.NodeOutput:
|
def execute(cls, positive, negative, vae, latent, image, frame_idx, strength, attention_mask=None, iclora_parameters=None) -> io.NodeOutput:
|
||||||
scale_factors = vae.downscale_index_formula
|
scale_factors = vae.downscale_index_formula
|
||||||
latent_image = latent["samples"]
|
latent_image = latent["samples"]
|
||||||
noise_mask = get_noise_mask(latent)
|
noise_mask = get_noise_mask(latent)
|
||||||
@ -469,6 +475,7 @@ class LTXVAddGuide(io.ComfyNode):
|
|||||||
pre_filter_count = t.shape[2] * t.shape[3] * t.shape[4]
|
pre_filter_count = t.shape[2] * t.shape[3] * t.shape[4]
|
||||||
positive, negative = _append_guide_attention_entry(
|
positive, negative = _append_guide_attention_entry(
|
||||||
positive, negative, pre_filter_count, guide_latent_shape, strength=strength,
|
positive, negative, pre_filter_count, guide_latent_shape, strength=strength,
|
||||||
|
attention_mask=attention_mask,
|
||||||
)
|
)
|
||||||
|
|
||||||
return io.NodeOutput(positive, negative, {"samples": latent_image, "noise_mask": noise_mask})
|
return io.NodeOutput(positive, negative, {"samples": latent_image, "noise_mask": noise_mask})
|
||||||
@ -594,7 +601,7 @@ class LTXVScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="LTXVScheduler",
|
node_id="LTXVScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Int.Input("steps", default=20, min=1, max=10000),
|
io.Int.Input("steps", default=20, min=1, max=10000),
|
||||||
io.Float.Input("max_shift", default=2.05, min=0.0, max=100.0, step=0.01),
|
io.Float.Input("max_shift", default=2.05, min=0.0, max=100.0, step=0.01),
|
||||||
|
|||||||
@ -83,7 +83,7 @@ class ImageCompositeMasked(IO.ComfyNode):
|
|||||||
node_id="ImageCompositeMasked",
|
node_id="ImageCompositeMasked",
|
||||||
search_aliases=["overlay", "layer", "paste image", "images composition"],
|
search_aliases=["overlay", "layer", "paste image", "images composition"],
|
||||||
display_name="Image Composite Masked",
|
display_name="Image Composite Masked",
|
||||||
category="image",
|
category="image/compositing",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("destination"),
|
IO.Image.Input("destination"),
|
||||||
IO.Image.Input("source"),
|
IO.Image.Input("source"),
|
||||||
@ -112,7 +112,7 @@ class MaskToImage(IO.ComfyNode):
|
|||||||
node_id="MaskToImage",
|
node_id="MaskToImage",
|
||||||
search_aliases=["convert mask"],
|
search_aliases=["convert mask"],
|
||||||
display_name="Convert Mask to Image",
|
display_name="Convert Mask to Image",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
],
|
],
|
||||||
@ -134,7 +134,7 @@ class ImageToMask(IO.ComfyNode):
|
|||||||
node_id="ImageToMask",
|
node_id="ImageToMask",
|
||||||
search_aliases=["extract channel", "channel to mask"],
|
search_aliases=["extract channel", "channel to mask"],
|
||||||
display_name="Convert Image to Mask",
|
display_name="Convert Image to Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("image"),
|
IO.Image.Input("image"),
|
||||||
IO.Combo.Input("channel", options=["red", "green", "blue", "alpha"]),
|
IO.Combo.Input("channel", options=["red", "green", "blue", "alpha"]),
|
||||||
@ -157,7 +157,8 @@ class ImageColorToMask(IO.ComfyNode):
|
|||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="ImageColorToMask",
|
node_id="ImageColorToMask",
|
||||||
search_aliases=["color keying", "chroma key"],
|
search_aliases=["color keying", "chroma key"],
|
||||||
category="mask",
|
display_name="Convert Image Color to Mask",
|
||||||
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Image.Input("image"),
|
IO.Image.Input("image"),
|
||||||
IO.Int.Input("color", default=0, min=0, max=0xFFFFFF, step=1, display_mode=IO.NumberDisplay.number),
|
IO.Int.Input("color", default=0, min=0, max=0xFFFFFF, step=1, display_mode=IO.NumberDisplay.number),
|
||||||
@ -180,7 +181,8 @@ class SolidMask(IO.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="SolidMask",
|
node_id="SolidMask",
|
||||||
category="mask",
|
display_name="Create Solid Mask",
|
||||||
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Float.Input("value", default=1.0, min=0.0, max=1.0, step=0.01),
|
IO.Float.Input("value", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||||
IO.Int.Input("width", default=512, min=1, max=nodes.MAX_RESOLUTION, step=1),
|
IO.Int.Input("width", default=512, min=1, max=nodes.MAX_RESOLUTION, step=1),
|
||||||
@ -204,7 +206,7 @@ class InvertMask(IO.ComfyNode):
|
|||||||
node_id="InvertMask",
|
node_id="InvertMask",
|
||||||
search_aliases=["reverse mask", "flip mask"],
|
search_aliases=["reverse mask", "flip mask"],
|
||||||
display_name="Invert Mask",
|
display_name="Invert Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
],
|
],
|
||||||
@ -226,7 +228,7 @@ class CropMask(IO.ComfyNode):
|
|||||||
node_id="CropMask",
|
node_id="CropMask",
|
||||||
search_aliases=["cut mask", "extract mask region", "mask slice"],
|
search_aliases=["cut mask", "extract mask region", "mask slice"],
|
||||||
display_name="Crop Mask",
|
display_name="Crop Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
IO.Int.Input("x", default=0, min=0, max=nodes.MAX_RESOLUTION, step=1),
|
IO.Int.Input("x", default=0, min=0, max=nodes.MAX_RESOLUTION, step=1),
|
||||||
@ -253,7 +255,7 @@ class MaskComposite(IO.ComfyNode):
|
|||||||
node_id="MaskComposite",
|
node_id="MaskComposite",
|
||||||
search_aliases=["combine masks", "blend masks", "layer masks", "masks composition"],
|
search_aliases=["combine masks", "blend masks", "layer masks", "masks composition"],
|
||||||
display_name="Combine Masks",
|
display_name="Combine Masks",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("destination"),
|
IO.Mask.Input("destination"),
|
||||||
IO.Mask.Input("source"),
|
IO.Mask.Input("source"),
|
||||||
@ -304,7 +306,7 @@ class FeatherMask(IO.ComfyNode):
|
|||||||
node_id="FeatherMask",
|
node_id="FeatherMask",
|
||||||
search_aliases=["soft edge mask", "blur mask edges", "gradient mask edge"],
|
search_aliases=["soft edge mask", "blur mask edges", "gradient mask edge"],
|
||||||
display_name="Feather Mask",
|
display_name="Feather Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
IO.Int.Input("left", default=0, min=0, max=nodes.MAX_RESOLUTION, step=1),
|
IO.Int.Input("left", default=0, min=0, max=nodes.MAX_RESOLUTION, step=1),
|
||||||
@ -330,7 +332,7 @@ class FeatherMask(IO.ComfyNode):
|
|||||||
|
|
||||||
for x in range(right):
|
for x in range(right):
|
||||||
feather_rate = (x + 1) / right
|
feather_rate = (x + 1) / right
|
||||||
output[:, :, -x] *= feather_rate
|
output[:, :, -(x + 1)] *= feather_rate
|
||||||
|
|
||||||
for y in range(top):
|
for y in range(top):
|
||||||
feather_rate = (y + 1) / top
|
feather_rate = (y + 1) / top
|
||||||
@ -338,7 +340,7 @@ class FeatherMask(IO.ComfyNode):
|
|||||||
|
|
||||||
for y in range(bottom):
|
for y in range(bottom):
|
||||||
feather_rate = (y + 1) / bottom
|
feather_rate = (y + 1) / bottom
|
||||||
output[:, -y, :] *= feather_rate
|
output[:, -(y + 1), :] *= feather_rate
|
||||||
|
|
||||||
return IO.NodeOutput(output)
|
return IO.NodeOutput(output)
|
||||||
|
|
||||||
@ -352,7 +354,7 @@ class GrowMask(IO.ComfyNode):
|
|||||||
node_id="GrowMask",
|
node_id="GrowMask",
|
||||||
search_aliases=["expand mask", "shrink mask"],
|
search_aliases=["expand mask", "shrink mask"],
|
||||||
display_name="Grow Mask",
|
display_name="Grow Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
IO.Int.Input("expand", default=0, min=-nodes.MAX_RESOLUTION, max=nodes.MAX_RESOLUTION, step=1),
|
IO.Int.Input("expand", default=0, min=-nodes.MAX_RESOLUTION, max=nodes.MAX_RESOLUTION, step=1),
|
||||||
@ -388,7 +390,8 @@ class ThresholdMask(IO.ComfyNode):
|
|||||||
return IO.Schema(
|
return IO.Schema(
|
||||||
node_id="ThresholdMask",
|
node_id="ThresholdMask",
|
||||||
search_aliases=["binary mask"],
|
search_aliases=["binary mask"],
|
||||||
category="mask",
|
display_name="Threshold Mask",
|
||||||
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
IO.Float.Input("value", default=0.5, min=0.0, max=1.0, step=0.01),
|
IO.Float.Input("value", default=0.5, min=0.0, max=1.0, step=0.01),
|
||||||
@ -414,7 +417,7 @@ class MaskPreview(IO.ComfyNode):
|
|||||||
node_id="MaskPreview",
|
node_id="MaskPreview",
|
||||||
search_aliases=["show mask", "view mask", "inspect mask", "debug mask"],
|
search_aliases=["show mask", "view mask", "inspect mask", "debug mask"],
|
||||||
display_name="Preview Mask",
|
display_name="Preview Mask",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
description="Saves the input images to your ComfyUI output directory.",
|
description="Saves the input images to your ComfyUI output directory.",
|
||||||
inputs=[
|
inputs=[
|
||||||
IO.Mask.Input("mask"),
|
IO.Mask.Input("mask"),
|
||||||
|
|||||||
@ -276,8 +276,8 @@ class CLIPSave:
|
|||||||
for x in extra_pnginfo:
|
for x in extra_pnginfo:
|
||||||
metadata[x] = json.dumps(extra_pnginfo[x])
|
metadata[x] = json.dumps(extra_pnginfo[x])
|
||||||
|
|
||||||
comfy.model_management.load_models_gpu([clip.load_model()], force_patch_weights=True)
|
clip.load_model()
|
||||||
clip_sd = clip.get_sd()
|
clip_sd = clip.state_dict_for_saving()
|
||||||
|
|
||||||
for prefix in ["clip_l.", "clip_g.", "clip_h.", "t5xxl.", "pile_t5xl.", "mt5xl.", "umt5xxl.", "t5base.", "gemma2_2b.", "llama.", "hydit_clip.", ""]:
|
for prefix in ["clip_l.", "clip_g.", "clip_h.", "t5xxl.", "pile_t5xl.", "mt5xl.", "umt5xxl.", "t5base.", "gemma2_2b.", "llama.", "hydit_clip.", ""]:
|
||||||
k = list(filter(lambda a: a.startswith(prefix), clip_sd.keys()))
|
k = list(filter(lambda a: a.startswith(prefix), clip_sd.keys()))
|
||||||
|
|||||||
@ -13,8 +13,8 @@ class Morphology(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="Morphology",
|
node_id="Morphology",
|
||||||
search_aliases=["erode", "dilate"],
|
search_aliases=["erode", "dilate"],
|
||||||
display_name="ImageMorphology",
|
display_name="Apply Morphology",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
io.Combo.Input(
|
io.Combo.Input(
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class wanBlockSwap(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="wanBlockSwap",
|
node_id="wanBlockSwap",
|
||||||
category="",
|
category="",
|
||||||
description="NOP",
|
description="Intercept wanBlockSwap custom node that causes major instability and make it no-op.",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model"),
|
io.Model.Input("model"),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class NumberConvertNode(io.ComfyNode):
|
|||||||
def define_schema(cls) -> io.Schema:
|
def define_schema(cls) -> io.Schema:
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ComfyNumberConvert",
|
node_id="ComfyNumberConvert",
|
||||||
display_name="Number Convert",
|
display_name="Convert Number",
|
||||||
category="utils",
|
category="utils",
|
||||||
search_aliases=[
|
search_aliases=[
|
||||||
"int to float", "float to int", "number convert",
|
"int to float", "float to int", "number convert",
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class OptimalStepsScheduler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="OptimalStepsScheduler",
|
node_id="OptimalStepsScheduler",
|
||||||
category="sampling/custom_sampling/schedulers",
|
category="sampling/schedulers",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Combo.Input("model_type", options=["FLUX", "Wan", "Chroma"]),
|
io.Combo.Input("model_type", options=["FLUX", "Wan", "Chroma"]),
|
||||||
io.Int.Input("steps", default=20, min=3, max=1000),
|
io.Int.Input("steps", default=20, min=3, max=1000),
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class Blend(io.ComfyNode):
|
|||||||
node_id="ImageBlend",
|
node_id="ImageBlend",
|
||||||
search_aliases=["mix images"],
|
search_aliases=["mix images"],
|
||||||
display_name="Blend Images",
|
display_name="Blend Images",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
essentials_category="Image Tools",
|
essentials_category="Image Tools",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image1"),
|
io.Image.Input("image1"),
|
||||||
@ -80,8 +80,8 @@ class Blur(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ImageBlur",
|
node_id="ImageBlur",
|
||||||
display_name="Image Blur",
|
display_name="Blur Image",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
io.Int.Input("blur_radius", default=1, min=1, max=31, step=1),
|
io.Int.Input("blur_radius", default=1, min=1, max=31, step=1),
|
||||||
@ -117,7 +117,7 @@ class Quantize(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ImageQuantize",
|
node_id="ImageQuantize",
|
||||||
display_name="Quantize Image",
|
display_name="Quantize Image",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
io.Int.Input("colors", default=256, min=1, max=256, step=1),
|
io.Int.Input("colors", default=256, min=1, max=256, step=1),
|
||||||
@ -183,7 +183,7 @@ class Sharpen(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ImageSharpen",
|
node_id="ImageSharpen",
|
||||||
display_name="Sharpen Image",
|
display_name="Sharpen Image",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image"),
|
io.Image.Input("image"),
|
||||||
io.Int.Input("sharpen_radius", default=1, min=1, max=31, step=1, advanced=True),
|
io.Int.Input("sharpen_radius", default=1, min=1, max=31, step=1, advanced=True),
|
||||||
@ -568,7 +568,7 @@ def batch_latents(latents: list[dict[str, torch.Tensor]]) -> dict[str, torch.Ten
|
|||||||
class BatchImagesNode(io.ComfyNode):
|
class BatchImagesNode(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
autogrow_template = io.Autogrow.TemplatePrefix(io.Image.Input("image"), prefix="image", min=2, max=50)
|
autogrow_template = io.Autogrow.TemplatePrefix(io.Image.Input("image"), prefix="image", min=1, max=50)
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BatchImagesNode",
|
node_id="BatchImagesNode",
|
||||||
display_name="Batch Images",
|
display_name="Batch Images",
|
||||||
@ -590,12 +590,12 @@ class BatchImagesNode(io.ComfyNode):
|
|||||||
class BatchMasksNode(io.ComfyNode):
|
class BatchMasksNode(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
autogrow_template = io.Autogrow.TemplatePrefix(io.Mask.Input("mask"), prefix="mask", min=2, max=50)
|
autogrow_template = io.Autogrow.TemplatePrefix(io.Mask.Input("mask"), prefix="mask", min=1, max=50)
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BatchMasksNode",
|
node_id="BatchMasksNode",
|
||||||
search_aliases=["combine masks", "stack masks", "merge masks"],
|
search_aliases=["combine masks", "stack masks", "merge masks"],
|
||||||
display_name="Batch Masks",
|
display_name="Batch Masks",
|
||||||
category="mask",
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Autogrow.Input("masks", template=autogrow_template)
|
io.Autogrow.Input("masks", template=autogrow_template)
|
||||||
],
|
],
|
||||||
@ -611,7 +611,7 @@ class BatchMasksNode(io.ComfyNode):
|
|||||||
class BatchLatentsNode(io.ComfyNode):
|
class BatchLatentsNode(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
autogrow_template = io.Autogrow.TemplatePrefix(io.Latent.Input("latent"), prefix="latent", min=2, max=50)
|
autogrow_template = io.Autogrow.TemplatePrefix(io.Latent.Input("latent"), prefix="latent", min=1, max=50)
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="BatchLatentsNode",
|
node_id="BatchLatentsNode",
|
||||||
search_aliases=["combine latents", "stack latents", "merge latents"],
|
search_aliases=["combine latents", "stack latents", "merge latents"],
|
||||||
@ -670,8 +670,8 @@ class ColorTransfer(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="ColorTransfer",
|
node_id="ColorTransfer",
|
||||||
display_name="Color Transfer",
|
display_name="Transfer Color",
|
||||||
category="image/postprocessing",
|
category="image/filters",
|
||||||
description="Match the colors of one image to another using various algorithms.",
|
description="Match the colors of one image to another using various algorithms.",
|
||||||
search_aliases=["color match", "color grading", "color correction", "match colors", "color transform", "mkl", "reinhard", "histogram"],
|
search_aliases=["color match", "color grading", "color correction", "match colors", "color transform", "mkl", "reinhard", "histogram"],
|
||||||
inputs=[
|
inputs=[
|
||||||
|
|||||||
@ -15,7 +15,7 @@ class RTDETR_detect(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="RTDETR_detect",
|
node_id="RTDETR_detect",
|
||||||
display_name="RT-DETR Detect",
|
display_name="RT-DETR Detect",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
search_aliases=["bbox", "bounding box", "object detection", "coco"],
|
search_aliases=["bbox", "bounding box", "object detection", "coco"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model", display_name="model"),
|
io.Model.Input("model", display_name="model"),
|
||||||
@ -71,7 +71,7 @@ class DrawBBoxes(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="DrawBBoxes",
|
node_id="DrawBBoxes",
|
||||||
display_name="Draw BBoxes",
|
display_name="Draw BBoxes",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
search_aliases=["bbox", "bounding box", "object detection", "rt_detr", "visualize detections", "coco"],
|
search_aliases=["bbox", "bounding box", "object detection", "rt_detr", "visualize detections", "coco"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("image", optional=True),
|
io.Image.Input("image", optional=True),
|
||||||
|
|||||||
@ -93,7 +93,7 @@ class SAM3_Detect(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SAM3_Detect",
|
node_id="SAM3_Detect",
|
||||||
display_name="SAM3 Detect",
|
display_name="SAM3 Detect",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
search_aliases=["sam3", "segment anything", "open vocabulary", "text detection", "segment"],
|
search_aliases=["sam3", "segment anything", "open vocabulary", "text detection", "segment"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Model.Input("model", display_name="model"),
|
io.Model.Input("model", display_name="model"),
|
||||||
@ -265,7 +265,7 @@ class SAM3_VideoTrack(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SAM3_VideoTrack",
|
node_id="SAM3_VideoTrack",
|
||||||
display_name="SAM3 Video Track",
|
display_name="SAM3 Video Track",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
search_aliases=["sam3", "video", "track", "propagate"],
|
search_aliases=["sam3", "video", "track", "propagate"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Image.Input("images", display_name="images", tooltip="Video frames as batched images"),
|
io.Image.Input("images", display_name="images", tooltip="Video frames as batched images"),
|
||||||
@ -320,7 +320,7 @@ class SAM3_TrackPreview(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SAM3_TrackPreview",
|
node_id="SAM3_TrackPreview",
|
||||||
display_name="SAM3 Track Preview",
|
display_name="SAM3 Track Preview",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
inputs=[
|
inputs=[
|
||||||
SAM3TrackData.Input("track_data", display_name="track_data"),
|
SAM3TrackData.Input("track_data", display_name="track_data"),
|
||||||
io.Image.Input("images", display_name="images", optional=True),
|
io.Image.Input("images", display_name="images", optional=True),
|
||||||
@ -478,7 +478,7 @@ class SAM3_TrackToMask(io.ComfyNode):
|
|||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SAM3_TrackToMask",
|
node_id="SAM3_TrackToMask",
|
||||||
display_name="SAM3 Track to Mask",
|
display_name="SAM3 Track to Mask",
|
||||||
category="detection",
|
category="image/detection",
|
||||||
inputs=[
|
inputs=[
|
||||||
SAM3TrackData.Input("track_data", display_name="track_data"),
|
SAM3TrackData.Input("track_data", display_name="track_data"),
|
||||||
io.String.Input("object_indices", display_name="object_indices", default="",
|
io.String.Input("object_indices", display_name="object_indices", default="",
|
||||||
|
|||||||
@ -353,7 +353,8 @@ class SDPoseDrawKeypoints(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SDPoseDrawKeypoints",
|
node_id="SDPoseDrawKeypoints",
|
||||||
category="image/preprocessors",
|
display_name="SDPose Draw Keypoints",
|
||||||
|
category="image/detection",
|
||||||
search_aliases=["openpose", "pose detection", "preprocessor", "keypoints", "pose"],
|
search_aliases=["openpose", "pose detection", "preprocessor", "keypoints", "pose"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Custom("POSE_KEYPOINT").Input("keypoints"),
|
io.Custom("POSE_KEYPOINT").Input("keypoints"),
|
||||||
@ -421,7 +422,8 @@ class SDPoseKeypointExtractor(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SDPoseKeypointExtractor",
|
node_id="SDPoseKeypointExtractor",
|
||||||
category="image/preprocessors",
|
display_name="SDPose Keypoint Extractor",
|
||||||
|
category="image/detection",
|
||||||
search_aliases=["openpose", "pose detection", "preprocessor", "keypoints", "sdpose"],
|
search_aliases=["openpose", "pose detection", "preprocessor", "keypoints", "sdpose"],
|
||||||
description="Extract pose keypoints from images using the SDPose model: https://huggingface.co/Comfy-Org/SDPose/tree/main/checkpoints",
|
description="Extract pose keypoints from images using the SDPose model: https://huggingface.co/Comfy-Org/SDPose/tree/main/checkpoints",
|
||||||
inputs=[
|
inputs=[
|
||||||
@ -595,7 +597,8 @@ class SDPoseFaceBBoxes(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="SDPoseFaceBBoxes",
|
node_id="SDPoseFaceBBoxes",
|
||||||
category="image/preprocessors",
|
display_name="SDPose Face Bounding Boxes",
|
||||||
|
category="image/detection",
|
||||||
search_aliases=["face bbox", "face bounding box", "pose", "keypoints"],
|
search_aliases=["face bbox", "face bounding box", "pose", "keypoints"],
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Custom("POSE_KEYPOINT").Input("keypoints"),
|
io.Custom("POSE_KEYPOINT").Input("keypoints"),
|
||||||
@ -652,7 +655,8 @@ class CropByBBoxes(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="CropByBBoxes",
|
node_id="CropByBBoxes",
|
||||||
category="image/preprocessors",
|
display_name="Crop By Bounding Boxes",
|
||||||
|
category="image/transform",
|
||||||
search_aliases=["crop", "face crop", "bbox crop", "pose", "bounding box"],
|
search_aliases=["crop", "face crop", "bbox crop", "pose", "bounding box"],
|
||||||
description="Crop and resize regions from the input image batch based on provided bounding boxes.",
|
description="Crop and resize regions from the input image batch based on provided bounding boxes.",
|
||||||
inputs=[
|
inputs=[
|
||||||
|
|||||||
@ -1,10 +1,41 @@
|
|||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
import string
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from comfy_api.latest import ComfyExtension, io
|
from comfy_api.latest import ComfyExtension, io
|
||||||
|
|
||||||
|
|
||||||
|
class StringFormat(io.ComfyNode):
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls) -> io.Schema:
|
||||||
|
autogrow = io.Autogrow.TemplateNames(
|
||||||
|
input=io.AnyType.Input("value"),
|
||||||
|
names=list(string.ascii_lowercase),
|
||||||
|
min=0,
|
||||||
|
)
|
||||||
|
return io.Schema(
|
||||||
|
node_id="StringFormat",
|
||||||
|
display_name="Format Text",
|
||||||
|
category="text",
|
||||||
|
search_aliases=["string", "format"],
|
||||||
|
description="Same as Python's string format method. Supports all of Python's format options and features.",
|
||||||
|
inputs=[
|
||||||
|
io.Autogrow.Input("values", template=autogrow),
|
||||||
|
io.String.Input("f_string", default="{a}", multiline=True),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(
|
||||||
|
cls, values: io.Autogrow.Type, f_string: str
|
||||||
|
) -> io.NodeOutput:
|
||||||
|
return io.NodeOutput(f_string.format(**values))
|
||||||
|
|
||||||
|
|
||||||
class StringConcatenate(io.ComfyNode):
|
class StringConcatenate(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
@ -413,6 +444,7 @@ class StringExtension(ComfyExtension):
|
|||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
||||||
return [
|
return [
|
||||||
|
StringFormat,
|
||||||
StringConcatenate,
|
StringConcatenate,
|
||||||
StringSubstring,
|
StringSubstring,
|
||||||
StringLength,
|
StringLength,
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class VideoLinearCFGGuidance:
|
|||||||
RETURN_TYPES = ("MODEL",)
|
RETURN_TYPES = ("MODEL",)
|
||||||
FUNCTION = "patch"
|
FUNCTION = "patch"
|
||||||
|
|
||||||
CATEGORY = "sampling/video_models"
|
CATEGORY = "sampling/guiders"
|
||||||
|
|
||||||
def patch(self, model, min_cfg):
|
def patch(self, model, min_cfg):
|
||||||
def linear_cfg(args):
|
def linear_cfg(args):
|
||||||
@ -89,7 +89,7 @@ class VideoTriangleCFGGuidance:
|
|||||||
RETURN_TYPES = ("MODEL",)
|
RETURN_TYPES = ("MODEL",)
|
||||||
FUNCTION = "patch"
|
FUNCTION = "patch"
|
||||||
|
|
||||||
CATEGORY = "sampling/video_models"
|
CATEGORY = "sampling/guiders"
|
||||||
|
|
||||||
def patch(self, model, min_cfg):
|
def patch(self, model, min_cfg):
|
||||||
def linear_cfg(args):
|
def linear_cfg(args):
|
||||||
@ -157,5 +157,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
"ImageOnlyCheckpointLoader": "Image Only Checkpoint Loader (img2vid model)",
|
"ImageOnlyCheckpointLoader": "Load Checkpoint Image Only (img2vid model)",
|
||||||
|
"VideoLinearCFGGuidance": "Video Linear CFG Guidance",
|
||||||
|
"VideoTriangleCFGGuidance": "Video Triangle CFG Guidance",
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,8 @@ class VOIDQuadmaskPreprocess(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="VOIDQuadmaskPreprocess",
|
node_id="VOIDQuadmaskPreprocess",
|
||||||
category="mask/video",
|
display_name="VOID Quadmask Preprocessor",
|
||||||
|
category="image/mask",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Mask.Input("mask"),
|
io.Mask.Input("mask"),
|
||||||
io.Int.Input("dilate_width", default=0, min=0, max=50, step=1,
|
io.Int.Input("dilate_width", default=0, min=0, max=50, step=1,
|
||||||
@ -392,7 +393,7 @@ class VOIDWarpedNoiseSource(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="VOIDWarpedNoiseSource",
|
node_id="VOIDWarpedNoiseSource",
|
||||||
category="sampling/custom_sampling/noise",
|
category="sampling/noise",
|
||||||
inputs=[
|
inputs=[
|
||||||
io.Latent.Input("warped_noise",
|
io.Latent.Input("warped_noise",
|
||||||
tooltip="Warped noise latent from VOIDWarpedNoise"),
|
tooltip="Warped noise latent from VOIDWarpedNoise"),
|
||||||
@ -454,7 +455,7 @@ class VOIDSampler(io.ComfyNode):
|
|||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
return io.Schema(
|
return io.Schema(
|
||||||
node_id="VOIDSampler",
|
node_id="VOIDSampler",
|
||||||
category="sampling/custom_sampling/samplers",
|
category="sampling/samplers",
|
||||||
inputs=[],
|
inputs=[],
|
||||||
outputs=[io.Sampler.Output()],
|
outputs=[io.Sampler.Output()],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -626,7 +626,7 @@ async def execute(server, dynprompt, caches, current_item, extra_data, executed,
|
|||||||
|
|
||||||
if comfy.model_management.is_oom(ex):
|
if comfy.model_management.is_oom(ex):
|
||||||
tips = "This error means you ran out of memory on your GPU.\n\nTIPS: If the workflow worked before you might have accidentally set the batch_size to a large number."
|
tips = "This error means you ran out of memory on your GPU.\n\nTIPS: If the workflow worked before you might have accidentally set the batch_size to a large number."
|
||||||
logging.info("Memory summary: {}".format(comfy.model_management.debug_memory_summary()))
|
logging.info("Memory summary:\n{}".format(comfy.model_management.debug_memory_summary()))
|
||||||
logging.error("Got an OOM, unloading all loaded models.")
|
logging.error("Got an OOM, unloading all loaded models.")
|
||||||
comfy.model_management.unload_all_models()
|
comfy.model_management.unload_all_models()
|
||||||
elif isinstance(ex, RuntimeError) and ("mat1 and mat2 shapes" in str(ex)) and "Sampler" in class_type:
|
elif isinstance(ex, RuntimeError) and ("mat1 and mat2 shapes" in str(ex)) and "Sampler" in class_type:
|
||||||
|
|||||||
8
nodes.py
8
nodes.py
@ -691,7 +691,7 @@ class LoraLoader:
|
|||||||
FUNCTION = "load_lora"
|
FUNCTION = "load_lora"
|
||||||
|
|
||||||
CATEGORY = "loaders"
|
CATEGORY = "loaders"
|
||||||
DESCRIPTION = "LoRAs are used to modify diffusion and CLIP models, altering the way in which latents are denoised such as applying styles. Multiple LoRA nodes can be linked together."
|
DESCRIPTION = "This LoRA loader is used to modify both diffusion and CLIP models, altering the way in which latents are denoised such as applying styles. Multiple LoRA nodes can be linked together."
|
||||||
SEARCH_ALIASES = ["lora", "load lora", "apply lora", "lora loader", "lora model"]
|
SEARCH_ALIASES = ["lora", "load lora", "apply lora", "lora loader", "lora model"]
|
||||||
|
|
||||||
def load_lora(self, model, clip, lora_name, strength_model, strength_clip):
|
def load_lora(self, model, clip, lora_name, strength_model, strength_clip):
|
||||||
@ -723,6 +723,7 @@ class LoraLoaderModelOnly(LoraLoader):
|
|||||||
"strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),
|
"strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),
|
||||||
}}
|
}}
|
||||||
RETURN_TYPES = ("MODEL",)
|
RETURN_TYPES = ("MODEL",)
|
||||||
|
DESCRIPTION = "This LoRAs loader is used to modify the diffusion model, altering the way in which latents are denoised such as applying styles. Multiple LoRA nodes can be linked together."
|
||||||
FUNCTION = "load_lora_model_only"
|
FUNCTION = "load_lora_model_only"
|
||||||
|
|
||||||
def load_lora_model_only(self, model, lora_name, strength_model):
|
def load_lora_model_only(self, model, lora_name, strength_model):
|
||||||
@ -1524,7 +1525,7 @@ class SetLatentNoiseMask:
|
|||||||
|
|
||||||
def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=1.0, disable_noise=False, start_step=None, last_step=None, force_full_denoise=False):
|
def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent, denoise=1.0, disable_noise=False, start_step=None, last_step=None, force_full_denoise=False):
|
||||||
latent_image = latent["samples"]
|
latent_image = latent["samples"]
|
||||||
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image, latent.get("downscale_ratio_spacial", None))
|
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image, latent.get("downscale_ratio_spacial", None), latent.get("downscale_ratio_temporal", None))
|
||||||
|
|
||||||
if disable_noise:
|
if disable_noise:
|
||||||
noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu")
|
noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu")
|
||||||
@ -1547,6 +1548,7 @@ def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive,
|
|||||||
force_full_denoise=force_full_denoise, noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=seed)
|
force_full_denoise=force_full_denoise, noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=seed)
|
||||||
out = latent.copy()
|
out = latent.copy()
|
||||||
out.pop("downscale_ratio_spacial", None)
|
out.pop("downscale_ratio_spacial", None)
|
||||||
|
out.pop("downscale_ratio_temporal", None)
|
||||||
out["samples"] = samples
|
out["samples"] = samples
|
||||||
return (out, )
|
return (out, )
|
||||||
|
|
||||||
@ -1779,7 +1781,7 @@ class LoadImageMask(LoadImage):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "mask"
|
CATEGORY = "image"
|
||||||
RETURN_TYPES = ("MASK",)
|
RETURN_TYPES = ("MASK",)
|
||||||
FUNCTION = "load_image_mask"
|
FUNCTION = "load_image_mask"
|
||||||
|
|
||||||
|
|||||||
33
openapi.yaml
33
openapi.yaml
@ -485,8 +485,15 @@ paths:
|
|||||||
post:
|
post:
|
||||||
operationId: uploadMask
|
operationId: uploadMask
|
||||||
tags: [upload]
|
tags: [upload]
|
||||||
summary: Upload a mask image
|
deprecated: true
|
||||||
description: Uploads a mask image associated with a previously-uploaded reference image.
|
summary: Upload a mask image (deprecated)
|
||||||
|
description: |
|
||||||
|
Deprecated. Clients should composite the mask onto the source image
|
||||||
|
client-side and upload the resulting image via POST /api/upload/image
|
||||||
|
instead. This endpoint will continue to function for older clients,
|
||||||
|
but will not receive new features.
|
||||||
|
|
||||||
|
Uploads a mask image associated with a previously-uploaded reference image.
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@ -4153,6 +4160,10 @@ paths:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
description: Display name for the API key
|
description: Display name for the API key
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: User-provided description of the key's purpose
|
||||||
|
maxLength: 5000
|
||||||
responses:
|
responses:
|
||||||
"201":
|
"201":
|
||||||
description: API key created
|
description: API key created
|
||||||
@ -6344,14 +6355,6 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
description: Size of the asset in bytes
|
description: Size of the asset in bytes
|
||||||
width:
|
|
||||||
type: integer
|
|
||||||
nullable: true
|
|
||||||
description: "Original image width in pixels. Null for non-image assets or assets ingested before dimension extraction."
|
|
||||||
height:
|
|
||||||
type: integer
|
|
||||||
nullable: true
|
|
||||||
description: "Original image height in pixels. Null for non-image assets or assets ingested before dimension extraction."
|
|
||||||
mime_type:
|
mime_type:
|
||||||
type: string
|
type: string
|
||||||
description: MIME type of the asset
|
description: MIME type of the asset
|
||||||
@ -7678,11 +7681,16 @@ components:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- name
|
- name
|
||||||
|
- description
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
maxLength: 5000
|
||||||
|
description: User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create.
|
||||||
prefix:
|
prefix:
|
||||||
type: string
|
type: string
|
||||||
description: First few characters of the key for identification
|
description: First few characters of the key for identification
|
||||||
@ -7703,12 +7711,17 @@ components:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- name
|
- name
|
||||||
|
- description
|
||||||
- key
|
- key
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
maxLength: 5000
|
||||||
|
description: User-provided description of the key's purpose. Always present in responses; empty string when no description was supplied on create.
|
||||||
key:
|
key:
|
||||||
type: string
|
type: string
|
||||||
description: Full API key value (only returned on creation)
|
description: Full API key value (only returned on creation)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user