diff --git a/.github/workflows/test-launch.yml b/.github/workflows/test-launch.yml index 581c0474b842..ef0d3f123763 100644 --- a/.github/workflows/test-launch.yml +++ b/.github/workflows/test-launch.yml @@ -13,7 +13,7 @@ jobs: - name: Checkout ComfyUI uses: actions/checkout@v4 with: - repository: "Comfy-Org/ComfyUI" + repository: "comfyanonymous/ComfyUI" path: "ComfyUI" - uses: actions/setup-python@v4 with: diff --git a/README.md b/README.md index e25f3cda72bb..6d09758c0ead 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ Simply download, extract with [7-Zip](https://7-zip.org) or with the windows exp If you have trouble extracting it, right click the file -> properties -> unblock -The portable above currently comes with python 3.13 and pytorch cuda 13.0. Update your Nvidia drivers if it doesn't start. +Update your Nvidia drivers if it doesn't start. #### Alternative Downloads: @@ -212,7 +212,7 @@ Python 3.14 works but you may encounter issues with the torch compile node. The Python 3.13 is very well supported. If you have trouble with some custom node dependencies on 3.13 you can try 3.12 -torch 2.4 and above is supported but some features might only work on newer versions. We generally recommend using the latest major version of pytorch with the latest cuda version unless it is less than 2 weeks old. +torch 2.4 and above is supported but some features might only work on newer versions. We generally recommend using the latest major version of pytorch unless it is less than 2 weeks old. ### Instructions: diff --git a/app/assets/database/bulk_ops.py b/app/assets/database/bulk_ops.py index c7b75290af07..9352cd65d465 100644 --- a/app/assets/database/bulk_ops.py +++ b/app/assets/database/bulk_ops.py @@ -92,23 +92,14 @@ def seed_from_paths_batch( session.execute(ins_asset, chunk) # try to claim AssetCacheState (file_path) - # Insert with ON CONFLICT DO NOTHING, then query to find which paths were actually inserted + winners_by_path: set[str] = set() ins_state = ( sqlite.insert(AssetCacheState) .on_conflict_do_nothing(index_elements=[AssetCacheState.file_path]) + .returning(AssetCacheState.file_path) ) for chunk in _iter_chunks(state_rows, _rows_per_stmt(3)): - session.execute(ins_state, chunk) - - # Query to find which of our paths won (were actually inserted) - winners_by_path: set[str] = set() - for chunk in _iter_chunks(path_list, MAX_BIND_PARAMS): - result = session.execute( - sqlalchemy.select(AssetCacheState.file_path) - .where(AssetCacheState.file_path.in_(chunk)) - .where(AssetCacheState.asset_id.in_([path_to_asset[p] for p in chunk])) - ) - winners_by_path.update(result.scalars().all()) + winners_by_path.update((session.execute(ins_state, chunk)).scalars().all()) all_paths_set = set(path_list) losers_by_path = all_paths_set - winners_by_path @@ -121,23 +112,16 @@ def seed_from_paths_batch( return {"inserted_infos": 0, "won_states": 0, "lost_states": len(losers_by_path)} # insert AssetInfo only for winners - # Insert with ON CONFLICT DO NOTHING, then query to find which were actually inserted winner_info_rows = [asset_to_info[path_to_asset[p]] for p in winners_by_path] ins_info = ( sqlite.insert(AssetInfo) .on_conflict_do_nothing(index_elements=[AssetInfo.asset_id, AssetInfo.owner_id, AssetInfo.name]) + .returning(AssetInfo.id) ) - for chunk in _iter_chunks(winner_info_rows, _rows_per_stmt(9)): - session.execute(ins_info, chunk) - # Query to find which info rows were actually inserted (by matching our generated IDs) - all_info_ids = [row["id"] for row in winner_info_rows] inserted_info_ids: set[str] = set() - for chunk in _iter_chunks(all_info_ids, MAX_BIND_PARAMS): - result = session.execute( - sqlalchemy.select(AssetInfo.id).where(AssetInfo.id.in_(chunk)) - ) - inserted_info_ids.update(result.scalars().all()) + for chunk in _iter_chunks(winner_info_rows, _rows_per_stmt(9)): + inserted_info_ids.update((session.execute(ins_info, chunk)).scalars().all()) # build and insert tag + meta rows for the AssetInfo tag_rows: list[dict] = [] diff --git a/app/subgraph_manager.py b/app/subgraph_manager.py index 6a8f586a469a..dbe40454169e 100644 --- a/app/subgraph_manager.py +++ b/app/subgraph_manager.py @@ -10,7 +10,6 @@ class Source: custom_node = "custom_node" - templates = "templates" class SubgraphEntry(TypedDict): source: str @@ -39,18 +38,6 @@ class CustomNodeSubgraphEntryInfo(TypedDict): class SubgraphManager: def __init__(self): self.cached_custom_node_subgraphs: dict[SubgraphEntry] | None = None - self.cached_blueprint_subgraphs: dict[SubgraphEntry] | None = None - - def _create_entry(self, file: str, source: str, node_pack: str) -> tuple[str, SubgraphEntry]: - """Create a subgraph entry from a file path. Expects normalized path (forward slashes).""" - entry_id = hashlib.sha256(f"{source}{file}".encode()).hexdigest() - entry: SubgraphEntry = { - "source": source, - "name": os.path.splitext(os.path.basename(file))[0], - "path": file, - "info": {"node_pack": node_pack}, - } - return entry_id, entry async def load_entry_data(self, entry: SubgraphEntry): with open(entry['path'], 'r') as f: @@ -73,60 +60,53 @@ async def sanitize_entries(self, entries: dict[str, SubgraphEntry], remove_data= return entries async def get_custom_node_subgraphs(self, loadedModules, force_reload=False): - """Load subgraphs from custom nodes.""" + # if not forced to reload and cached, return cache if not force_reload and self.cached_custom_node_subgraphs is not None: return self.cached_custom_node_subgraphs - + # Load subgraphs from custom nodes + subfolder = "subgraphs" subgraphs_dict: dict[SubgraphEntry] = {} + for folder in folder_paths.get_folder_paths("custom_nodes"): - pattern = os.path.join(folder, "*/subgraphs/*.json") - for file in glob.glob(pattern): + pattern = os.path.join(folder, f"*/{subfolder}/*.json") + matched_files = glob.glob(pattern) + for file in matched_files: + # replace backslashes with forward slashes file = file.replace('\\', '/') - node_pack = "custom_nodes." + file.split('/')[-3] - entry_id, entry = self._create_entry(file, Source.custom_node, node_pack) - subgraphs_dict[entry_id] = entry - + info: CustomNodeSubgraphEntryInfo = { + "node_pack": "custom_nodes." + file.split('/')[-3] + } + source = Source.custom_node + # hash source + path to make sure id will be as unique as possible, but + # reproducible across backend reloads + id = hashlib.sha256(f"{source}{file}".encode()).hexdigest() + entry: SubgraphEntry = { + "source": Source.custom_node, + "name": os.path.splitext(os.path.basename(file))[0], + "path": file, + "info": info, + } + subgraphs_dict[id] = entry self.cached_custom_node_subgraphs = subgraphs_dict return subgraphs_dict - async def get_blueprint_subgraphs(self, force_reload=False): - """Load subgraphs from the blueprints directory.""" - if not force_reload and self.cached_blueprint_subgraphs is not None: - return self.cached_blueprint_subgraphs - - subgraphs_dict: dict[SubgraphEntry] = {} - blueprints_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'blueprints') - - if os.path.exists(blueprints_dir): - for file in glob.glob(os.path.join(blueprints_dir, "*.json")): - file = file.replace('\\', '/') - entry_id, entry = self._create_entry(file, Source.templates, "comfyui") - subgraphs_dict[entry_id] = entry - - self.cached_blueprint_subgraphs = subgraphs_dict - return subgraphs_dict - - async def get_all_subgraphs(self, loadedModules, force_reload=False): - """Get all subgraphs from all sources (custom nodes and blueprints).""" - custom_node_subgraphs = await self.get_custom_node_subgraphs(loadedModules, force_reload) - blueprint_subgraphs = await self.get_blueprint_subgraphs(force_reload) - return {**custom_node_subgraphs, **blueprint_subgraphs} - - async def get_subgraph(self, id: str, loadedModules): - """Get a specific subgraph by ID from any source.""" - entry = (await self.get_all_subgraphs(loadedModules)).get(id) - if entry is not None and entry.get('data') is None: + async def get_custom_node_subgraph(self, id: str, loadedModules): + subgraphs = await self.get_custom_node_subgraphs(loadedModules) + entry: SubgraphEntry = subgraphs.get(id, None) + if entry is not None and entry.get('data', None) is None: await self.load_entry_data(entry) return entry def add_routes(self, routes, loadedModules): @routes.get("/global_subgraphs") async def get_global_subgraphs(request): - subgraphs_dict = await self.get_all_subgraphs(loadedModules) + subgraphs_dict = await self.get_custom_node_subgraphs(loadedModules) + # NOTE: we may want to include other sources of global subgraphs such as templates in the future; + # that's the reasoning for the current implementation return web.json_response(await self.sanitize_entries(subgraphs_dict, remove_data=True)) @routes.get("/global_subgraphs/{id}") async def get_global_subgraph(request): id = request.match_info.get("id", None) - subgraph = await self.get_subgraph(id, loadedModules) + subgraph = await self.get_custom_node_subgraph(id, loadedModules) return web.json_response(await self.sanitize_entry(subgraph)) diff --git a/comfy/clip_model.py b/comfy/clip_model.py index d7d3f994c4db..e88872728f4a 100644 --- a/comfy/clip_model.py +++ b/comfy/clip_model.py @@ -1,7 +1,6 @@ import torch from comfy.ldm.modules.attention import optimized_attention_for_device import comfy.ops -import math def clip_preprocess(image, size=224, mean=[0.48145466, 0.4578275, 0.40821073], std=[0.26862954, 0.26130258, 0.27577711], crop=True): image = image[:, :, :, :3] if image.shape[3] > 3 else image @@ -22,39 +21,6 @@ def clip_preprocess(image, size=224, mean=[0.48145466, 0.4578275, 0.40821073], s image = torch.clip((255. * image), 0, 255).round() / 255.0 return (image - mean.view([3,1,1])) / std.view([3,1,1]) -def siglip2_flex_calc_resolution(oh, ow, patch_size, max_num_patches, eps=1e-5): - def scale_dim(size, scale): - scaled = math.ceil(size * scale / patch_size) * patch_size - return max(patch_size, int(scaled)) - - # Binary search for optimal scale - lo, hi = eps / 10, 100.0 - while hi - lo >= eps: - mid = (lo + hi) / 2 - h, w = scale_dim(oh, mid), scale_dim(ow, mid) - if (h // patch_size) * (w // patch_size) <= max_num_patches: - lo = mid - else: - hi = mid - - return scale_dim(oh, lo), scale_dim(ow, lo) - -def siglip2_preprocess(image, size, patch_size, num_patches, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5], crop=True): - if size > 0: - return clip_preprocess(image, size=size, mean=mean, std=std, crop=crop) - - image = image[:, :, :, :3] if image.shape[3] > 3 else image - mean = torch.tensor(mean, device=image.device, dtype=image.dtype) - std = torch.tensor(std, device=image.device, dtype=image.dtype) - image = image.movedim(-1, 1) - - b, c, h, w = image.shape - h, w = siglip2_flex_calc_resolution(h, w, patch_size, num_patches) - - image = torch.nn.functional.interpolate(image, size=(h, w), mode="bilinear", antialias=True) - image = torch.clip((255. * image), 0, 255).round() / 255.0 - return (image - mean.view([3, 1, 1])) / std.view([3, 1, 1]) - class CLIPAttention(torch.nn.Module): def __init__(self, embed_dim, heads, dtype, device, operations): super().__init__() @@ -209,27 +175,6 @@ def forward(self, *args, **kwargs): out = self.text_projection(x[2]) return (x[0], x[1], out, x[2]) -def siglip2_pos_embed(embed_weight, embeds, orig_shape): - embed_weight_len = round(embed_weight.shape[0] ** 0.5) - embed_weight = comfy.ops.cast_to_input(embed_weight, embeds).movedim(1, 0).reshape(1, -1, embed_weight_len, embed_weight_len) - embed_weight = torch.nn.functional.interpolate(embed_weight, size=orig_shape, mode="bilinear", align_corners=False, antialias=True) - embed_weight = embed_weight.reshape(-1, embed_weight.shape[-2] * embed_weight.shape[-1]).movedim(0, 1) - return embeds + embed_weight - -class Siglip2Embeddings(torch.nn.Module): - def __init__(self, embed_dim, num_channels=3, patch_size=14, image_size=224, model_type="", num_patches=None, dtype=None, device=None, operations=None): - super().__init__() - self.patch_embedding = operations.Linear(num_channels * patch_size * patch_size, embed_dim, dtype=dtype, device=device) - self.position_embedding = operations.Embedding(num_patches, embed_dim, dtype=dtype, device=device) - self.patch_size = patch_size - - def forward(self, pixel_values): - b, c, h, w = pixel_values.shape - img = pixel_values.movedim(1, -1).reshape(b, h // self.patch_size, self.patch_size, w // self.patch_size, self.patch_size, c) - img = img.permute(0, 1, 3, 2, 4, 5) - img = img.reshape(b, img.shape[1] * img.shape[2], -1) - img = self.patch_embedding(img) - return siglip2_pos_embed(self.position_embedding.weight, img, (h // self.patch_size, w // self.patch_size)) class CLIPVisionEmbeddings(torch.nn.Module): def __init__(self, embed_dim, num_channels=3, patch_size=14, image_size=224, model_type="", dtype=None, device=None, operations=None): @@ -273,11 +218,8 @@ def __init__(self, config_dict, dtype, device, operations): intermediate_activation = config_dict["hidden_act"] model_type = config_dict["model_type"] - if model_type in ["siglip2_vision_model"]: - self.embeddings = Siglip2Embeddings(embed_dim, config_dict["num_channels"], config_dict["patch_size"], config_dict["image_size"], model_type=model_type, num_patches=config_dict.get("num_patches", None), dtype=dtype, device=device, operations=operations) - else: - self.embeddings = CLIPVisionEmbeddings(embed_dim, config_dict["num_channels"], config_dict["patch_size"], config_dict["image_size"], model_type=model_type, dtype=dtype, device=device, operations=operations) - if model_type in ["siglip_vision_model", "siglip2_vision_model"]: + self.embeddings = CLIPVisionEmbeddings(embed_dim, config_dict["num_channels"], config_dict["patch_size"], config_dict["image_size"], model_type=model_type, dtype=dtype, device=device, operations=operations) + if model_type == "siglip_vision_model": self.pre_layrnorm = lambda a: a self.output_layernorm = True else: diff --git a/comfy/clip_vision.py b/comfy/clip_vision.py index 66f2a9d9cb4e..d5fc5349748c 100644 --- a/comfy/clip_vision.py +++ b/comfy/clip_vision.py @@ -21,7 +21,6 @@ def __setitem__(self, key, item): IMAGE_ENCODERS = { "clip_vision_model": comfy.clip_model.CLIPVisionModelProjection, "siglip_vision_model": comfy.clip_model.CLIPVisionModelProjection, - "siglip2_vision_model": comfy.clip_model.CLIPVisionModelProjection, "dinov2": comfy.image_encoders.dino2.Dinov2Model, } @@ -33,10 +32,9 @@ def __init__(self, json_config): self.image_size = config.get("image_size", 224) self.image_mean = config.get("image_mean", [0.48145466, 0.4578275, 0.40821073]) self.image_std = config.get("image_std", [0.26862954, 0.26130258, 0.27577711]) - self.model_type = config.get("model_type", "clip_vision_model") - self.config = config.copy() - model_class = IMAGE_ENCODERS.get(self.model_type) - if self.model_type == "siglip_vision_model": + model_type = config.get("model_type", "clip_vision_model") + model_class = IMAGE_ENCODERS.get(model_type) + if model_type == "siglip_vision_model": self.return_all_hidden_states = True else: self.return_all_hidden_states = False @@ -57,10 +55,7 @@ def get_sd(self): def encode_image(self, image, crop=True): comfy.model_management.load_model_gpu(self.patcher) - if self.model_type == "siglip2_vision_model": - pixel_values = comfy.clip_model.siglip2_preprocess(image.to(self.load_device), size=self.image_size, patch_size=self.config.get("patch_size", 16), num_patches=self.config.get("num_patches", 256), mean=self.image_mean, std=self.image_std, crop=crop).float() - else: - 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=crop).float() + 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=crop).float() out = self.model(pixel_values=pixel_values, intermediate_output='all' if self.return_all_hidden_states else -2) outputs = Output() @@ -112,14 +107,10 @@ def load_clipvision_from_sd(sd, prefix="", convert_keys=False): elif "vision_model.encoder.layers.22.layer_norm1.weight" in sd: embed_shape = sd["vision_model.embeddings.position_embedding.weight"].shape[0] if sd["vision_model.encoder.layers.0.layer_norm1.weight"].shape[0] == 1152: - patch_embedding_shape = sd["vision_model.embeddings.patch_embedding.weight"].shape - if len(patch_embedding_shape) == 2: - json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip2_base_naflex.json") - else: - if embed_shape == 729: - json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_384.json") - elif embed_shape == 1024: - json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_512.json") + if embed_shape == 729: + json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_384.json") + elif embed_shape == 1024: + json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_512.json") elif embed_shape == 577: if "multi_modal_projector.linear_1.bias" in sd: json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl_336_llava.json") diff --git a/comfy/float.py b/comfy/float.py index 88c47cd8097b..521316fd2fac 100644 --- a/comfy/float.py +++ b/comfy/float.py @@ -65,147 +65,3 @@ def stochastic_rounding(value, dtype, seed=0): return output return value.to(dtype=dtype) - - -# TODO: improve this? -def stochastic_float_to_fp4_e2m1(x, generator): - orig_shape = x.shape - sign = torch.signbit(x).to(torch.uint8) - - exp = torch.floor(torch.log2(x.abs()) + 1.0).clamp(0, 3) - x += (torch.rand(x.size(), dtype=x.dtype, layout=x.layout, device=x.device, generator=generator) - 0.5) * (2 ** (exp - 2.0)) * 1.25 - - x = x.abs() - exp = torch.floor(torch.log2(x) + 1.1925).clamp(0, 3) - - mantissa = torch.where( - exp > 0, - (x / (2.0 ** (exp - 1)) - 1.0) * 2.0, - (x * 2.0), - out=x - ).round().to(torch.uint8) - del x - - exp = exp.to(torch.uint8) - - fp4 = (sign << 3) | (exp << 1) | mantissa - del sign, exp, mantissa - - fp4_flat = fp4.view(-1) - packed = (fp4_flat[0::2] << 4) | fp4_flat[1::2] - return packed.reshape(list(orig_shape)[:-1] + [-1]) - - -def to_blocked(input_matrix, flatten: bool = True) -> torch.Tensor: - """ - Rearrange a large matrix by breaking it into blocks and applying the rearrangement pattern. - See: - https://docs.nvidia.com/cuda/cublas/index.html#d-block-scaling-factors-layout - - Args: - input_matrix: Input tensor of shape (H, W) - Returns: - Rearranged tensor of shape (32*ceil_div(H,128), 16*ceil_div(W,4)) - """ - - def ceil_div(a, b): - return (a + b - 1) // b - - rows, cols = input_matrix.shape - n_row_blocks = ceil_div(rows, 128) - n_col_blocks = ceil_div(cols, 4) - - # Calculate the padded shape - padded_rows = n_row_blocks * 128 - padded_cols = n_col_blocks * 4 - - padded = input_matrix - if (rows, cols) != (padded_rows, padded_cols): - padded = torch.zeros( - (padded_rows, padded_cols), - device=input_matrix.device, - dtype=input_matrix.dtype, - ) - padded[:rows, :cols] = input_matrix - - # Rearrange the blocks - blocks = padded.view(n_row_blocks, 128, n_col_blocks, 4).permute(0, 2, 1, 3) - rearranged = blocks.reshape(-1, 4, 32, 4).transpose(1, 2).reshape(-1, 32, 16) - if flatten: - return rearranged.flatten() - - return rearranged.reshape(padded_rows, padded_cols) - - -def stochastic_round_quantize_nvfp4_block(x, per_tensor_scale, generator): - F4_E2M1_MAX = 6.0 - F8_E4M3_MAX = 448.0 - - orig_shape = x.shape - - block_size = 16 - - x = x.reshape(orig_shape[0], -1, block_size) - scaled_block_scales_fp8 = torch.clamp(((torch.amax(torch.abs(x), dim=-1)) / F4_E2M1_MAX) / per_tensor_scale.to(x.dtype), max=F8_E4M3_MAX).to(torch.float8_e4m3fn) - x = x / (per_tensor_scale.to(x.dtype) * scaled_block_scales_fp8.to(x.dtype)).unsqueeze(-1) - - x = x.view(orig_shape).nan_to_num() - data_lp = stochastic_float_to_fp4_e2m1(x, generator=generator) - return data_lp, scaled_block_scales_fp8 - - -def stochastic_round_quantize_nvfp4(x, per_tensor_scale, pad_16x, seed=0): - def roundup(x: int, multiple: int) -> int: - """Round up x to the nearest multiple.""" - return ((x + multiple - 1) // multiple) * multiple - - generator = torch.Generator(device=x.device) - generator.manual_seed(seed) - - # Handle padding - if pad_16x: - rows, cols = x.shape - padded_rows = roundup(rows, 16) - padded_cols = roundup(cols, 16) - if padded_rows != rows or padded_cols != cols: - x = torch.nn.functional.pad(x, (0, padded_cols - cols, 0, padded_rows - rows)) - - x, blocked_scaled = stochastic_round_quantize_nvfp4_block(x, per_tensor_scale, generator) - return x, to_blocked(blocked_scaled, flatten=False) - - -def stochastic_round_quantize_nvfp4_by_block(x, per_tensor_scale, pad_16x, seed=0, block_size=4096 * 4096): - def roundup(x: int, multiple: int) -> int: - """Round up x to the nearest multiple.""" - return ((x + multiple - 1) // multiple) * multiple - - orig_shape = x.shape - - # Handle padding - if pad_16x: - rows, cols = x.shape - padded_rows = roundup(rows, 16) - padded_cols = roundup(cols, 16) - if padded_rows != rows or padded_cols != cols: - x = torch.nn.functional.pad(x, (0, padded_cols - cols, 0, padded_rows - rows)) - # Note: We update orig_shape because the output tensor logic below assumes x.shape matches - # what we want to produce. If we pad here, we want the padded output. - orig_shape = x.shape - - orig_shape = list(orig_shape) - - output_fp4 = torch.empty(orig_shape[:-1] + [orig_shape[-1] // 2], dtype=torch.uint8, device=x.device) - output_block = torch.empty(orig_shape[:-1] + [orig_shape[-1] // 16], dtype=torch.float8_e4m3fn, device=x.device) - - generator = torch.Generator(device=x.device) - generator.manual_seed(seed) - - num_slices = max(1, (x.numel() / block_size)) - slice_size = max(1, (round(x.shape[0] / num_slices))) - - for i in range(0, x.shape[0], slice_size): - fp4, block = stochastic_round_quantize_nvfp4_block(x[i: i + slice_size], per_tensor_scale, generator=generator) - output_fp4[i:i + slice_size].copy_(fp4) - output_block[i:i + slice_size].copy_(block) - - return output_fp4, to_blocked(output_block, flatten=False) diff --git a/comfy/ldm/lightricks/av_model.py b/comfy/ldm/lightricks/av_model.py index c12ace241e09..759535501f23 100644 --- a/comfy/ldm/lightricks/av_model.py +++ b/comfy/ldm/lightricks/av_model.py @@ -11,69 +11,6 @@ from comfy.ldm.lightricks.symmetric_patchifier import AudioPatchifier import comfy.ldm.common_dit -class CompressedTimestep: - """Store video timestep embeddings in compressed form using per-frame indexing.""" - __slots__ = ('data', 'batch_size', 'num_frames', 'patches_per_frame', 'feature_dim') - - def __init__(self, tensor: torch.Tensor, patches_per_frame: int): - """ - tensor: [batch_size, num_tokens, feature_dim] tensor where num_tokens = num_frames * patches_per_frame - patches_per_frame: Number of spatial patches per frame (height * width in latent space) - """ - self.batch_size, num_tokens, self.feature_dim = tensor.shape - - # Check if compression is valid (num_tokens must be divisible by patches_per_frame) - if num_tokens % patches_per_frame == 0 and num_tokens >= patches_per_frame: - self.patches_per_frame = patches_per_frame - self.num_frames = num_tokens // patches_per_frame - - # Reshape to [batch, frames, patches_per_frame, feature_dim] and store one value per frame - # All patches in a frame are identical, so we only keep the first one - reshaped = tensor.view(self.batch_size, self.num_frames, patches_per_frame, self.feature_dim) - self.data = reshaped[:, :, 0, :].contiguous() # [batch, frames, feature_dim] - else: - # Not divisible or too small - store directly without compression - self.patches_per_frame = 1 - self.num_frames = num_tokens - self.data = tensor - - def expand(self): - """Expand back to original tensor.""" - if self.patches_per_frame == 1: - return self.data - - # [batch, frames, feature_dim] -> [batch, frames, patches_per_frame, feature_dim] -> [batch, tokens, feature_dim] - expanded = self.data.unsqueeze(2).expand(self.batch_size, self.num_frames, self.patches_per_frame, self.feature_dim) - return expanded.reshape(self.batch_size, -1, self.feature_dim) - - def expand_for_computation(self, scale_shift_table: torch.Tensor, batch_size: int, indices: slice = slice(None, None)): - """Compute ada values on compressed per-frame data, then expand spatially.""" - num_ada_params = scale_shift_table.shape[0] - - # No compression - compute directly - if self.patches_per_frame == 1: - num_tokens = self.data.shape[1] - dim_per_param = self.feature_dim // num_ada_params - reshaped = self.data.reshape(batch_size, num_tokens, num_ada_params, dim_per_param)[:, :, indices, :] - table_values = scale_shift_table[indices].unsqueeze(0).unsqueeze(0).to(device=self.data.device, dtype=self.data.dtype) - ada_values = (table_values + reshaped).unbind(dim=2) - return ada_values - - # Compressed: compute on per-frame data then expand spatially - # Reshape: [batch, frames, feature_dim] -> [batch, frames, num_ada_params, dim_per_param] - frame_reshaped = self.data.reshape(batch_size, self.num_frames, num_ada_params, -1)[:, :, indices, :] - table_values = scale_shift_table[indices].unsqueeze(0).unsqueeze(0).to( - device=self.data.device, dtype=self.data.dtype - ) - frame_ada = (table_values + frame_reshaped).unbind(dim=2) - - # Expand each ada parameter spatially: [batch, frames, dim] -> [batch, frames, patches, dim] -> [batch, tokens, dim] - return tuple( - frame_val.unsqueeze(2).expand(batch_size, self.num_frames, self.patches_per_frame, -1) - .reshape(batch_size, -1, frame_val.shape[-1]) - for frame_val in frame_ada - ) - class BasicAVTransformerBlock(nn.Module): def __init__( self, @@ -182,9 +119,6 @@ def __init__( def get_ada_values( self, scale_shift_table: torch.Tensor, batch_size: int, timestep: torch.Tensor, indices: slice = slice(None, None) ): - if isinstance(timestep, CompressedTimestep): - return timestep.expand_for_computation(scale_shift_table, batch_size, indices) - num_ada_params = scale_shift_table.shape[0] ada_values = ( @@ -212,7 +146,10 @@ def get_av_ca_ada_values( gate_timestep, ) - return (*scale_shift_ada_values, *gate_ada_values) + scale_shift_chunks = [t.squeeze(2) for t in scale_shift_ada_values] + gate_ada_values = [t.squeeze(2) for t in gate_ada_values] + + return (*scale_shift_chunks, *gate_ada_values) def forward( self, @@ -606,80 +543,72 @@ def _prepare_timestep(self, timestep, batch_size, hidden_dtype, **kwargs): if grid_mask is not None: timestep = timestep[:, grid_mask] - timestep_scaled = timestep * self.timestep_scale_multiplier - + timestep = timestep * self.timestep_scale_multiplier v_timestep, v_embedded_timestep = self.adaln_single( - timestep_scaled.flatten(), + timestep.flatten(), {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) - # Calculate patches_per_frame from orig_shape: [batch, channels, frames, height, width] - # Video tokens are arranged as (frames * height * width), so patches_per_frame = height * width - orig_shape = kwargs.get("orig_shape") - v_patches_per_frame = None - if orig_shape is not None and len(orig_shape) == 5: - # orig_shape[3] = height, orig_shape[4] = width (in latent space) - v_patches_per_frame = orig_shape[3] * orig_shape[4] - - # Reshape to [batch_size, num_tokens, dim] and compress for storage - v_timestep = CompressedTimestep(v_timestep.view(batch_size, -1, v_timestep.shape[-1]), v_patches_per_frame) - v_embedded_timestep = CompressedTimestep(v_embedded_timestep.view(batch_size, -1, v_embedded_timestep.shape[-1]), v_patches_per_frame) + # Second dimension is 1 or number of tokens (if timestep_per_token) + v_timestep = v_timestep.view(batch_size, -1, v_timestep.shape[-1]) + v_embedded_timestep = v_embedded_timestep.view( + batch_size, -1, v_embedded_timestep.shape[-1] + ) # Prepare audio timestep a_timestep = kwargs.get("a_timestep") if a_timestep is not None: - a_timestep_scaled = a_timestep * self.timestep_scale_multiplier - a_timestep_flat = a_timestep_scaled.flatten() - timestep_flat = timestep_scaled.flatten() + a_timestep = a_timestep * self.timestep_scale_multiplier av_ca_factor = self.av_ca_timestep_scale_multiplier / self.timestep_scale_multiplier - # Cross-attention timesteps - compress these too av_ca_audio_scale_shift_timestep, _ = self.av_ca_audio_scale_shift_adaln_single( - a_timestep_flat, + a_timestep.flatten(), {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) av_ca_video_scale_shift_timestep, _ = self.av_ca_video_scale_shift_adaln_single( - timestep_flat, + timestep.flatten(), {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) av_ca_a2v_gate_noise_timestep, _ = self.av_ca_a2v_gate_adaln_single( - timestep_flat * av_ca_factor, + timestep.flatten() * av_ca_factor, {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) av_ca_v2a_gate_noise_timestep, _ = self.av_ca_v2a_gate_adaln_single( - a_timestep_flat * av_ca_factor, + a_timestep.flatten() * av_ca_factor, {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) - # Compress cross-attention timesteps (only video side, audio is too small to benefit) - cross_av_timestep_ss = [ - av_ca_audio_scale_shift_timestep.view(batch_size, -1, av_ca_audio_scale_shift_timestep.shape[-1]), - CompressedTimestep(av_ca_video_scale_shift_timestep.view(batch_size, -1, av_ca_video_scale_shift_timestep.shape[-1]), v_patches_per_frame), # video - compressed - CompressedTimestep(av_ca_a2v_gate_noise_timestep.view(batch_size, -1, av_ca_a2v_gate_noise_timestep.shape[-1]), v_patches_per_frame), # video - compressed - av_ca_v2a_gate_noise_timestep.view(batch_size, -1, av_ca_v2a_gate_noise_timestep.shape[-1]), - ] - a_timestep, a_embedded_timestep = self.audio_adaln_single( - a_timestep_flat, + a_timestep.flatten(), {"resolution": None, "aspect_ratio": None}, batch_size=batch_size, hidden_dtype=hidden_dtype, ) - # Audio timesteps a_timestep = a_timestep.view(batch_size, -1, a_timestep.shape[-1]) - a_embedded_timestep = a_embedded_timestep.view(batch_size, -1, a_embedded_timestep.shape[-1]) + a_embedded_timestep = a_embedded_timestep.view( + batch_size, -1, a_embedded_timestep.shape[-1] + ) + cross_av_timestep_ss = [ + av_ca_audio_scale_shift_timestep, + av_ca_video_scale_shift_timestep, + av_ca_a2v_gate_noise_timestep, + av_ca_v2a_gate_noise_timestep, + ] + cross_av_timestep_ss = list( + [t.view(batch_size, -1, t.shape[-1]) for t in cross_av_timestep_ss] + ) else: - a_timestep = timestep_scaled + a_timestep = timestep a_embedded_timestep = kwargs.get("embedded_timestep") cross_av_timestep_ss = [] @@ -838,11 +767,6 @@ def _process_output(self, x, embedded_timestep, keyframe_idxs, **kwargs): ax = x[1] v_embedded_timestep = embedded_timestep[0] a_embedded_timestep = embedded_timestep[1] - - # Expand compressed video timestep if needed - if isinstance(v_embedded_timestep, CompressedTimestep): - v_embedded_timestep = v_embedded_timestep.expand() - vx = super()._process_output(vx, v_embedded_timestep, keyframe_idxs, **kwargs) # Process audio output diff --git a/comfy/lora.py b/comfy/lora.py index e8246bd66d62..2ed0acb9de20 100644 --- a/comfy/lora.py +++ b/comfy/lora.py @@ -322,7 +322,6 @@ def model_lora_keys_unet(model, key_map={}): key_map["diffusion_model.{}".format(key_lora)] = to key_map["transformer.{}".format(key_lora)] = to key_map["lycoris_{}".format(key_lora.replace(".", "_"))] = to - key_map[key_lora] = to if isinstance(model, comfy.model_base.Kandinsky5): for k in sdk: diff --git a/comfy/model_detection.py b/comfy/model_detection.py index aff5a50b93d3..0853b3aec5cd 100644 --- a/comfy/model_detection.py +++ b/comfy/model_detection.py @@ -237,8 +237,6 @@ def detect_unet_config(state_dict, key_prefix, metadata=None): else: dit_config["vec_in_dim"] = None - dit_config["num_heads"] = dit_config["hidden_size"] // sum(dit_config["axes_dim"]) - dit_config["depth"] = count_blocks(state_dict_keys, '{}double_blocks.'.format(key_prefix) + '{}.') dit_config["depth_single_blocks"] = count_blocks(state_dict_keys, '{}single_blocks.'.format(key_prefix) + '{}.') if '{}distilled_guidance_layer.0.norms.0.scale'.format(key_prefix) in state_dict_keys or '{}distilled_guidance_layer.norms.0.scale'.format(key_prefix) in state_dict_keys: #Chroma diff --git a/comfy/model_management.py b/comfy/model_management.py index 9d39be7b2a19..e5de4a5b5238 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -368,7 +368,7 @@ def aotriton_supported(gpu_arch): if any((a in arch) for a in ["gfx90a", "gfx942", "gfx1100", "gfx1101", "gfx1151"]): # TODO: more arches, TODO: gfx950 ENABLE_PYTORCH_ATTENTION = True if rocm_version >= (7, 0): - if any((a in arch) for a in ["gfx1200", "gfx1201"]): + if any((a in arch) for a in ["gfx1201"]): ENABLE_PYTORCH_ATTENTION = True if torch_version_numeric >= (2, 7) and rocm_version >= (6, 4): if any((a in arch) for a in ["gfx1200", "gfx1201", "gfx950"]): # TODO: more arches, "gfx942" gives error on pytorch nightly 2.10 1013 rocm7.0 diff --git a/comfy/ops.py b/comfy/ops.py index 415c39e9202b..9c0b54ff44f0 100644 --- a/comfy/ops.py +++ b/comfy/ops.py @@ -699,7 +699,7 @@ def convert_weight(self, weight, inplace=False, **kwargs): def set_weight(self, weight, inplace_update=False, seed=None, return_weight=False, **kwargs): if getattr(self, 'layout_type', None) is not None: # dtype is now implicit in the layout class - weight = QuantizedTensor.from_float(weight, self.layout_type, scale="recalculate", stochastic_rounding=seed, inplace_ops=True).to(self.weight.dtype) + weight = QuantizedTensor.from_float(weight, self.layout_type, scale="recalculate", stochastic_rounding=seed, inplace_ops=True) else: weight = weight.to(self.weight.dtype) if return_weight: diff --git a/comfy/quant_ops.py b/comfy/quant_ops.py index 15a4f457bed6..8324be42a0a9 100644 --- a/comfy/quant_ops.py +++ b/comfy/quant_ops.py @@ -7,7 +7,7 @@ QuantizedTensor, QuantizedLayout, TensorCoreFP8Layout as _CKFp8Layout, - TensorCoreNVFP4Layout as _CKNvfp4Layout, + TensorCoreNVFP4Layout, # Direct import, no wrapper needed register_layout_op, register_layout_class, get_layout_class, @@ -34,7 +34,7 @@ class QuantizedTensor: class _CKFp8Layout: pass - class _CKNvfp4Layout: + class TensorCoreNVFP4Layout: pass def register_layout_class(name, cls): @@ -84,39 +84,6 @@ def quantize(cls, tensor, scale=None, stochastic_rounding=0, inplace_ops=False): return qdata, params -class TensorCoreNVFP4Layout(_CKNvfp4Layout): - @classmethod - def quantize(cls, tensor, scale=None, stochastic_rounding=0, inplace_ops=False): - if tensor.dim() != 2: - raise ValueError(f"NVFP4 requires 2D tensor, got {tensor.dim()}D") - - orig_dtype = tensor.dtype - orig_shape = tuple(tensor.shape) - - if scale is None or (isinstance(scale, str) and scale == "recalculate"): - scale = torch.amax(tensor.abs()) / (ck.float_utils.F8_E4M3_MAX * ck.float_utils.F4_E2M1_MAX) - - if not isinstance(scale, torch.Tensor): - scale = torch.tensor(scale) - scale = scale.to(device=tensor.device, dtype=torch.float32) - - padded_shape = cls.get_padded_shape(orig_shape) - needs_padding = padded_shape != orig_shape - - if stochastic_rounding > 0: - qdata, block_scale = comfy.float.stochastic_round_quantize_nvfp4_by_block(tensor, scale, pad_16x=needs_padding, seed=stochastic_rounding) - else: - qdata, block_scale = ck.quantize_nvfp4(tensor, scale, pad_16x=needs_padding) - - params = cls.Params( - scale=scale, - orig_dtype=orig_dtype, - orig_shape=orig_shape, - block_scale=block_scale, - ) - return qdata, params - - class TensorCoreFP8E4M3Layout(_TensorCoreFP8LayoutBase): FP8_DTYPE = torch.float8_e4m3fn diff --git a/comfy/sd.py b/comfy/sd.py index 77700dfd3b50..5a7221620290 100644 --- a/comfy/sd.py +++ b/comfy/sd.py @@ -1014,7 +1014,6 @@ class CLIPType(Enum): KANDINSKY5 = 22 KANDINSKY5_IMAGE = 23 NEWBIE = 24 - FLUX2 = 25 def load_clip(ckpt_paths, embedding_directory=None, clip_type=CLIPType.STABLE_DIFFUSION, model_options={}): @@ -1047,7 +1046,6 @@ class TEModel(Enum): QWEN3_2B = 17 GEMMA_3_12B = 18 JINA_CLIP_2 = 19 - QWEN3_8B = 20 def detect_te_model(sd): @@ -1061,9 +1059,9 @@ def detect_te_model(sd): return TEModel.JINA_CLIP_2 if "encoder.block.23.layer.1.DenseReluDense.wi_1.weight" in sd: weight = sd["encoder.block.23.layer.1.DenseReluDense.wi_1.weight"] - if weight.shape[0] == 10240: + if weight.shape[-1] == 4096: return TEModel.T5_XXL - elif weight.shape[0] == 5120: + elif weight.shape[-1] == 2048: return TEModel.T5_XL if 'encoder.block.23.layer.1.DenseReluDense.wi.weight' in sd: return TEModel.T5_XXL_OLD @@ -1091,8 +1089,6 @@ def detect_te_model(sd): return TEModel.QWEN3_4B elif weight.shape[0] == 2048: return TEModel.QWEN3_2B - elif weight.shape[0] == 4096: - return TEModel.QWEN3_8B if weight.shape[0] == 5120: if "model.layers.39.post_attention_layernorm.weight" in sd: return TEModel.MISTRAL3_24B @@ -1218,18 +1214,11 @@ class EmptyClass: clip_target.tokenizer = comfy.text_encoders.flux.Flux2Tokenizer tokenizer_data["tekken_model"] = clip_data[0].get("tekken_model", None) elif te_model == TEModel.QWEN3_4B: - if clip_type == CLIPType.FLUX or clip_type == CLIPType.FLUX2: - clip_target.clip = comfy.text_encoders.flux.klein_te(**llama_detect(clip_data), model_type="qwen3_4b") - clip_target.tokenizer = comfy.text_encoders.flux.KleinTokenizer - else: - clip_target.clip = comfy.text_encoders.z_image.te(**llama_detect(clip_data)) - clip_target.tokenizer = comfy.text_encoders.z_image.ZImageTokenizer + clip_target.clip = comfy.text_encoders.z_image.te(**llama_detect(clip_data)) + clip_target.tokenizer = comfy.text_encoders.z_image.ZImageTokenizer elif te_model == TEModel.QWEN3_2B: clip_target.clip = comfy.text_encoders.ovis.te(**llama_detect(clip_data)) clip_target.tokenizer = comfy.text_encoders.ovis.OvisTokenizer - elif te_model == TEModel.QWEN3_8B: - clip_target.clip = comfy.text_encoders.flux.klein_te(**llama_detect(clip_data), model_type="qwen3_8b") - clip_target.tokenizer = comfy.text_encoders.flux.KleinTokenizer8B elif te_model == TEModel.JINA_CLIP_2: clip_target.clip = comfy.text_encoders.jina_clip_2.JinaClip2TextModelWrapper clip_target.tokenizer = comfy.text_encoders.jina_clip_2.JinaClip2TokenizerWrapper diff --git a/comfy/supported_models.py b/comfy/supported_models.py index 2c4c6b8fc73e..d44c0bc370c6 100644 --- a/comfy/supported_models.py +++ b/comfy/supported_models.py @@ -845,7 +845,7 @@ class LTXAV(LTXV): def __init__(self, unet_config): super().__init__(unet_config) - self.memory_usage_factor = 0.077 # TODO + self.memory_usage_factor = 0.061 # TODO def get_model(self, state_dict, prefix="", device=None): out = model_base.LTXAV(self, device=device) @@ -1042,7 +1042,7 @@ class ZImage(Lumina2): "shift": 3.0, } - memory_usage_factor = 2.8 + memory_usage_factor = 2.0 supported_inference_dtypes = [torch.bfloat16, torch.float32] diff --git a/comfy/text_encoders/cosmos.py b/comfy/text_encoders/cosmos.py index f4b40ac68c33..448381fa9f9c 100644 --- a/comfy/text_encoders/cosmos.py +++ b/comfy/text_encoders/cosmos.py @@ -36,7 +36,7 @@ def __init__(self, device="cpu", dtype=None, model_options={}): if t5_quantization_metadata is not None: model_options = model_options.copy() model_options["t5xxl_quantization_metadata"] = t5_quantization_metadata - if dtype_t5 is not None: + if dtype is None: dtype = dtype_t5 super().__init__(device=device, dtype=dtype, model_options=model_options) return CosmosTEModel_ diff --git a/comfy/text_encoders/flux.py b/comfy/text_encoders/flux.py index 4075afca452b..21d93d757603 100644 --- a/comfy/text_encoders/flux.py +++ b/comfy/text_encoders/flux.py @@ -3,7 +3,7 @@ import comfy.text_encoders.sd3_clip import comfy.text_encoders.llama import comfy.model_management -from transformers import T5TokenizerFast, LlamaTokenizerFast, Qwen2Tokenizer +from transformers import T5TokenizerFast, LlamaTokenizerFast import torch import os import json @@ -172,60 +172,3 @@ def __init__(self, device="cpu", dtype=None, model_options={}): model_options["num_layers"] = 30 super().__init__(device=device, dtype=dtype, model_options=model_options) return Flux2TEModel_ - -class Qwen3Tokenizer(sd1_clip.SDTokenizer): - def __init__(self, embedding_directory=None, tokenizer_data={}): - tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "qwen25_tokenizer") - super().__init__(tokenizer_path, pad_with_end=False, embedding_size=2560, embedding_key='qwen3_4b', tokenizer_class=Qwen2Tokenizer, has_start_token=False, has_end_token=False, pad_to_max_length=False, max_length=99999999, min_length=512, pad_token=151643, tokenizer_data=tokenizer_data) - -class Qwen3Tokenizer8B(sd1_clip.SDTokenizer): - def __init__(self, embedding_directory=None, tokenizer_data={}): - tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "qwen25_tokenizer") - super().__init__(tokenizer_path, pad_with_end=False, embedding_size=4096, embedding_key='qwen3_8b', tokenizer_class=Qwen2Tokenizer, has_start_token=False, has_end_token=False, pad_to_max_length=False, max_length=99999999, min_length=512, pad_token=151643, tokenizer_data=tokenizer_data) - -class KleinTokenizer(sd1_clip.SD1Tokenizer): - def __init__(self, embedding_directory=None, tokenizer_data={}, name="qwen3_4b"): - if name == "qwen3_4b": - tokenizer = Qwen3Tokenizer - elif name == "qwen3_8b": - tokenizer = Qwen3Tokenizer8B - - super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, name=name, tokenizer=tokenizer) - self.llama_template = "<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n\n\n\n\n" - - def tokenize_with_weights(self, text, return_word_ids=False, llama_template=None, **kwargs): - if llama_template is None: - llama_text = self.llama_template.format(text) - else: - llama_text = llama_template.format(text) - - tokens = super().tokenize_with_weights(llama_text, return_word_ids=return_word_ids, disable_weights=True, **kwargs) - return tokens - -class KleinTokenizer8B(KleinTokenizer): - def __init__(self, embedding_directory=None, tokenizer_data={}, name="qwen3_8b"): - super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, name=name) - -class Qwen3_4BModel(sd1_clip.SDClipModel): - def __init__(self, device="cpu", layer=[9, 18, 27], layer_idx=None, dtype=None, attention_mask=True, model_options={}): - super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config={}, dtype=dtype, special_tokens={"pad": 151643}, layer_norm_hidden_state=False, model_class=comfy.text_encoders.llama.Qwen3_4B, enable_attention_masks=attention_mask, return_attention_masks=attention_mask, model_options=model_options) - -class Qwen3_8BModel(sd1_clip.SDClipModel): - def __init__(self, device="cpu", layer=[9, 18, 27], layer_idx=None, dtype=None, attention_mask=True, model_options={}): - super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config={}, dtype=dtype, special_tokens={"pad": 151643}, layer_norm_hidden_state=False, model_class=comfy.text_encoders.llama.Qwen3_8B, enable_attention_masks=attention_mask, return_attention_masks=attention_mask, model_options=model_options) - -def klein_te(dtype_llama=None, llama_quantization_metadata=None, model_type="qwen3_4b"): - if model_type == "qwen3_4b": - model = Qwen3_4BModel - elif model_type == "qwen3_8b": - model = Qwen3_8BModel - - class Flux2TEModel_(Flux2TEModel): - def __init__(self, device="cpu", dtype=None, model_options={}): - if llama_quantization_metadata is not None: - model_options = model_options.copy() - model_options["quantization_metadata"] = llama_quantization_metadata - if dtype_llama is not None: - dtype = dtype_llama - super().__init__(device=device, dtype=dtype, name=model_type, model_options=model_options, clip_model=model) - return Flux2TEModel_ diff --git a/comfy/text_encoders/genmo.py b/comfy/text_encoders/genmo.py index 2d7a3fbce80a..5daea81355c1 100644 --- a/comfy/text_encoders/genmo.py +++ b/comfy/text_encoders/genmo.py @@ -32,7 +32,7 @@ def __init__(self, device="cpu", dtype=None, model_options={}): if t5_quantization_metadata is not None: model_options = model_options.copy() model_options["t5xxl_quantization_metadata"] = t5_quantization_metadata - if dtype_t5 is not None: + if dtype is None: dtype = dtype_t5 super().__init__(device=device, dtype=dtype, model_options=model_options) return MochiTEModel_ diff --git a/comfy/text_encoders/llama.py b/comfy/text_encoders/llama.py index 331a30f610a3..76731576b461 100644 --- a/comfy/text_encoders/llama.py +++ b/comfy/text_encoders/llama.py @@ -99,28 +99,6 @@ class Qwen3_4BConfig: rope_scale = None final_norm: bool = True -@dataclass -class Qwen3_8BConfig: - vocab_size: int = 151936 - hidden_size: int = 4096 - intermediate_size: int = 12288 - num_hidden_layers: int = 36 - num_attention_heads: int = 32 - num_key_value_heads: int = 8 - max_position_embeddings: int = 40960 - rms_norm_eps: float = 1e-6 - rope_theta: float = 1000000.0 - transformer_type: str = "llama" - head_dim = 128 - rms_norm_add = False - mlp_activation = "silu" - qkv_bias = False - rope_dims = None - q_norm = "gemma3" - k_norm = "gemma3" - rope_scale = None - final_norm: bool = True - @dataclass class Ovis25_2BConfig: vocab_size: int = 151936 @@ -650,15 +628,6 @@ def __init__(self, config_dict, dtype, device, operations): self.model = Llama2_(config, device=device, dtype=dtype, ops=operations) self.dtype = dtype -class Qwen3_8B(BaseLlama, torch.nn.Module): - def __init__(self, config_dict, dtype, device, operations): - super().__init__() - config = Qwen3_8BConfig(**config_dict) - self.num_layers = config.num_hidden_layers - - self.model = Llama2_(config, device=device, dtype=dtype, ops=operations) - self.dtype = dtype - class Ovis25_2B(BaseLlama, torch.nn.Module): def __init__(self, config_dict, dtype, device, operations): super().__init__() diff --git a/comfy/text_encoders/lt.py b/comfy/text_encoders/lt.py index c33c77db7ff4..776e25e975c2 100644 --- a/comfy/text_encoders/lt.py +++ b/comfy/text_encoders/lt.py @@ -118,9 +118,8 @@ def load_sd(self, sd): sdo = comfy.utils.state_dict_prefix_replace(sd, {"text_embedding_projection.aggregate_embed.weight": "text_embedding_projection.weight", "model.diffusion_model.video_embeddings_connector.": "video_embeddings_connector.", "model.diffusion_model.audio_embeddings_connector.": "audio_embeddings_connector."}, filter_keys=True) if len(sdo) == 0: sdo = sd - missing, unexpected = self.load_state_dict(sdo, strict=False) - missing = [k for k in missing if not k.startswith("gemma3_12b.")] # filter out keys that belong to the main gemma model - return (missing, unexpected) + + return self.load_state_dict(sdo, strict=False) def memory_estimation_function(self, token_weight_pairs, device=None): constant = 6.0 diff --git a/comfy/text_encoders/pixart_t5.py b/comfy/text_encoders/pixart_t5.py index 51c6e50c7be4..e5e5f18bed0c 100644 --- a/comfy/text_encoders/pixart_t5.py +++ b/comfy/text_encoders/pixart_t5.py @@ -36,7 +36,7 @@ def __init__(self, device="cpu", dtype=None, model_options={}): if t5_quantization_metadata is not None: model_options = model_options.copy() model_options["t5xxl_quantization_metadata"] = t5_quantization_metadata - if dtype_t5 is not None: + if dtype is None: dtype = dtype_t5 super().__init__(device=device, dtype=dtype, model_options=model_options) return PixArtTEModel_ diff --git a/comfy/utils.py b/comfy/utils.py index 2e33a42587ee..ffa98c9b1b81 100644 --- a/comfy/utils.py +++ b/comfy/utils.py @@ -30,7 +30,6 @@ from einops import rearrange from comfy.cli_args import args import json -import time MMAP_TORCH_FILES = args.mmap_torch_files DISABLE_MMAP = args.disable_mmap @@ -929,9 +928,7 @@ def generate_bilinear_data(length_old, length_new, device): return result.to(orig_dtype) def lanczos(samples, width, height): - #the below API is strict and expects grayscale to be squeezed - samples = samples.squeeze(1) if samples.shape[1] == 1 else samples.movedim(1, -1) - images = [Image.fromarray(np.clip(255. * image.cpu().numpy(), 0, 255).astype(np.uint8)) for image in samples] + images = [Image.fromarray(np.clip(255. * image.movedim(0, -1).cpu().numpy(), 0, 255).astype(np.uint8)) for image in samples] images = [image.resize((width, height), resample=Image.Resampling.LANCZOS) for image in images] images = [torch.from_numpy(np.array(image).astype(np.float32) / 255.0).movedim(-1, 0) for image in images] result = torch.stack(images) @@ -1100,10 +1097,6 @@ def set_progress_bar_global_hook(function): global PROGRESS_BAR_HOOK PROGRESS_BAR_HOOK = function -# Throttle settings for progress bar updates to reduce WebSocket flooding -PROGRESS_THROTTLE_MIN_INTERVAL = 0.1 # 100ms minimum between updates -PROGRESS_THROTTLE_MIN_PERCENT = 0.5 # 0.5% minimum progress change - class ProgressBar: def __init__(self, total, node_id=None): global PROGRESS_BAR_HOOK @@ -1111,8 +1104,6 @@ def __init__(self, total, node_id=None): self.current = 0 self.hook = PROGRESS_BAR_HOOK self.node_id = node_id - self._last_update_time = 0.0 - self._last_sent_value = -1 def update_absolute(self, value, total=None, preview=None): if total is not None: @@ -1121,29 +1112,7 @@ def update_absolute(self, value, total=None, preview=None): value = self.total self.current = value if self.hook is not None: - current_time = time.perf_counter() - is_first = (self._last_sent_value < 0) - is_final = (value >= self.total) - has_preview = (preview is not None) - - # Always send immediately for previews, first update, or final update - if has_preview or is_first or is_final: - self.hook(self.current, self.total, preview, node_id=self.node_id) - self._last_update_time = current_time - self._last_sent_value = value - return - - # Apply throttling for regular progress updates - if self.total > 0: - percent_changed = ((value - max(0, self._last_sent_value)) / self.total) * 100 - else: - percent_changed = 100 - time_elapsed = current_time - self._last_update_time - - if time_elapsed >= PROGRESS_THROTTLE_MIN_INTERVAL and percent_changed >= PROGRESS_THROTTLE_MIN_PERCENT: - self.hook(self.current, self.total, preview, node_id=self.node_id) - self._last_update_time = current_time - self._last_sent_value = value + self.hook(self.current, self.total, preview, node_id=self.node_id) def update(self, value): self.update_absolute(self.current + value) diff --git a/comfy_api/latest/_io.py b/comfy_api/latest/_io.py index e6a0d1821e0b..50143ff53516 100644 --- a/comfy_api/latest/_io.py +++ b/comfy_api/latest/_io.py @@ -1225,7 +1225,6 @@ class NodeInfoV1: deprecated: bool=None experimental: bool=None api_node: bool=None - price_badge: dict | None = None @dataclass class NodeInfoV3: @@ -1235,77 +1234,11 @@ class NodeInfoV3: name: str=None display_name: str=None description: str=None - python_module: Any = None category: str=None output_node: bool=None deprecated: bool=None experimental: bool=None api_node: bool=None - price_badge: dict | None = None - - -@dataclass -class PriceBadgeDepends: - widgets: list[str] = field(default_factory=list) - inputs: list[str] = field(default_factory=list) - input_groups: list[str] = field(default_factory=list) - - def validate(self) -> None: - if not isinstance(self.widgets, list) or any(not isinstance(x, str) for x in self.widgets): - raise ValueError("PriceBadgeDepends.widgets must be a list[str].") - if not isinstance(self.inputs, list) or any(not isinstance(x, str) for x in self.inputs): - raise ValueError("PriceBadgeDepends.inputs must be a list[str].") - if not isinstance(self.input_groups, list) or any(not isinstance(x, str) for x in self.input_groups): - raise ValueError("PriceBadgeDepends.input_groups must be a list[str].") - - def as_dict(self, schema_inputs: list["Input"]) -> dict[str, Any]: - # Build lookup: widget_id -> io_type - input_types: dict[str, str] = {} - for inp in schema_inputs: - all_inputs = inp.get_all() - input_types[inp.id] = inp.get_io_type() # First input is always the parent itself - for nested_inp in all_inputs[1:]: - # For DynamicCombo/DynamicSlot, nested inputs are prefixed with parent ID - # to match frontend naming convention (e.g., "should_texture.enable_pbr") - prefixed_id = f"{inp.id}.{nested_inp.id}" - input_types[prefixed_id] = nested_inp.get_io_type() - - # Enrich widgets with type information, raising error for unknown widgets - widgets_data: list[dict[str, str]] = [] - for w in self.widgets: - if w not in input_types: - raise ValueError( - f"PriceBadge depends_on.widgets references unknown widget '{w}'. " - f"Available widgets: {list(input_types.keys())}" - ) - widgets_data.append({"name": w, "type": input_types[w]}) - - return { - "widgets": widgets_data, - "inputs": self.inputs, - "input_groups": self.input_groups, - } - - -@dataclass -class PriceBadge: - expr: str - depends_on: PriceBadgeDepends = field(default_factory=PriceBadgeDepends) - engine: str = field(default="jsonata") - - def validate(self) -> None: - if self.engine != "jsonata": - raise ValueError(f"Unsupported PriceBadge.engine '{self.engine}'. Only 'jsonata' is supported.") - if not isinstance(self.expr, str) or not self.expr.strip(): - raise ValueError("PriceBadge.expr must be a non-empty string.") - self.depends_on.validate() - - def as_dict(self, schema_inputs: list["Input"]) -> dict[str, Any]: - return { - "engine": self.engine, - "depends_on": self.depends_on.as_dict(schema_inputs), - "expr": self.expr, - } @dataclass @@ -1351,8 +1284,6 @@ class Schema: """Flags a node as experimental, informing users that it may change or not work as expected.""" is_api_node: bool=False """Flags a node as an API node. See: https://docs.comfy.org/tutorials/api-nodes/overview.""" - price_badge: PriceBadge | None = None - """Optional client-evaluated pricing badge declaration for this node.""" not_idempotent: bool=False """Flags a node as not idempotent; when True, the node will run and not reuse the cached outputs when identical inputs are provided on a different node in the graph.""" enable_expand: bool=False @@ -1383,8 +1314,6 @@ def validate(self): input.validate() for output in self.outputs: output.validate() - if self.price_badge is not None: - self.price_badge.validate() def finalize(self): """Add hidden based on selected schema options, and give outputs without ids default ids.""" @@ -1458,8 +1387,7 @@ def get_v1_info(self, cls) -> NodeInfoV1: deprecated=self.is_deprecated, experimental=self.is_experimental, api_node=self.is_api_node, - python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes"), - price_badge=self.price_badge.as_dict(self.inputs) if self.price_badge is not None else None, + python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes") ) return info @@ -1491,8 +1419,7 @@ def get_v3_info(self, cls) -> NodeInfoV3: deprecated=self.is_deprecated, experimental=self.is_experimental, api_node=self.is_api_node, - python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes"), - price_badge=self.price_badge.as_dict(self.inputs) if self.price_badge is not None else None, + python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes") ) return info @@ -2044,6 +1971,4 @@ def as_dict(self) -> dict: "add_to_dict_v3", "V3Data", "ImageCompare", - "PriceBadgeDepends", - "PriceBadge", ] diff --git a/comfy_api_nodes/nodes_bfl.py b/comfy_api_nodes/nodes_bfl.py index 76021ef7fc93..ce077d6b3c04 100644 --- a/comfy_api_nodes/nodes_bfl.py +++ b/comfy_api_nodes/nodes_bfl.py @@ -97,9 +97,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.06}""", - ), ) @classmethod @@ -355,9 +352,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.05}""", - ), ) @classmethod @@ -464,9 +458,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.05}""", - ), ) @classmethod @@ -520,21 +511,6 @@ class Flux2ProImageNode(IO.ComfyNode): NODE_ID = "Flux2ProImageNode" DISPLAY_NAME = "Flux.2 [pro] Image" API_ENDPOINT = "/proxy/bfl/flux-2-pro/generate" - PRICE_BADGE_EXPR = """ - ( - $MP := 1024 * 1024; - $outMP := $max([1, $floor(((widgets.width * widgets.height) + $MP - 1) / $MP)]); - $outputCost := 0.03 + 0.015 * ($outMP - 1); - inputs.images.connected - ? { - "type":"range_usd", - "min_usd": $outputCost + 0.015, - "max_usd": $outputCost + 0.12, - "format": { "approximate": true } - } - : {"type":"usd","usd": $outputCost} - ) - """ @classmethod def define_schema(cls) -> IO.Schema: @@ -587,10 +563,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["width", "height"], inputs=["images"]), - expr=cls.PRICE_BADGE_EXPR, - ), ) @classmethod @@ -651,22 +623,6 @@ class Flux2MaxImageNode(Flux2ProImageNode): NODE_ID = "Flux2MaxImageNode" DISPLAY_NAME = "Flux.2 [max] Image" API_ENDPOINT = "/proxy/bfl/flux-2-max/generate" - PRICE_BADGE_EXPR = """ - ( - $MP := 1024 * 1024; - $outMP := $max([1, $floor(((widgets.width * widgets.height) + $MP - 1) / $MP)]); - $outputCost := 0.07 + 0.03 * ($outMP - 1); - - inputs.images.connected - ? { - "type":"range_usd", - "min_usd": $outputCost + 0.03, - "max_usd": $outputCost + 0.24, - "format": { "approximate": true } - } - : {"type":"usd","usd": $outputCost} - ) - """ class BFLExtension(ComfyExtension): diff --git a/comfy_api_nodes/nodes_bytedance.py b/comfy_api_nodes/nodes_bytedance.py index f09a4a0ed5f4..d4a2cfae681a 100644 --- a/comfy_api_nodes/nodes_bytedance.py +++ b/comfy_api_nodes/nodes_bytedance.py @@ -126,9 +126,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.03}""", - ), ) @classmethod @@ -370,19 +367,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $price := $contains(widgets.model, "seedream-4-5-251128") ? 0.04 : 0.03; - { - "type":"usd", - "usd": $price, - "format": { "suffix":" x images/Run", "approximate": true } - } - ) - """, - ), ) @classmethod @@ -538,7 +522,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -649,7 +632,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -772,7 +754,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -896,7 +877,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -966,52 +946,6 @@ def raise_if_text_params(prompt: str, text_params: list[str]) -> None: ) -PRICE_BADGE_VIDEO = IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]), - expr=""" - ( - $priceByModel := { - "seedance-1-0-pro": { - "480p":[0.23,0.24], - "720p":[0.51,0.56], - "1080p":[1.18,1.22] - }, - "seedance-1-0-pro-fast": { - "480p":[0.09,0.1], - "720p":[0.21,0.23], - "1080p":[0.47,0.49] - }, - "seedance-1-0-lite": { - "480p":[0.17,0.18], - "720p":[0.37,0.41], - "1080p":[0.85,0.88] - } - }; - $model := widgets.model; - $modelKey := - $contains($model, "seedance-1-0-pro-fast") ? "seedance-1-0-pro-fast" : - $contains($model, "seedance-1-0-pro") ? "seedance-1-0-pro" : - "seedance-1-0-lite"; - $resolution := widgets.resolution; - $resKey := - $contains($resolution, "1080") ? "1080p" : - $contains($resolution, "720") ? "720p" : - "480p"; - $modelPrices := $lookup($priceByModel, $modelKey); - $baseRange := $lookup($modelPrices, $resKey); - $min10s := $baseRange[0]; - $max10s := $baseRange[1]; - $scale := widgets.duration / 10; - $minCost := $min10s * $scale; - $maxCost := $max10s * $scale; - ($minCost = $maxCost) - ? {"type":"usd","usd": $minCost} - : {"type":"range_usd","min_usd": $minCost, "max_usd": $maxCost} - ) - """, -) - - class ByteDanceExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: diff --git a/comfy_api_nodes/nodes_gemini.py b/comfy_api_nodes/nodes_gemini.py index a2daea50a1d4..e8ed7e797218 100644 --- a/comfy_api_nodes/nodes_gemini.py +++ b/comfy_api_nodes/nodes_gemini.py @@ -130,7 +130,7 @@ def get_parts_by_type(response: GeminiGenerateContentResponse, part_type: Litera Returns: List of response parts matching the requested type. """ - if not response.candidates: + if response.candidates is None: if response.promptFeedback and response.promptFeedback.blockReason: feedback = response.promptFeedback raise ValueError( @@ -141,24 +141,14 @@ def get_parts_by_type(response: GeminiGenerateContentResponse, part_type: Litera "try changing it to `IMAGE+TEXT` to view the model's reasoning and understand why image generation failed." ) parts = [] - blocked_reasons = [] - for candidate in response.candidates: - if candidate.finishReason and candidate.finishReason.upper() == "IMAGE_PROHIBITED_CONTENT": - blocked_reasons.append(candidate.finishReason) - continue - if candidate.content is None or candidate.content.parts is None: - continue - for part in candidate.content.parts: - if part_type == "text" and part.text: - parts.append(part) - elif part.inlineData and part.inlineData.mimeType == part_type: - parts.append(part) - elif part.fileData and part.fileData.mimeType == part_type: - parts.append(part) - - if not parts and blocked_reasons: - raise ValueError(f"Gemini API blocked the request. Reasons: {blocked_reasons}") - + for part in response.candidates[0].content.parts: + if part_type == "text" and part.text: + parts.append(part) + elif part.inlineData and part.inlineData.mimeType == part_type: + parts.append(part) + elif part.fileData and part.fileData.mimeType == part_type: + parts.append(part) + # Skip parts that don't match the requested type return parts @@ -319,30 +309,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $m := widgets.model; - $contains($m, "gemini-2.5-flash") ? { - "type": "list_usd", - "usd": [0.0003, 0.0025], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens"} - } - : $contains($m, "gemini-2.5-pro") ? { - "type": "list_usd", - "usd": [0.00125, 0.01], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gemini-3-pro-preview") ? { - "type": "list_usd", - "usd": [0.002, 0.012], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : {"type":"text", "text":"Token-based"} - ) - """, - ), ) @classmethod @@ -604,9 +570,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.039,"format":{"suffix":"/Image (1K)","approximate":true}}""", - ), ) @classmethod @@ -737,19 +700,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["resolution"]), - expr=""" - ( - $r := widgets.resolution; - ($contains($r,"1k") or $contains($r,"2k")) - ? {"type":"usd","usd":0.134,"format":{"suffix":"/Image","approximate":true}} - : $contains($r,"4k") - ? {"type":"usd","usd":0.24,"format":{"suffix":"/Image","approximate":true}} - : {"type":"text","text":"Token-based"} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_ideogram.py b/comfy_api_nodes/nodes_ideogram.py index 827b3523a6f0..48f94e612b92 100644 --- a/comfy_api_nodes/nodes_ideogram.py +++ b/comfy_api_nodes/nodes_ideogram.py @@ -236,6 +236,7 @@ def define_schema(cls): display_name="Ideogram V1", category="api node/image/Ideogram", description="Generates images using the Ideogram V1 model.", + is_api_node=True, inputs=[ IO.String.Input( "prompt", @@ -297,17 +298,6 @@ def define_schema(cls): IO.Hidden.api_key_comfy_org, IO.Hidden.unique_id, ], - is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["num_images", "turbo"]), - expr=""" - ( - $n := widgets.num_images; - $base := (widgets.turbo = true) ? 0.0286 : 0.0858; - {"type":"usd","usd": $round($base * $n, 2)} - ) - """, - ), ) @classmethod @@ -361,6 +351,7 @@ def define_schema(cls): display_name="Ideogram V2", category="api node/image/Ideogram", description="Generates images using the Ideogram V2 model.", + is_api_node=True, inputs=[ IO.String.Input( "prompt", @@ -445,17 +436,6 @@ def define_schema(cls): IO.Hidden.api_key_comfy_org, IO.Hidden.unique_id, ], - is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["num_images", "turbo"]), - expr=""" - ( - $n := widgets.num_images; - $base := (widgets.turbo = true) ? 0.0715 : 0.1144; - {"type":"usd","usd": $round($base * $n, 2)} - ) - """, - ), ) @classmethod @@ -526,6 +506,7 @@ def define_schema(cls): category="api node/image/Ideogram", description="Generates images using the Ideogram V3 model. " "Supports both regular image generation from text prompts and image editing with mask.", + is_api_node=True, inputs=[ IO.String.Input( "prompt", @@ -610,23 +591,6 @@ def define_schema(cls): IO.Hidden.api_key_comfy_org, IO.Hidden.unique_id, ], - is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["rendering_speed", "num_images"], inputs=["character_image"]), - expr=""" - ( - $n := widgets.num_images; - $speed := widgets.rendering_speed; - $hasChar := inputs.character_image.connected; - $base := - $contains($speed,"quality") ? ($hasChar ? 0.286 : 0.1287) : - $contains($speed,"default") ? ($hasChar ? 0.2145 : 0.0858) : - $contains($speed,"turbo") ? ($hasChar ? 0.143 : 0.0429) : - 0.0858; - {"type":"usd","usd": $round($base * $n, 2)} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_kling.py b/comfy_api_nodes/nodes_kling.py index 05dde88b1052..9c707a339f60 100644 --- a/comfy_api_nodes/nodes_kling.py +++ b/comfy_api_nodes/nodes_kling.py @@ -567,7 +567,7 @@ async def execute_lipsync( # Upload the audio file to Comfy API and get download URL if audio: audio_url = await upload_audio_to_comfyapi( - cls, audio, container_format="mp3", codec_name="libmp3lame", mime_type="audio/mpeg" + cls, audio, container_format="mp3", codec_name="libmp3lame", mime_type="audio/mpeg", filename="output.mp3" ) logging.info("Uploaded audio to Comfy API. URL: %s", audio_url) else: @@ -764,33 +764,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["mode"]), - expr=""" - ( - $m := widgets.mode; - $contains($m,"v2-5-turbo") - ? ($contains($m,"10") ? {"type":"usd","usd":0.7} : {"type":"usd","usd":0.35}) - : $contains($m,"v2-1-master") - ? ($contains($m,"10s") ? {"type":"usd","usd":2.8} : {"type":"usd","usd":1.4}) - : $contains($m,"v2-master") - ? ($contains($m,"10s") ? {"type":"usd","usd":2.8} : {"type":"usd","usd":1.4}) - : $contains($m,"v1-6") - ? ( - $contains($m,"pro") - ? ($contains($m,"10s") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($m,"10s") ? {"type":"usd","usd":0.56} : {"type":"usd","usd":0.28}) - ) - : $contains($m,"v1") - ? ( - $contains($m,"pro") - ? ($contains($m,"10s") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($m,"10s") ? {"type":"usd","usd":0.28} : {"type":"usd","usd":0.14}) - ) - : {"type":"usd","usd":0.14} - ) - """, - ), ) @classmethod @@ -845,16 +818,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $mode := (widgets.resolution = "720p") ? "std" : "pro"; - $rates := {"std": 0.084, "pro": 0.112}; - {"type":"usd","usd": $lookup($rates, $mode) * widgets.duration} - ) - """, - ), ) @classmethod @@ -923,16 +886,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $mode := (widgets.resolution = "720p") ? "std" : "pro"; - $rates := {"std": 0.084, "pro": 0.112}; - {"type":"usd","usd": $lookup($rates, $mode) * widgets.duration} - ) - """, - ), ) @classmethod @@ -1028,16 +981,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $mode := (widgets.resolution = "720p") ? "std" : "pro"; - $rates := {"std": 0.084, "pro": 0.112}; - {"type":"usd","usd": $lookup($rates, $mode) * widgets.duration} - ) - """, - ), ) @classmethod @@ -1113,16 +1056,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $mode := (widgets.resolution = "720p") ? "std" : "pro"; - $rates := {"std": 0.126, "pro": 0.168}; - {"type":"usd","usd": $lookup($rates, $mode) * widgets.duration} - ) - """, - ), ) @classmethod @@ -1209,16 +1142,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["resolution"]), - expr=""" - ( - $mode := (widgets.resolution = "720p") ? "std" : "pro"; - $rates := {"std": 0.126, "pro": 0.168}; - {"type":"usd","usd": $lookup($rates, $mode), "format":{"suffix":"/second"}} - ) - """, - ), ) @classmethod @@ -1305,9 +1228,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.028}""", - ), ) @classmethod @@ -1393,9 +1313,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.14}""", - ), ) @classmethod @@ -1458,33 +1375,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["mode", "model_name", "duration"]), - expr=""" - ( - $mode := widgets.mode; - $model := widgets.model_name; - $dur := widgets.duration; - $contains($model,"v2-5-turbo") - ? ($contains($dur,"10") ? {"type":"usd","usd":0.7} : {"type":"usd","usd":0.35}) - : ($contains($model,"v2-1-master") or $contains($model,"v2-master")) - ? ($contains($dur,"10") ? {"type":"usd","usd":2.8} : {"type":"usd","usd":1.4}) - : ($contains($model,"v2-1") or $contains($model,"v1-6") or $contains($model,"v1-5")) - ? ( - $contains($mode,"pro") - ? ($contains($dur,"10") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($dur,"10") ? {"type":"usd","usd":0.56} : {"type":"usd","usd":0.28}) - ) - : $contains($model,"v1") - ? ( - $contains($mode,"pro") - ? ($contains($dur,"10") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($dur,"10") ? {"type":"usd","usd":0.28} : {"type":"usd","usd":0.14}) - ) - : {"type":"usd","usd":0.14} - ) - """, - ), ) @classmethod @@ -1558,9 +1448,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.49}""", - ), ) @classmethod @@ -1631,33 +1518,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["mode"]), - expr=""" - ( - $m := widgets.mode; - $contains($m,"v2-5-turbo") - ? ($contains($m,"10") ? {"type":"usd","usd":0.7} : {"type":"usd","usd":0.35}) - : $contains($m,"v2-1") - ? ($contains($m,"10s") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : $contains($m,"v2-master") - ? ($contains($m,"10s") ? {"type":"usd","usd":2.8} : {"type":"usd","usd":1.4}) - : $contains($m,"v1-6") - ? ( - $contains($m,"pro") - ? ($contains($m,"10s") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($m,"10s") ? {"type":"usd","usd":0.56} : {"type":"usd","usd":0.28}) - ) - : $contains($m,"v1") - ? ( - $contains($m,"pro") - ? ($contains($m,"10s") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($m,"10s") ? {"type":"usd","usd":0.28} : {"type":"usd","usd":0.14}) - ) - : {"type":"usd","usd":0.14} - ) - """, - ), ) @classmethod @@ -1723,9 +1583,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.28}""", - ), ) @classmethod @@ -1807,29 +1664,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["mode", "model_name", "duration"]), - expr=""" - ( - $mode := widgets.mode; - $model := widgets.model_name; - $dur := widgets.duration; - ($contains($model,"v1-6") or $contains($model,"v1-5")) - ? ( - $contains($mode,"pro") - ? ($contains($dur,"10") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($dur,"10") ? {"type":"usd","usd":0.56} : {"type":"usd","usd":0.28}) - ) - : $contains($model,"v1") - ? ( - $contains($mode,"pro") - ? ($contains($dur,"10") ? {"type":"usd","usd":0.98} : {"type":"usd","usd":0.49}) - : ($contains($dur,"10") ? {"type":"usd","usd":0.28} : {"type":"usd","usd":0.14}) - ) - : {"type":"usd","usd":0.14} - ) - """, - ), ) @classmethod @@ -1894,16 +1728,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["effect_scene"]), - expr=""" - ( - ($contains(widgets.effect_scene,"dizzydizzy") or $contains(widgets.effect_scene,"bloombloom")) - ? {"type":"usd","usd":0.49} - : {"type":"usd","usd":0.28} - ) - """, - ), ) @classmethod @@ -1958,9 +1782,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.1,"format":{"approximate":true}}""", - ), ) @classmethod @@ -2021,9 +1842,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.1,"format":{"approximate":true}}""", - ), ) @classmethod @@ -2074,9 +1892,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.7}""", - ), ) @classmethod @@ -2176,19 +1991,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model_name", "n"], inputs=["image"]), - expr=""" - ( - $m := widgets.model_name; - $base := - $contains($m,"kling-v1-5") - ? (inputs.image.connected ? 0.028 : 0.014) - : ($contains($m,"kling-v1") ? 0.0035 : 0.014); - {"type":"usd","usd": $base * widgets.n} - ) - """, - ), ) @classmethod @@ -2272,10 +2074,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "generate_audio"]), - expr="""{"type":"usd","usd": 0.07 * widgets.duration * (widgets.generate_audio ? 2 : 1)}""", - ), ) @classmethod @@ -2340,10 +2138,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "generate_audio"]), - expr="""{"type":"usd","usd": 0.07 * widgets.duration * (widgets.generate_audio ? 2 : 1)}""", - ), ) @classmethod @@ -2424,15 +2218,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["mode"]), - expr=""" - ( - $prices := {"std": 0.07, "pro": 0.112}; - {"type":"usd","usd": $lookup($prices, widgets.mode), "format":{"suffix":"/second"}} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_ltxv.py b/comfy_api_nodes/nodes_ltxv.py index c6424af92d71..7e61560dc71e 100644 --- a/comfy_api_nodes/nodes_ltxv.py +++ b/comfy_api_nodes/nodes_ltxv.py @@ -28,22 +28,6 @@ class ExecuteTaskRequest(BaseModel): image_uri: str | None = Field(None) -PRICE_BADGE = IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]), - expr=""" - ( - $prices := { - "ltx-2 (pro)": {"1920x1080":0.06,"2560x1440":0.12,"3840x2160":0.24}, - "ltx-2 (fast)": {"1920x1080":0.04,"2560x1440":0.08,"3840x2160":0.16} - }; - $modelPrices := $lookup($prices, $lowercase(widgets.model)); - $pps := $lookup($modelPrices, widgets.resolution); - {"type":"usd","usd": $pps * widgets.duration} - ) - """, -) - - class TextToVideoNode(IO.ComfyNode): @classmethod def define_schema(cls): @@ -85,7 +69,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE, ) @classmethod @@ -162,7 +145,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE, ) @classmethod diff --git a/comfy_api_nodes/nodes_luma.py b/comfy_api_nodes/nodes_luma.py index 95cb442e5550..894f2b08c994 100644 --- a/comfy_api_nodes/nodes_luma.py +++ b/comfy_api_nodes/nodes_luma.py @@ -189,19 +189,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $m := widgets.model; - $contains($m,"photon-flash-1") - ? {"type":"usd","usd":0.0027} - : $contains($m,"photon-1") - ? {"type":"usd","usd":0.0104} - : {"type":"usd","usd":0.0246} - ) - """, - ), ) @classmethod @@ -316,19 +303,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $m := widgets.model; - $contains($m,"photon-flash-1") - ? {"type":"usd","usd":0.0027} - : $contains($m,"photon-1") - ? {"type":"usd","usd":0.0104} - : {"type":"usd","usd":0.0246} - ) - """, - ), ) @classmethod @@ -421,7 +395,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -532,8 +505,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, - ) @classmethod @@ -597,53 +568,6 @@ async def _convert_to_keyframes( return LumaKeyframes(frame0=frame0, frame1=frame1) -PRICE_BADGE_VIDEO = IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "resolution", "duration"]), - expr=""" - ( - $p := { - "ray-flash-2": { - "5s": {"4k":3.13,"1080p":0.79,"720p":0.34,"540p":0.2}, - "9s": {"4k":5.65,"1080p":1.42,"720p":0.61,"540p":0.36} - }, - "ray-2": { - "5s": {"4k":9.11,"1080p":2.27,"720p":1.02,"540p":0.57}, - "9s": {"4k":16.4,"1080p":4.1,"720p":1.83,"540p":1.03} - } - }; - - $m := widgets.model; - $d := widgets.duration; - $r := widgets.resolution; - - $modelKey := - $contains($m,"ray-flash-2") ? "ray-flash-2" : - $contains($m,"ray-2") ? "ray-2" : - $contains($m,"ray-1-6") ? "ray-1-6" : - "other"; - - $durKey := $contains($d,"5s") ? "5s" : $contains($d,"9s") ? "9s" : ""; - $resKey := - $contains($r,"4k") ? "4k" : - $contains($r,"1080p") ? "1080p" : - $contains($r,"720p") ? "720p" : - $contains($r,"540p") ? "540p" : ""; - - $modelPrices := $lookup($p, $modelKey); - $durPrices := $lookup($modelPrices, $durKey); - $v := $lookup($durPrices, $resKey); - - $price := - ($modelKey = "ray-1-6") ? 0.5 : - ($modelKey = "other") ? 0.79 : - ($exists($v) ? $v : 0.79); - - {"type":"usd","usd": $price} - ) - """, -) - - class LumaExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: diff --git a/comfy_api_nodes/nodes_minimax.py b/comfy_api_nodes/nodes_minimax.py index 43a15d50dec6..05cbb700f6e0 100644 --- a/comfy_api_nodes/nodes_minimax.py +++ b/comfy_api_nodes/nodes_minimax.py @@ -134,9 +134,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.43}""", - ), ) @classmethod @@ -200,9 +197,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.43}""", - ), ) @classmethod @@ -346,20 +340,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["resolution", "duration"]), - expr=""" - ( - $prices := { - "768p": {"6": 0.28, "10": 0.56}, - "1080p": {"6": 0.49} - }; - $resPrices := $lookup($prices, $lowercase(widgets.resolution)); - $price := $lookup($resPrices, $string(widgets.duration)); - {"type":"usd","usd": $price ? $price : 0.43} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_moonvalley.py b/comfy_api_nodes/nodes_moonvalley.py index 769b171b71e2..2771e47901f7 100644 --- a/comfy_api_nodes/nodes_moonvalley.py +++ b/comfy_api_nodes/nodes_moonvalley.py @@ -233,10 +233,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(), - expr="""{"type":"usd","usd": 1.5}""", - ), ) @classmethod @@ -355,10 +351,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(), - expr="""{"type":"usd","usd": 2.25}""", - ), ) @classmethod @@ -479,10 +471,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(), - expr="""{"type":"usd","usd": 1.5}""", - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_openai.py b/comfy_api_nodes/nodes_openai.py index 2f144c5c3ac0..a6205a34ff80 100644 --- a/comfy_api_nodes/nodes_openai.py +++ b/comfy_api_nodes/nodes_openai.py @@ -160,23 +160,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["size", "n"]), - expr=""" - ( - $size := widgets.size; - $nRaw := widgets.n; - $n := ($nRaw != null and $nRaw != 0) ? $nRaw : 1; - - $base := - $contains($size, "256x256") ? 0.016 : - $contains($size, "512x512") ? 0.018 : - 0.02; - - {"type":"usd","usd": $round($base * $n, 3)} - ) - """, - ), ) @classmethod @@ -304,25 +287,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["size", "quality"]), - expr=""" - ( - $size := widgets.size; - $q := widgets.quality; - $hd := $contains($q, "hd"); - - $price := - $contains($size, "1024x1024") - ? ($hd ? 0.08 : 0.04) - : (($contains($size, "1792x1024") or $contains($size, "1024x1792")) - ? ($hd ? 0.12 : 0.08) - : 0.04); - - {"type":"usd","usd": $price} - ) - """, - ), ) @classmethod @@ -447,28 +411,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["quality", "n"]), - expr=""" - ( - $ranges := { - "low": [0.011, 0.02], - "medium": [0.046, 0.07], - "high": [0.167, 0.3] - }; - $range := $lookup($ranges, widgets.quality); - $n := widgets.n; - ($n = 1) - ? {"type":"range_usd","min_usd": $range[0], "max_usd": $range[1]} - : { - "type":"range_usd", - "min_usd": $range[0], - "max_usd": $range[1], - "format": { "suffix": " x " & $string($n) & "/Run" } - } - ) - """, - ), ) @classmethod @@ -624,75 +566,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $m := widgets.model; - $contains($m, "o4-mini") ? { - "type": "list_usd", - "usd": [0.0011, 0.0044], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "o1-pro") ? { - "type": "list_usd", - "usd": [0.15, 0.6], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "o1") ? { - "type": "list_usd", - "usd": [0.015, 0.06], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "o3-mini") ? { - "type": "list_usd", - "usd": [0.0011, 0.0044], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "o3") ? { - "type": "list_usd", - "usd": [0.01, 0.04], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-4o") ? { - "type": "list_usd", - "usd": [0.0025, 0.01], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-4.1-nano") ? { - "type": "list_usd", - "usd": [0.0001, 0.0004], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-4.1-mini") ? { - "type": "list_usd", - "usd": [0.0004, 0.0016], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-4.1") ? { - "type": "list_usd", - "usd": [0.002, 0.008], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-5-nano") ? { - "type": "list_usd", - "usd": [0.00005, 0.0004], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-5-mini") ? { - "type": "list_usd", - "usd": [0.00025, 0.002], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : $contains($m, "gpt-5") ? { - "type": "list_usd", - "usd": [0.00125, 0.01], - "format": { "approximate": true, "separator": "-", "suffix": " per 1K tokens" } - } - : {"type": "text", "text": "Token-based"} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_pixverse.py b/comfy_api_nodes/nodes_pixverse.py index 86ddb3ab98d0..6e1686af0248 100644 --- a/comfy_api_nodes/nodes_pixverse.py +++ b/comfy_api_nodes/nodes_pixverse.py @@ -128,7 +128,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -243,7 +242,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -357,7 +355,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=PRICE_BADGE_VIDEO, ) @classmethod @@ -419,33 +416,6 @@ async def execute( return IO.NodeOutput(await download_url_to_video_output(response_poll.Resp.url)) -PRICE_BADGE_VIDEO = IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration_seconds", "quality", "motion_mode"]), - expr=""" - ( - $prices := { - "5": { - "1080p": {"normal": 1.2, "fast": 1.2}, - "720p": {"normal": 0.6, "fast": 1.2}, - "540p": {"normal": 0.45, "fast": 0.9}, - "360p": {"normal": 0.45, "fast": 0.9} - }, - "8": { - "1080p": {"normal": 1.2, "fast": 1.2}, - "720p": {"normal": 1.2, "fast": 1.2}, - "540p": {"normal": 0.9, "fast": 1.2}, - "360p": {"normal": 0.9, "fast": 1.2} - } - }; - $durPrices := $lookup($prices, $string(widgets.duration_seconds)); - $qualityPrices := $lookup($durPrices, widgets.quality); - $price := $lookup($qualityPrices, widgets.motion_mode); - {"type":"usd","usd": $price ? $price : 0.9} - ) - """, -) - - class PixVerseExtension(ComfyExtension): @override async def get_node_list(self) -> list[type[IO.ComfyNode]]: diff --git a/comfy_api_nodes/nodes_recraft.py b/comfy_api_nodes/nodes_recraft.py index 05dc151ad5dd..e3440b94645e 100644 --- a/comfy_api_nodes/nodes_recraft.py +++ b/comfy_api_nodes/nodes_recraft.py @@ -378,10 +378,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["n"]), - expr="""{"type":"usd","usd": $round(0.04 * widgets.n, 2)}""", - ), ) @classmethod @@ -494,10 +490,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["n"]), - expr="""{"type":"usd","usd": $round(0.04 * widgets.n, 2)}""", - ), ) @classmethod @@ -599,10 +591,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["n"]), - expr="""{"type":"usd","usd": $round(0.04 * widgets.n, 2)}""", - ), ) @classmethod @@ -704,10 +692,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["n"]), - expr="""{"type":"usd","usd": $round(0.08 * widgets.n, 2)}""", - ), ) @classmethod @@ -775,10 +759,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(), - expr="""{"type":"usd","usd": 0.01}""", - ), ) @classmethod @@ -837,9 +817,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.04}""", - ), ) @classmethod @@ -906,9 +883,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.01}""", - ), ) @classmethod @@ -955,9 +929,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.004}""", - ), ) @classmethod @@ -1001,9 +972,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.25}""", - ), ) diff --git a/comfy_api_nodes/nodes_rodin.py b/comfy_api_nodes/nodes_rodin.py index b4420cb93998..e60e7a6d67ed 100644 --- a/comfy_api_nodes/nodes_rodin.py +++ b/comfy_api_nodes/nodes_rodin.py @@ -241,9 +241,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -297,9 +294,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -353,9 +347,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -415,9 +406,6 @@ def define_schema(cls) -> IO.Schema: IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_runway.py b/comfy_api_nodes/nodes_runway.py index d19fdb365878..3c55039c99ce 100644 --- a/comfy_api_nodes/nodes_runway.py +++ b/comfy_api_nodes/nodes_runway.py @@ -184,10 +184,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration"]), - expr="""{"type":"usd","usd": 0.0715 * widgets.duration}""", - ), ) @classmethod @@ -278,10 +274,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration"]), - expr="""{"type":"usd","usd": 0.0715 * widgets.duration}""", - ), ) @classmethod @@ -380,10 +372,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration"]), - expr="""{"type":"usd","usd": 0.0715 * widgets.duration}""", - ), ) @classmethod @@ -469,9 +457,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.11}""", - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_sora.py b/comfy_api_nodes/nodes_sora.py index 87e663845ac8..92b225d4043d 100644 --- a/comfy_api_nodes/nodes_sora.py +++ b/comfy_api_nodes/nodes_sora.py @@ -89,24 +89,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "size", "duration"]), - expr=""" - ( - $m := widgets.model; - $size := widgets.size; - $dur := widgets.duration; - $isPro := $contains($m, "sora-2-pro"); - $isSora2 := $contains($m, "sora-2"); - $isProSize := ($size = "1024x1792" or $size = "1792x1024"); - $perSec := - $isPro ? ($isProSize ? 0.5 : 0.3) : - $isSora2 ? 0.1 : - ($isProSize ? 0.5 : 0.1); - {"type":"usd","usd": $round($perSec * $dur, 2)} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_stability.py b/comfy_api_nodes/nodes_stability.py index 5c48c1f1ef61..bb7ceed7836e 100644 --- a/comfy_api_nodes/nodes_stability.py +++ b/comfy_api_nodes/nodes_stability.py @@ -127,9 +127,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.08}""", - ), ) @classmethod @@ -267,16 +264,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model"]), - expr=""" - ( - $contains(widgets.model,"large") - ? {"type":"usd","usd":0.065} - : {"type":"usd","usd":0.035} - ) - """, - ), ) @classmethod @@ -395,9 +382,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.25}""", - ), ) @classmethod @@ -502,9 +486,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.25}""", - ), ) @classmethod @@ -585,9 +566,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.01}""", - ), ) @classmethod @@ -670,9 +648,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.2}""", - ), ) @classmethod @@ -757,9 +732,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.2}""", - ), ) @classmethod @@ -856,9 +828,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.2}""", - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_tripo.py b/comfy_api_nodes/nodes_tripo.py index aa790143dff9..e72f8e96a781 100644 --- a/comfy_api_nodes/nodes_tripo.py +++ b/comfy_api_nodes/nodes_tripo.py @@ -117,38 +117,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends( - widgets=[ - "model_version", - "style", - "texture", - "pbr", - "quad", - "texture_quality", - "geometry_quality", - ], - ), - expr=""" - ( - $isV14 := $contains(widgets.model_version,"v1.4"); - $style := widgets.style; - $hasStyle := ($style != "" and $style != "none"); - $withTexture := widgets.texture or widgets.pbr; - $isHdTexture := (widgets.texture_quality = "detailed"); - $isDetailedGeometry := (widgets.geometry_quality = "detailed"); - $baseCredits := - $isV14 ? 20 : ($withTexture ? 20 : 10); - $credits := - $baseCredits - + ($hasStyle ? 5 : 0) - + (widgets.quad ? 5 : 0) - + ($isHdTexture ? 10 : 0) - + ($isDetailedGeometry ? 20 : 0); - {"type":"usd","usd": $round($credits * 0.01, 2)} - ) - """, - ), ) @classmethod @@ -242,38 +210,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends( - widgets=[ - "model_version", - "style", - "texture", - "pbr", - "quad", - "texture_quality", - "geometry_quality", - ], - ), - expr=""" - ( - $isV14 := $contains(widgets.model_version,"v1.4"); - $style := widgets.style; - $hasStyle := ($style != "" and $style != "none"); - $withTexture := widgets.texture or widgets.pbr; - $isHdTexture := (widgets.texture_quality = "detailed"); - $isDetailedGeometry := (widgets.geometry_quality = "detailed"); - $baseCredits := - $isV14 ? 30 : ($withTexture ? 30 : 20); - $credits := - $baseCredits - + ($hasStyle ? 5 : 0) - + (widgets.quad ? 5 : 0) - + ($isHdTexture ? 10 : 0) - + ($isDetailedGeometry ? 20 : 0); - {"type":"usd","usd": $round($credits * 0.01, 2)} - ) - """, - ), ) @classmethod @@ -378,34 +314,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends( - widgets=[ - "model_version", - "texture", - "pbr", - "quad", - "texture_quality", - "geometry_quality", - ], - ), - expr=""" - ( - $isV14 := $contains(widgets.model_version,"v1.4"); - $withTexture := widgets.texture or widgets.pbr; - $isHdTexture := (widgets.texture_quality = "detailed"); - $isDetailedGeometry := (widgets.geometry_quality = "detailed"); - $baseCredits := - $isV14 ? 30 : ($withTexture ? 30 : 20); - $credits := - $baseCredits - + (widgets.quad ? 5 : 0) - + ($isHdTexture ? 10 : 0) - + ($isDetailedGeometry ? 20 : 0); - {"type":"usd","usd": $round($credits * 0.01, 2)} - ) - """, - ), ) @classmethod @@ -497,15 +405,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["texture_quality"]), - expr=""" - ( - $tq := widgets.texture_quality; - {"type":"usd","usd": ($contains($tq,"detailed") ? 0.2 : 0.1)} - ) - """, - ), ) @classmethod @@ -557,9 +456,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.3}""", - ), ) @classmethod @@ -593,9 +489,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.25}""", - ), ) @classmethod @@ -652,9 +545,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.1}""", - ), ) @classmethod @@ -748,60 +638,6 @@ def define_schema(cls): ], is_api_node=True, is_output_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends( - widgets=[ - "quad", - "face_limit", - "texture_size", - "texture_format", - "force_symmetry", - "flatten_bottom", - "flatten_bottom_threshold", - "pivot_to_center_bottom", - "scale_factor", - "with_animation", - "pack_uv", - "bake", - "part_names", - "fbx_preset", - "export_vertex_colors", - "export_orientation", - "animate_in_place", - ], - ), - expr=""" - ( - $face := (widgets.face_limit != null) ? widgets.face_limit : -1; - $texSize := (widgets.texture_size != null) ? widgets.texture_size : 4096; - $flatThresh := (widgets.flatten_bottom_threshold != null) ? widgets.flatten_bottom_threshold : 0; - $scale := (widgets.scale_factor != null) ? widgets.scale_factor : 1; - $texFmt := (widgets.texture_format != "" ? widgets.texture_format : "jpeg"); - $part := widgets.part_names; - $fbx := (widgets.fbx_preset != "" ? widgets.fbx_preset : "blender"); - $orient := (widgets.export_orientation != "" ? widgets.export_orientation : "default"); - $advanced := - widgets.quad or - widgets.force_symmetry or - widgets.flatten_bottom or - widgets.pivot_to_center_bottom or - widgets.with_animation or - widgets.pack_uv or - widgets.bake or - widgets.export_vertex_colors or - widgets.animate_in_place or - ($face != -1) or - ($texSize != 4096) or - ($flatThresh != 0) or - ($scale != 1) or - ($texFmt != "jpeg") or - ($part != "") or - ($fbx != "blender") or - ($orient != "default"); - {"type":"usd","usd": ($advanced ? 0.1 : 0.05)} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_veo2.py b/comfy_api_nodes/nodes_veo2.py index c14d6ad68b04..13a6bfd91f24 100644 --- a/comfy_api_nodes/nodes_veo2.py +++ b/comfy_api_nodes/nodes_veo2.py @@ -122,10 +122,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration_seconds"]), - expr="""{"type":"usd","usd": 0.5 * widgets.duration_seconds}""", - ), ) @classmethod @@ -351,20 +347,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "generate_audio"]), - expr=""" - ( - $m := widgets.model; - $a := widgets.generate_audio; - ($contains($m,"veo-3.0-fast-generate-001") or $contains($m,"veo-3.1-fast-generate")) - ? {"type":"usd","usd": ($a ? 1.2 : 0.8)} - : ($contains($m,"veo-3.0-generate-001") or $contains($m,"veo-3.1-generate")) - ? {"type":"usd","usd": ($a ? 3.2 : 1.6)} - : {"type":"range_usd","min_usd":0.8,"max_usd":3.2} - ) - """, - ), ) @@ -438,30 +420,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "generate_audio", "duration"]), - expr=""" - ( - $prices := { - "veo-3.1-fast-generate": { "audio": 0.15, "no_audio": 0.10 }, - "veo-3.1-generate": { "audio": 0.40, "no_audio": 0.20 } - }; - $m := widgets.model; - $ga := (widgets.generate_audio = "true"); - $seconds := widgets.duration; - $modelKey := - $contains($m, "veo-3.1-fast-generate") ? "veo-3.1-fast-generate" : - $contains($m, "veo-3.1-generate") ? "veo-3.1-generate" : - ""; - $audioKey := $ga ? "audio" : "no_audio"; - $modelPrices := $lookup($prices, $modelKey); - $pps := $lookup($modelPrices, $audioKey); - ($pps != null) - ? {"type":"usd","usd": $pps * $seconds} - : {"type":"range_usd","min_usd": 0.4, "max_usd": 3.2} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_vidu.py b/comfy_api_nodes/nodes_vidu.py index 8edb02f39ba1..9d94ae7add23 100644 --- a/comfy_api_nodes/nodes_vidu.py +++ b/comfy_api_nodes/nodes_vidu.py @@ -121,9 +121,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -217,9 +214,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -323,9 +317,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -435,9 +426,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.4}""", - ), ) @classmethod @@ -519,17 +507,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $is1080 := widgets.resolution = "1080p"; - $base := $is1080 ? 0.1 : 0.075; - $perSec := $is1080 ? 0.05 : 0.025; - {"type":"usd","usd": $base + $perSec * (widgets.duration - 1)} - ) - """, - ), ) @classmethod @@ -617,39 +594,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]), - expr=""" - ( - $m := widgets.model; - $d := widgets.duration; - $is1080 := widgets.resolution = "1080p"; - $contains($m, "pro-fast") - ? ( - $base := $is1080 ? 0.08 : 0.04; - $perSec := $is1080 ? 0.02 : 0.01; - {"type":"usd","usd": $base + $perSec * ($d - 1)} - ) - : $contains($m, "pro") - ? ( - $base := $is1080 ? 0.275 : 0.075; - $perSec := $is1080 ? 0.075 : 0.05; - {"type":"usd","usd": $base + $perSec * ($d - 1)} - ) - : $contains($m, "turbo") - ? ( - $is1080 - ? {"type":"usd","usd": 0.175 + 0.05 * ($d - 1)} - : ( - $d <= 1 ? {"type":"usd","usd": 0.04} - : $d <= 2 ? {"type":"usd","usd": 0.05} - : {"type":"usd","usd": 0.05 + 0.05 * ($d - 2)} - ) - ) - : {"type":"usd","usd": 0.04} - ) - """, - ), ) @classmethod @@ -754,18 +698,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["audio", "duration", "resolution"]), - expr=""" - ( - $is1080 := widgets.resolution = "1080p"; - $base := $is1080 ? 0.375 : 0.125; - $perSec := $is1080 ? 0.05 : 0.025; - $audioCost := widgets.audio = true ? 0.075 : 0; - {"type":"usd","usd": $base + $perSec * (widgets.duration - 1) + $audioCost} - ) - """, - ), ) @classmethod @@ -872,38 +804,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["model", "duration", "resolution"]), - expr=""" - ( - $m := widgets.model; - $d := widgets.duration; - $is1080 := widgets.resolution = "1080p"; - $contains($m, "pro-fast") - ? ( - $base := $is1080 ? 0.08 : 0.04; - $perSec := $is1080 ? 0.02 : 0.01; - {"type":"usd","usd": $base + $perSec * ($d - 1)} - ) - : $contains($m, "pro") - ? ( - $base := $is1080 ? 0.275 : 0.075; - $perSec := $is1080 ? 0.075 : 0.05; - {"type":"usd","usd": $base + $perSec * ($d - 1)} - ) - : $contains($m, "turbo") - ? ( - $is1080 - ? {"type":"usd","usd": 0.175 + 0.05 * ($d - 1)} - : ( - $d <= 2 ? {"type":"usd","usd": 0.05} - : {"type":"usd","usd": 0.05 + 0.05 * ($d - 2)} - ) - ) - : {"type":"usd","usd": 0.04} - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/nodes_wan.py b/comfy_api_nodes/nodes_wan.py index a1355d4f1876..3e04786a965e 100644 --- a/comfy_api_nodes/nodes_wan.py +++ b/comfy_api_nodes/nodes_wan.py @@ -244,9 +244,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.03}""", - ), ) @classmethod @@ -366,9 +363,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - expr="""{"type":"usd","usd":0.03}""", - ), ) @classmethod @@ -526,17 +520,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "size"]), - expr=""" - ( - $ppsTable := { "480p": 0.05, "720p": 0.1, "1080p": 0.15 }; - $resKey := $substringBefore(widgets.size, ":"); - $pps := $lookup($ppsTable, $resKey); - { "type": "usd", "usd": $round($pps * widgets.duration, 2) } - ) - """, - ), ) @classmethod @@ -698,16 +681,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["duration", "resolution"]), - expr=""" - ( - $ppsTable := { "480p": 0.05, "720p": 0.1, "1080p": 0.15 }; - $pps := $lookup($ppsTable, widgets.resolution); - { "type": "usd", "usd": $round($pps * widgets.duration, 2) } - ) - """, - ), ) @classmethod @@ -855,22 +828,6 @@ def define_schema(cls): IO.Hidden.unique_id, ], is_api_node=True, - price_badge=IO.PriceBadge( - depends_on=IO.PriceBadgeDepends(widgets=["size", "duration"]), - expr=""" - ( - $rate := $contains(widgets.size, "1080p") ? 0.15 : 0.10; - $inputMin := 2 * $rate; - $inputMax := 5 * $rate; - $outputPrice := widgets.duration * $rate; - { - "type": "range_usd", - "min_usd": $inputMin + $outputPrice, - "max_usd": $inputMax + $outputPrice - } - ) - """, - ), ) @classmethod diff --git a/comfy_api_nodes/util/conversions.py b/comfy_api_nodes/util/conversions.py index 99c302a2af07..d64239c86052 100644 --- a/comfy_api_nodes/util/conversions.py +++ b/comfy_api_nodes/util/conversions.py @@ -55,7 +55,7 @@ def image_tensor_pair_to_batch(image1: torch.Tensor, image2: torch.Tensor) -> to def tensor_to_bytesio( image: torch.Tensor, - *, + name: str | None = None, total_pixels: int = 2048 * 2048, mime_type: str = "image/png", ) -> BytesIO: @@ -75,7 +75,7 @@ def tensor_to_bytesio( pil_image = tensor_to_pil(image, total_pixels=total_pixels) img_binary = pil_to_bytesio(pil_image, mime_type=mime_type) - img_binary.name = f"{uuid.uuid4()}.{mimetype_to_extension(mime_type)}" + img_binary.name = f"{name if name else uuid.uuid4()}.{mimetype_to_extension(mime_type)}" return img_binary diff --git a/comfy_api_nodes/util/upload_helpers.py b/comfy_api_nodes/util/upload_helpers.py index 2794be35c717..2535a0884693 100644 --- a/comfy_api_nodes/util/upload_helpers.py +++ b/comfy_api_nodes/util/upload_helpers.py @@ -43,7 +43,7 @@ class UploadResponse(BaseModel): async def upload_images_to_comfyapi( cls: type[IO.ComfyNode], - image: torch.Tensor | list[torch.Tensor], + image: torch.Tensor, *, max_images: int = 8, mime_type: str | None = None, @@ -55,28 +55,15 @@ async def upload_images_to_comfyapi( Uploads images to ComfyUI API and returns download URLs. To upload multiple images, stack them in the batch dimension first. """ - tensors: list[torch.Tensor] = [] - if isinstance(image, list): - for img in image: - is_batch = len(img.shape) > 3 - if is_batch: - tensors.extend(img[i] for i in range(img.shape[0])) - else: - tensors.append(img) - else: - is_batch = len(image.shape) > 3 - if is_batch: - tensors.extend(image[i] for i in range(image.shape[0])) - else: - tensors.append(image) - # if batched, try to upload each file if max_images is greater than 0 download_urls: list[str] = [] - num_to_upload = min(len(tensors), max_images) + is_batch = len(image.shape) > 3 + batch_len = image.shape[0] if is_batch else 1 + num_to_upload = min(batch_len, max_images) batch_start_ts = time.monotonic() for idx in range(num_to_upload): - tensor = tensors[idx] + tensor = image[idx] if is_batch else image img_io = tensor_to_bytesio(tensor, total_pixels=total_pixels, mime_type=mime_type) effective_label = wait_label @@ -95,6 +82,7 @@ async def upload_audio_to_comfyapi( container_format: str = "mp4", codec_name: str = "aac", mime_type: str = "audio/mp4", + filename: str = "uploaded_audio.mp4", ) -> str: """ Uploads a single audio input to ComfyUI API and returns its download URL. @@ -104,7 +92,7 @@ async def upload_audio_to_comfyapi( waveform: torch.Tensor = audio["waveform"] audio_data_np = audio_tensor_to_contiguous_ndarray(waveform) audio_bytes_io = audio_ndarray_to_bytesio(audio_data_np, sample_rate, container_format, codec_name) - return await upload_file_to_comfyapi(cls, audio_bytes_io, f"{uuid.uuid4()}.{container_format}", mime_type) + return await upload_file_to_comfyapi(cls, audio_bytes_io, filename, mime_type) async def upload_video_to_comfyapi( diff --git a/comfy_extras/nodes_model_patch.py b/comfy_extras/nodes_model_patch.py index f66d28fc9214..1355b3c93a53 100644 --- a/comfy_extras/nodes_model_patch.py +++ b/comfy_extras/nodes_model_patch.py @@ -244,10 +244,6 @@ def load_model_patch(self, name): elif 'control_all_x_embedder.2-1.weight' in sd: # alipai z image fun controlnet sd = z_image_convert(sd) config = {} - if 'control_layers.4.adaLN_modulation.0.weight' not in sd: - config['n_control_layers'] = 3 - config['additional_in_dim'] = 17 - config['refiner_control'] = True if 'control_layers.14.adaLN_modulation.0.weight' in sd: config['n_control_layers'] = 15 config['additional_in_dim'] = 17 diff --git a/comfy_extras/nodes_post_processing.py b/comfy_extras/nodes_post_processing.py index 2e559c35c9ff..01afa13a18ee 100644 --- a/comfy_extras/nodes_post_processing.py +++ b/comfy_extras/nodes_post_processing.py @@ -254,7 +254,6 @@ class ResizeType(str, Enum): SCALE_HEIGHT = "scale height" SCALE_TOTAL_PIXELS = "scale total pixels" MATCH_SIZE = "match size" - SCALE_TO_MULTIPLE = "scale to multiple" def is_image(input: torch.Tensor) -> bool: # images have 4 dimensions: [batch, height, width, channels] @@ -329,7 +328,7 @@ def scale_shorter_dimension(input: torch.Tensor, shorter_size: int, scale_method if height < width: width = round((width / height) * shorter_size) height = shorter_size - elif width < height: + elif width > height: height = round((height / width) * shorter_size) width = shorter_size else: @@ -364,43 +363,6 @@ def scale_match_size(input: torch.Tensor, match: torch.Tensor, scale_method: str input = finalize_image_mask_input(input, is_type_image) return input -def scale_to_multiple_cover(input: torch.Tensor, multiple: int, scale_method: str) -> torch.Tensor: - if multiple <= 1: - return input - is_type_image = is_image(input) - if is_type_image: - _, height, width, _ = input.shape - else: - _, height, width = input.shape - target_w = (width // multiple) * multiple - target_h = (height // multiple) * multiple - if target_w == 0 or target_h == 0: - return input - if target_w == width and target_h == height: - return input - s_w = target_w / width - s_h = target_h / height - if s_w >= s_h: - scaled_w = target_w - scaled_h = int(math.ceil(height * s_w)) - if scaled_h < target_h: - scaled_h = target_h - else: - scaled_h = target_h - scaled_w = int(math.ceil(width * s_h)) - if scaled_w < target_w: - scaled_w = target_w - input = init_image_mask_input(input, is_type_image) - input = comfy.utils.common_upscale(input, scaled_w, scaled_h, scale_method, "disabled") - input = finalize_image_mask_input(input, is_type_image) - x0 = (scaled_w - target_w) // 2 - y0 = (scaled_h - target_h) // 2 - x1 = x0 + target_w - y1 = y0 + target_h - if is_type_image: - return input[:, y0:y1, x0:x1, :] - return input[:, y0:y1, x0:x1] - class ResizeImageMaskNode(io.ComfyNode): scale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"] @@ -416,7 +378,6 @@ class ResizeTypedDict(TypedDict): longer_size: int shorter_size: int megapixels: float - multiple: int @classmethod def define_schema(cls): @@ -456,9 +417,6 @@ def define_schema(cls): io.MultiType.Input("match", [io.Image, io.Mask]), crop_combo, ]), - io.DynamicCombo.Option(ResizeType.SCALE_TO_MULTIPLE, [ - io.Int.Input("multiple", default=8, min=1, max=MAX_RESOLUTION, step=1), - ]), ]), io.Combo.Input("scale_method", options=cls.scale_methods, default="area"), ], @@ -484,8 +442,6 @@ def execute(cls, input: io.Image.Type | io.Mask.Type, scale_method: io.Combo.Typ return io.NodeOutput(scale_total_pixels(input, resize_type["megapixels"], scale_method)) elif selected_type == ResizeType.MATCH_SIZE: return io.NodeOutput(scale_match_size(input, resize_type["match"], scale_method, resize_type["crop"])) - elif selected_type == ResizeType.SCALE_TO_MULTIPLE: - return io.NodeOutput(scale_to_multiple_cover(input, resize_type["multiple"], scale_method)) raise ValueError(f"Unsupported resize type: {selected_type}") def batch_images(images: list[torch.Tensor]) -> torch.Tensor | None: diff --git a/comfyui_version.py b/comfyui_version.py index dbb57b4e55a2..df82ed4fc6ba 100644 --- a/comfyui_version.py +++ b/comfyui_version.py @@ -1,3 +1,3 @@ # This file is automatically generated by the build process when version is # updated in pyproject.toml. -__version__ = "0.9.2" +__version__ = "0.8.2" diff --git a/main.py b/main.py index 37b06c1faada..3c7289c8247a 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ from comfy_api import feature_flags + if __name__ == "__main__": #NOTE: These do not do anything on core ComfyUI, they are for custom nodes. os.environ['HF_HUB_DISABLE_TELEMETRY'] = '1' diff --git a/nodes.py b/nodes.py index f19d5fd1c3b6..5a9d42d4a515 100644 --- a/nodes.py +++ b/nodes.py @@ -788,7 +788,6 @@ def INPUT_TYPES(s): #TODO: scale factor? def load_vae(self, vae_name): - metadata = None if vae_name == "pixel_space": sd = {} sd["pixel_space_vae"] = torch.tensor(1.0) @@ -799,8 +798,8 @@ def load_vae(self, vae_name): vae_path = folder_paths.get_full_path_or_raise("vae_approx", vae_name) else: vae_path = folder_paths.get_full_path_or_raise("vae", vae_name) - sd, metadata = comfy.utils.load_torch_file(vae_path, return_metadata=True) - vae = comfy.sd.VAE(sd=sd, metadata=metadata) + sd = comfy.utils.load_torch_file(vae_path) + vae = comfy.sd.VAE(sd=sd) vae.throw_exception_if_invalid() return (vae,) @@ -2401,7 +2400,6 @@ async def init_builtin_api_nodes(): "nodes_sora.py", "nodes_topaz.py", "nodes_tripo.py", - "nodes_meshy.py", "nodes_moonvalley.py", "nodes_rodin.py", "nodes_gemini.py", diff --git a/pyproject.toml b/pyproject.toml index 9ea73da05c3b..49f1a03fd01b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ComfyUI" -version = "0.9.2" +version = "0.8.2" readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.10" diff --git a/requirements.txt b/requirements.txt index 8650d28ec95c..6c1cd86d2484 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -comfyui-frontend-package==1.36.14 -comfyui-workflow-templates==0.8.4 +comfyui-frontend-package==1.36.13 +comfyui-workflow-templates==0.7.69 comfyui-embedded-docs==0.4.0 torch torchsde @@ -21,7 +21,7 @@ psutil alembic SQLAlchemy av>=14.2.0 -comfy-kitchen>=0.2.6 +comfy-kitchen>=0.2.5 #non essential dependencies: kornia>=0.7.1