From 4badc8949029b8aa954a07169218672187f3845e Mon Sep 17 00:00:00 2001 From: Talmaj Marinc Date: Sat, 27 Jun 2026 12:37:31 +0200 Subject: [PATCH 1/5] Add new LoraNodes using DynamicGroup widget. --- comfy_extras/nodes_lora_stack.py | 130 +++++++++++++++++++++++++++++++ nodes.py | 1 + 2 files changed, 131 insertions(+) create mode 100644 comfy_extras/nodes_lora_stack.py diff --git a/comfy_extras/nodes_lora_stack.py b/comfy_extras/nodes_lora_stack.py new file mode 100644 index 000000000..e4bf6c9b3 --- /dev/null +++ b/comfy_extras/nodes_lora_stack.py @@ -0,0 +1,130 @@ +"""LoRA stacking loaders built on io.DynamicGroup. + +Two nodes that let you stack any number of LoRAs in a single node, each row +carrying only a LoRA name and a strength: + + LoadLoraModel + Applies a stack of LoRAs to a diffusion MODEL. + + LoadLoraTextEncoder + Applies a stack of LoRAs to a CLIP text encoder. + +Both are modelled on DynamicGroupLoraStyleTest in nodes_dynamic_group_test.py, +but operate on real models and real LoRA files. +""" + +from __future__ import annotations + +from typing_extensions import override + +import comfy.sd +import comfy.utils +import folder_paths +from comfy_api.latest import ComfyExtension, io + +# Module-level cache so repeated executions don't re-read the same file from disk. +_LORA_CACHE: dict[str, tuple] = {} + + +def _load_lora_file(lora_name: str): + lora_path = folder_paths.get_full_path_or_raise("loras", lora_name) + cached = _LORA_CACHE.get(lora_path) + if cached is not None: + return cached + lora, metadata = comfy.utils.load_torch_file(lora_path, safe_load=True, return_metadata=True) + _LORA_CACHE[lora_path] = (lora, metadata) + return lora, metadata + + +def _lora_template() -> list[io.Input]: + return [ + io.Combo.Input("lora_name", options=folder_paths.get_filename_list("loras"), + tooltip="The name of the LoRA file to apply."), + io.Float.Input("strength", default=1.0, min=-100.0, max=100.0, step=0.01, + tooltip="How strongly to apply this LoRA. 0 = off, negative inverts the effect."), + ] + + +class LoadLoraModel(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="LoadLoraModel", + display_name="Load LoRA (Model)", + search_aliases=["lora", "load lora", "apply lora", "lora model", "lora stack"], + category="model/loaders", + description="Apply a stack of LoRAs to a diffusion model. Add one row per LoRA; " + "each row picks a LoRA file and its strength.", + inputs=[ + io.Model.Input("model", tooltip="The diffusion model the LoRAs will be applied to."), + io.DynamicGroup.Input( + "loras", + template=_lora_template(), + min=1, + max=50, + tooltip="Each row applies one LoRA to the model.", + group_name="LoRA", + ), + ], + outputs=[io.Model.Output(tooltip="The modified diffusion model.")], + ) + + @classmethod + def execute(cls, model, loras: list[dict]) -> io.NodeOutput: + for row in loras: + lora_name = row.get("lora_name") + strength = row.get("strength", 1.0) + if not lora_name or lora_name == "none" or strength == 0: + continue + lora, metadata = _load_lora_file(lora_name) + model, _ = comfy.sd.load_lora_for_models(model, None, lora, strength, 0, lora_metadata=metadata) + return io.NodeOutput(model) + + +class LoadLoraTextEncoder(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="LoadLoraTextEncoder", + display_name="Load LoRA (Text Encoder)", + search_aliases=["lora", "load lora", "apply lora", "clip lora", "lora stack"], + category="model/loaders", + description="Apply a stack of LoRAs to a CLIP text encoder. Add one row per LoRA; " + "each row picks a LoRA file and its strength.", + inputs=[ + io.Clip.Input("clip", tooltip="The CLIP text encoder the LoRAs will be applied to."), + io.DynamicGroup.Input( + "loras", + template=_lora_template(), + min=1, + max=50, + tooltip="Each row applies one LoRA to the text encoder.", + group_name="LoRA", + ), + ], + outputs=[io.Clip.Output(tooltip="The modified CLIP text encoder.")], + ) + + @classmethod + def execute(cls, clip, loras: list[dict]) -> io.NodeOutput: + for row in loras: + lora_name = row.get("lora_name") + strength = row.get("strength", 1.0) + if not lora_name or lora_name == "none" or strength == 0: + continue + lora, metadata = _load_lora_file(lora_name) + _, clip = comfy.sd.load_lora_for_models(None, clip, lora, 0, strength, lora_metadata=metadata) + return io.NodeOutput(clip) + + +class LoraStackExtension(ComfyExtension): + @override + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [ + LoadLoraModel, + LoadLoraTextEncoder, + ] + + +async def comfy_entrypoint() -> LoraStackExtension: + return LoraStackExtension() diff --git a/nodes.py b/nodes.py index 028e58c77..476340ddd 100644 --- a/nodes.py +++ b/nodes.py @@ -2476,6 +2476,7 @@ async def init_builtin_extra_nodes(): "nodes_triposplat.py", "nodes_depth_anything_3.py", "nodes_seed.py", + "nodes_lora_stack.py", ] import_failed = [] From a95e461916de9cbda2e89140ab86a8a7c3f9702a Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:53:11 -0700 Subject: [PATCH 2/5] int8 support on turing GPUs. (#14662) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8509599a6..01e7d2f94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ alembic SQLAlchemy>=2.0.0 filelock av>=16.0.0 -comfy-kitchen==0.2.13 +comfy-kitchen==0.2.14 comfy-aimdo==0.4.10 requests simpleeval>=1.0.0 From f19735759e8973bd2ead76f08d9cc45abc1f98e4 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Sat, 27 Jun 2026 23:34:30 -0700 Subject: [PATCH 3/5] ci: add team-gated Cursor review (thin caller for github-workflows) (#14527) --- .github/workflows/ci-cursor-review.yml | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/ci-cursor-review.yml diff --git a/.github/workflows/ci-cursor-review.yml b/.github/workflows/ci-cursor-review.yml new file mode 100644 index 000000000..2312c0ccd --- /dev/null +++ b/.github/workflows/ci-cursor-review.yml @@ -0,0 +1,38 @@ +name: CI - Cursor Review + +# Thin caller for the shared reusable cursor-review workflow in +# Comfy-Org/github-workflows. The review logic (panel matrix, judge +# consolidation, prompts, extract/post/notify scripts) lives there as the +# single source of truth, so this repo only carries the repo-specific diff +# excludes. + +on: + pull_request: + types: [labeled, unlabeled] + +concurrency: + group: cursor-review-pr-${{ github.event.pull_request.number }}-${{ github.event.label.name }} + cancel-in-progress: true + +jobs: + cursor-review: + if: github.event.label.name == 'cursor-review' + permissions: + contents: read + pull-requests: write + # SHA-pinned per zizmor `unpinned-uses: hash-pin`. Bump this SHA to pick up + # upstream changes; keep `workflows_ref` matching so prompts/scripts load + # from the same commit as the workflow definition. + uses: Comfy-Org/github-workflows/.github/workflows/cursor-review.yml@047ca48febe3a6647608ed2e0c4331b491cb9d6a # github-workflows#9 + with: + workflows_ref: 047ca48febe3a6647608ed2e0c4331b491cb9d6a + diff_excludes: >- + :!**/.claude/** + :!**/dist/** + :!**/vendor/** + :!**/*.generated.* + :!**/*.min.js + :!**/*.min.css + secrets: + CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} From 79c555ce6bfebf862d014e710b8d4b541ba5b896 Mon Sep 17 00:00:00 2001 From: comfyanonymous <121283862+comfyanonymous@users.noreply.github.com> Date: Sun, 28 Jun 2026 20:52:36 -0700 Subject: [PATCH 4/5] Fix int8 mm being skipped on offloaded lora weights. (#14669) --- comfy/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comfy/ops.py b/comfy/ops.py index 6a5090548..69d32e254 100644 --- a/comfy/ops.py +++ b/comfy/ops.py @@ -1216,7 +1216,7 @@ def mixed_precision_ops(quant_config={}, compute_dtype=torch.bfloat16, full_prec bias_dtype=input.dtype, offloadable=True, compute_dtype=compute_dtype, - want_requant=want_requant, + want_requant=True, ) weight = weight.to(dtype=input.dtype) else: From a58473fd9bf3a1e2383a41c6267ca03a168150ba Mon Sep 17 00:00:00 2001 From: "Daxiong (Lin)" Date: Mon, 29 Jun 2026 17:08:06 +0800 Subject: [PATCH 5/5] chore: update embedded docs to v0.5.6 (#14668) Co-authored-by: Alexis Rolland --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 01e7d2f94..b09b12f29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ comfyui-frontend-package==1.45.19 comfyui-workflow-templates==0.10.7 -comfyui-embedded-docs==0.5.5 +comfyui-embedded-docs==0.5.6 torch torchsde torchvision