From 17b14110abc6a136735617cd0a0c2664607e76d1 Mon Sep 17 00:00:00 2001 From: doctorpangloss <@hiddenswitch.com> Date: Mon, 21 Apr 2025 14:11:56 -0700 Subject: [PATCH] Update to latest ComfyUI --- comfy/app/frontend_management.py | 8 +++-- comfy/cli_args.py | 2 +- comfy/client/embedded_comfy_client.py | 3 +- comfy/clip_vision.py | 4 ++- comfy/cmd/execution.py | 6 ++-- comfy/model_downloader.py | 11 +++++++ comfy/node_helpers.py | 1 + comfy/nodes/base_nodes.py | 5 +-- comfy/sampler_helpers.py | 5 ++- comfy/text_encoders/aura_t5.py | 1 + comfy/text_encoders/hydit.py | 1 + comfy_extras/{ => nodes}/nodes_fresca.py | 0 comfy_extras/{ => nodes}/nodes_hidream.py | 32 +++++++++++-------- comfy_extras/nodes/nodes_hunyuan3d.py | 2 ++ .../{ => nodes}/nodes_optimalsteps.py | 0 15 files changed, 54 insertions(+), 27 deletions(-) rename comfy_extras/{ => nodes}/nodes_fresca.py (100%) rename comfy_extras/{ => nodes}/nodes_hidream.py (61%) rename comfy_extras/{ => nodes}/nodes_optimalsteps.py (100%) diff --git a/comfy/app/frontend_management.py b/comfy/app/frontend_management.py index 1971a0db4..717b92dff 100644 --- a/comfy/app/frontend_management.py +++ b/comfy/app/frontend_management.py @@ -20,9 +20,15 @@ from ..cmd.folder_paths import add_model_folder_path # pylint: disable=import-e REQUEST_TIMEOUT = 10 # seconds + def check_frontend_version(): return None + +def frontend_install_warning_message() -> str: + return "" + + class Asset(TypedDict): url: str @@ -141,8 +147,6 @@ class FrontendManager: comfyui-workflow-templates is not installed. -{frontend_install_warning_message()} - ********** ERROR *********** """.strip() ) diff --git a/comfy/cli_args.py b/comfy/cli_args.py index 59a5c66a3..bb7ed1e30 100644 --- a/comfy/cli_args.py +++ b/comfy/cli_args.py @@ -131,7 +131,7 @@ def _create_parser() -> EnhancedConfigArgParser: parser.add_argument("--deterministic", action="store_true", help="Make pytorch use slower deterministic algorithms when it can. Note that this might not make images deterministic in all cases.") - parser.add_argument("--fast", nargs="*", type=PerformanceFeature, help="Enable some untested and potentially quality deteriorating optimizations. Pass a list specific optimizations if you only want to enable specific ones. Current valid optimizations: fp16_accumulation fp8_matrix_mult cublas_ops") + parser.add_argument("--fast", nargs="*", type=PerformanceFeature, help="Enable some untested and potentially quality deteriorating optimizations. Pass a list specific optimizations if you only want to enable specific ones. Current valid optimizations: fp16_accumulation fp8_matrix_mult cublas_ops", default=set()) parser.add_argument("--dont-print-server", action="store_true", help="Don't print server output.") parser.add_argument("--quick-test-for-ci", action="store_true", help="Quick test for CI. Raises an error if nodes cannot be imported,") diff --git a/comfy/client/embedded_comfy_client.py b/comfy/client/embedded_comfy_client.py index 4824e76a6..3994c3e58 100644 --- a/comfy/client/embedded_comfy_client.py +++ b/comfy/client/embedded_comfy_client.py @@ -71,7 +71,8 @@ async def __execute_prompt( args.update(configuration) with tracer.start_as_current_span("Initialize Prompt Executor", context=span_context): - prompt_executor = PromptExecutor(progress_handler, lru_size=configuration.cache_lru if configuration is not None else 0) + # todo: deal with new caching features + prompt_executor = PromptExecutor(progress_handler) prompt_executor.raise_exceptions = True _prompt_executor.executor = prompt_executor diff --git a/comfy/clip_vision.py b/comfy/clip_vision.py index 875d93d0a..bb79485e2 100644 --- a/comfy/clip_vision.py +++ b/comfy/clip_vision.py @@ -1,5 +1,6 @@ import json import logging +from typing import Optional import torch @@ -116,7 +117,8 @@ def convert_to_transformers(sd, prefix): return sd -def load_clipvision_from_sd(sd, prefix="", convert_keys=False): +def load_clipvision_from_sd(sd, prefix="", convert_keys=False) -> Optional[ClipVisionModel]: + json_config: dict = {} if convert_keys: sd = convert_to_transformers(sd, prefix) if "vision_model.encoder.layers.47.layer_norm1.weight" in sd: diff --git a/comfy/cmd/execution.py b/comfy/cmd/execution.py index 0a10b96b5..61e7ffc7e 100644 --- a/comfy/cmd/execution.py +++ b/comfy/cmd/execution.py @@ -745,7 +745,7 @@ def validate_inputs(prompt, item, validated: typing.Dict[str, ValidateInputsTupl r = get_nodes().NODE_CLASS_MAPPINGS[o_class_type].RETURN_TYPES received_type = r[val[1]] received_types[x] = received_type - any_enum = received_type == [] and (isinstance(type_input, list) or isinstance(type_input, tuple)) + any_enum = received_type == [] and (isinstance(input_type, list) or isinstance(input_type, tuple)) if 'input_types' not in validate_function_inputs and not validate_node_input(received_type, input_type) and not any_enum: details = f"{x}, {received_type} != {input_type}" @@ -857,8 +857,8 @@ def validate_inputs(prompt, item, validated: typing.Dict[str, ValidateInputsTupl if "\\" in val: # try to normalize paths for comparison purposes val = canonicalize_path(val) - if all(isinstance(item, (str, PathLike)) for item in type_input): - type_input = [canonicalize_path(item) for item in type_input] + if all(isinstance(item, (str, PathLike)) for item in combo_options): + combo_options = [canonicalize_path(item) for item in combo_options] if val not in combo_options: input_config = info list_info = "" diff --git a/comfy/model_downloader.py b/comfy/model_downloader.py index f43444a4d..b1afbfec2 100644 --- a/comfy/model_downloader.py +++ b/comfy/model_downloader.py @@ -36,6 +36,10 @@ _session = Session() _hf_fs = HfFileSystem() +def get_filename_list(folder_name: str) -> list[str]: + return get_filename_list_with_downloadable(folder_name) + + def get_filename_list_with_downloadable(folder_name: str, known_files: Optional[List[Downloadable] | KnownDownloadables] = None) -> List[str]: if known_files is None: known_files = _get_known_models_for_folder_name(folder_name) @@ -45,6 +49,13 @@ def get_filename_list_with_downloadable(folder_name: str, known_files: Optional[ return list(map(canonicalize_path, sorted(list(existing | downloadable)))) +def get_full_path_or_raise(folder_name: str, filename: str) -> str: + res = get_or_download(folder_name, filename) + if res is None: + raise FileNotFoundError(f"{folder_name} does not contain {filename}") + return res + + def get_or_download(folder_name: str, filename: str, known_files: Optional[List[Downloadable] | KnownDownloadables] = None) -> Optional[str]: if known_files is None: known_files = _get_known_models_for_folder_name(folder_name) diff --git a/comfy/node_helpers.py b/comfy/node_helpers.py index b341a45a0..7ed32913a 100644 --- a/comfy/node_helpers.py +++ b/comfy/node_helpers.py @@ -102,6 +102,7 @@ def string_to_torch_dtype(string): def image_alpha_fix(destination, source): + import torch if destination.shape[-1] < source.shape[-1]: source = source[..., :destination.shape[-1]] elif destination.shape[-1] > source.shape[-1]: diff --git a/comfy/nodes/base_nodes.py b/comfy/nodes/base_nodes.py index 1bec4cfd0..11a1df5f6 100644 --- a/comfy/nodes/base_nodes.py +++ b/comfy/nodes/base_nodes.py @@ -1714,8 +1714,9 @@ class LoadImage: mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0 mask = 1. - torch.from_numpy(mask) elif i.mode == 'P' and 'transparency' in i.info: - mask = np.array(i.convert('RGBA').getchannel('A')).astype(np.float32) / 255.0 - mask = 1. - torch.from_numpy(mask)else: + mask = np.array(i.convert('RGBA').getchannel('A')).astype(np.float32) / 255.0 + mask = 1. - torch.from_numpy(mask) + else: mask = torch.zeros((64,64), dtype=torch.float32, device="cpu") output_images.append(image) output_masks.append(mask.unsqueeze(0)) diff --git a/comfy/sampler_helpers.py b/comfy/sampler_helpers.py index 661ed29c2..c0c4baf5f 100644 --- a/comfy/sampler_helpers.py +++ b/comfy/sampler_helpers.py @@ -1,6 +1,5 @@ import uuid -from . import conds from . import model_management from . import patcher_extension from . import utils @@ -113,9 +112,9 @@ def cleanup_additional_models(models): def prepare_sampling(model: ModelPatcher, noise_shape, conds, model_options=None): - executor = comfy.patcher_extension.WrapperExecutor.new_executor( + executor = patcher_extension.WrapperExecutor.new_executor( _prepare_sampling, - comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.PREPARE_SAMPLING, model_options, is_model_options=True) + patcher_extension.get_all_wrappers(patcher_extension.WrappersMP.PREPARE_SAMPLING, model_options, is_model_options=True) ) return executor.execute(model, noise_shape, conds, model_options=model_options) diff --git a/comfy/text_encoders/aura_t5.py b/comfy/text_encoders/aura_t5.py index 51463a9c1..8fe97581b 100644 --- a/comfy/text_encoders/aura_t5.py +++ b/comfy/text_encoders/aura_t5.py @@ -16,6 +16,7 @@ class PT5XlModel(sd1_clip.SDClipModel): class PT5XlTokenizer(sd1_clip.SDTokenizer): def __init__(self, embedding_directory=None, **kwargs): tokenizer_path = resources.files("comfy.text_encoders.t5_pile_tokenizer") / "tokenizer.model" + tokenizer_data = kwargs.pop("tokenizer_data", {}) super().__init__(tokenizer_path, pad_with_end=False, embedding_size=2048, embedding_key='pile_t5xl', tokenizer_class=SPieceTokenizer, has_start_token=False, pad_to_max_length=False, max_length=99999999, min_length=256, pad_token=1, tokenizer_data=tokenizer_data) diff --git a/comfy/text_encoders/hydit.py b/comfy/text_encoders/hydit.py index b944afa7b..7b3db6f29 100644 --- a/comfy/text_encoders/hydit.py +++ b/comfy/text_encoders/hydit.py @@ -21,6 +21,7 @@ class HyditBertModel(sd1_clip.SDClipModel): class HyditBertTokenizer(sd1_clip.SDTokenizer): def __init__(self, **kwargs): tokenizer_path = get_package_as_path(f"{__package__}.hydit_clip_tokenizer") + tokenizer_data = kwargs.pop("tokenizer_data", {}) super().__init__(tokenizer_path, pad_with_end=False, embedding_size=1024, embedding_key='chinese_roberta', tokenizer_class=BertTokenizer, pad_to_max_length=False, max_length=512, min_length=77, tokenizer_data=tokenizer_data) diff --git a/comfy_extras/nodes_fresca.py b/comfy_extras/nodes/nodes_fresca.py similarity index 100% rename from comfy_extras/nodes_fresca.py rename to comfy_extras/nodes/nodes_fresca.py diff --git a/comfy_extras/nodes_hidream.py b/comfy_extras/nodes/nodes_hidream.py similarity index 61% rename from comfy_extras/nodes_hidream.py rename to comfy_extras/nodes/nodes_hidream.py index dfb98597b..9d29cb6c9 100644 --- a/comfy_extras/nodes_hidream.py +++ b/comfy_extras/nodes/nodes_hidream.py @@ -1,16 +1,18 @@ -import folder_paths -import comfy.sd import comfy.model_management +import comfy.sd +from comfy.cmd import folder_paths +from comfy import model_downloader class QuadrupleCLIPLoader: @classmethod def INPUT_TYPES(s): - return {"required": { "clip_name1": (folder_paths.get_filename_list("text_encoders"), ), - "clip_name2": (folder_paths.get_filename_list("text_encoders"), ), - "clip_name3": (folder_paths.get_filename_list("text_encoders"), ), - "clip_name4": (folder_paths.get_filename_list("text_encoders"), ) + return {"required": {"clip_name1": (model_downloader.get_filename_list("text_encoders"),), + "clip_name2": (model_downloader.get_filename_list("text_encoders"),), + "clip_name3": (model_downloader.get_filename_list("text_encoders"),), + "clip_name4": (model_downloader.get_filename_list("text_encoders"),) }} + RETURN_TYPES = ("CLIP",) FUNCTION = "load_clip" @@ -19,35 +21,37 @@ class QuadrupleCLIPLoader: DESCRIPTION = "[Recipes]\n\nhidream: long clip-l, long clip-g, t5xxl, llama_8b_3.1_instruct" def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4): - clip_path1 = folder_paths.get_full_path_or_raise("text_encoders", clip_name1) - clip_path2 = folder_paths.get_full_path_or_raise("text_encoders", clip_name2) - clip_path3 = folder_paths.get_full_path_or_raise("text_encoders", clip_name3) - clip_path4 = folder_paths.get_full_path_or_raise("text_encoders", clip_name4) + clip_path1 = model_downloader.get_full_path_or_raise("text_encoders", clip_name1) + clip_path2 = model_downloader.get_full_path_or_raise("text_encoders", clip_name2) + clip_path3 = model_downloader.get_full_path_or_raise("text_encoders", clip_name3) + clip_path4 = model_downloader.get_full_path_or_raise("text_encoders", clip_name4) clip = comfy.sd.load_clip(ckpt_paths=[clip_path1, clip_path2, clip_path3, clip_path4], embedding_directory=folder_paths.get_folder_paths("embeddings")) return (clip,) + class CLIPTextEncodeHiDream: @classmethod def INPUT_TYPES(s): return {"required": { - "clip": ("CLIP", ), + "clip": ("CLIP",), "clip_l": ("STRING", {"multiline": True, "dynamicPrompts": True}), "clip_g": ("STRING", {"multiline": True, "dynamicPrompts": True}), "t5xxl": ("STRING", {"multiline": True, "dynamicPrompts": True}), "llama": ("STRING", {"multiline": True, "dynamicPrompts": True}) - }} + }} + RETURN_TYPES = ("CONDITIONING",) FUNCTION = "encode" CATEGORY = "advanced/conditioning" def encode(self, clip, clip_l, clip_g, t5xxl, llama): - tokens = clip.tokenize(clip_g) tokens["l"] = clip.tokenize(clip_l)["l"] tokens["t5xxl"] = clip.tokenize(t5xxl)["t5xxl"] tokens["llama"] = clip.tokenize(llama)["llama"] - return (clip.encode_from_tokens_scheduled(tokens), ) + return (clip.encode_from_tokens_scheduled(tokens),) + NODE_CLASS_MAPPINGS = { "QuadrupleCLIPLoader": QuadrupleCLIPLoader, diff --git a/comfy_extras/nodes/nodes_hunyuan3d.py b/comfy_extras/nodes/nodes_hunyuan3d.py index 2c1d0a4fa..5fe59f801 100644 --- a/comfy_extras/nodes/nodes_hunyuan3d.py +++ b/comfy_extras/nodes/nodes_hunyuan3d.py @@ -447,6 +447,8 @@ class VoxelToMesh: mesh_function = voxel_to_mesh elif algorithm == "surface net": mesh_function = voxel_to_mesh_surfnet + else: + mesh_function = None for x in voxel.data: v, f = mesh_function(x, threshold=threshold, device=None) diff --git a/comfy_extras/nodes_optimalsteps.py b/comfy_extras/nodes/nodes_optimalsteps.py similarity index 100% rename from comfy_extras/nodes_optimalsteps.py rename to comfy_extras/nodes/nodes_optimalsteps.py