mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-07-03 21:20:49 +08:00
Merge 9f22dd74c8 into 330a37db94
This commit is contained in:
commit
210d5c8d99
38
.github/workflows/ci-cursor-review.yml
vendored
Normal file
38
.github/workflows/ci-cursor-review.yml
vendored
Normal file
@ -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 }}
|
||||||
@ -1216,7 +1216,7 @@ def mixed_precision_ops(quant_config={}, compute_dtype=torch.bfloat16, full_prec
|
|||||||
bias_dtype=input.dtype,
|
bias_dtype=input.dtype,
|
||||||
offloadable=True,
|
offloadable=True,
|
||||||
compute_dtype=compute_dtype,
|
compute_dtype=compute_dtype,
|
||||||
want_requant=want_requant,
|
want_requant=True,
|
||||||
)
|
)
|
||||||
weight = weight.to(dtype=input.dtype)
|
weight = weight.to(dtype=input.dtype)
|
||||||
else:
|
else:
|
||||||
|
|||||||
130
comfy_extras/nodes_lora_stack.py
Normal file
130
comfy_extras/nodes_lora_stack.py
Normal file
@ -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()
|
||||||
1
nodes.py
1
nodes.py
@ -2476,6 +2476,7 @@ async def init_builtin_extra_nodes():
|
|||||||
"nodes_triposplat.py",
|
"nodes_triposplat.py",
|
||||||
"nodes_depth_anything_3.py",
|
"nodes_depth_anything_3.py",
|
||||||
"nodes_seed.py",
|
"nodes_seed.py",
|
||||||
|
"nodes_lora_stack.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
import_failed = []
|
import_failed = []
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
comfyui-frontend-package==1.45.19
|
comfyui-frontend-package==1.45.19
|
||||||
comfyui-workflow-templates==0.10.7
|
comfyui-workflow-templates==0.10.7
|
||||||
comfyui-embedded-docs==0.5.5
|
comfyui-embedded-docs==0.5.6
|
||||||
torch
|
torch
|
||||||
torchsde
|
torchsde
|
||||||
torchvision
|
torchvision
|
||||||
@ -22,7 +22,7 @@ alembic
|
|||||||
SQLAlchemy>=2.0.0
|
SQLAlchemy>=2.0.0
|
||||||
filelock
|
filelock
|
||||||
av>=16.0.0
|
av>=16.0.0
|
||||||
comfy-kitchen==0.2.13
|
comfy-kitchen==0.2.14
|
||||||
comfy-aimdo==0.4.10
|
comfy-aimdo==0.4.10
|
||||||
requests
|
requests
|
||||||
simpleeval>=1.0.0
|
simpleeval>=1.0.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user