mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-16 08:22:36 +08:00
Compare commits
6 Commits
15d80741d5
...
1d6f75dd60
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d6f75dd60 | ||
|
|
4f5bd39b1c | ||
|
|
dcff27fe3f | ||
|
|
027c862453 | ||
|
|
43e9509856 | ||
|
|
265b4f0fa1 |
@ -208,7 +208,7 @@ comfy install
|
||||
|
||||
## Manual Install (Windows, Linux)
|
||||
|
||||
Python 3.14 works but you may encounter issues with the torch compile node. The free threaded variant is still missing some dependencies.
|
||||
Python 3.14 works but some custom nodes may have issues. The free threaded variant works but some dependencies will enable the GIL so it's not fully supported.
|
||||
|
||||
Python 3.13 is very well supported. If you have trouble with some custom node dependencies on 3.13 you can try 3.12
|
||||
|
||||
|
||||
@ -236,6 +236,8 @@ class ComfyNodeABC(ABC):
|
||||
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
||||
DEPRECATED: bool
|
||||
"""Flags a node as deprecated, indicating to users that they should find alternatives to this node."""
|
||||
DEV_ONLY: bool
|
||||
"""Flags a node as dev-only, hiding it from search/menus unless dev mode is enabled."""
|
||||
API_NODE: Optional[bool]
|
||||
"""Flags a node as an API node. See: https://docs.comfy.org/tutorials/api-nodes/overview."""
|
||||
|
||||
|
||||
@ -1247,6 +1247,7 @@ class NodeInfoV1:
|
||||
output_node: bool=None
|
||||
deprecated: bool=None
|
||||
experimental: bool=None
|
||||
dev_only: bool=None
|
||||
api_node: bool=None
|
||||
price_badge: dict | None = None
|
||||
search_aliases: list[str]=None
|
||||
@ -1264,6 +1265,7 @@ class NodeInfoV3:
|
||||
output_node: bool=None
|
||||
deprecated: bool=None
|
||||
experimental: bool=None
|
||||
dev_only: bool=None
|
||||
api_node: bool=None
|
||||
price_badge: dict | None = None
|
||||
|
||||
@ -1375,6 +1377,8 @@ class Schema:
|
||||
"""Flags a node as deprecated, indicating to users that they should find alternatives to this node."""
|
||||
is_experimental: bool=False
|
||||
"""Flags a node as experimental, informing users that it may change or not work as expected."""
|
||||
is_dev_only: bool=False
|
||||
"""Flags a node as dev-only, hiding it from search/menus unless dev mode is enabled."""
|
||||
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
|
||||
@ -1485,6 +1489,7 @@ class Schema:
|
||||
output_node=self.is_output_node,
|
||||
deprecated=self.is_deprecated,
|
||||
experimental=self.is_experimental,
|
||||
dev_only=self.is_dev_only,
|
||||
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,
|
||||
@ -1519,6 +1524,7 @@ class Schema:
|
||||
output_node=self.is_output_node,
|
||||
deprecated=self.is_deprecated,
|
||||
experimental=self.is_experimental,
|
||||
dev_only=self.is_dev_only,
|
||||
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,
|
||||
@ -1791,6 +1797,14 @@ class _ComfyNodeBaseInternal(_ComfyNodeInternal):
|
||||
cls.GET_SCHEMA()
|
||||
return cls._DEPRECATED
|
||||
|
||||
_DEV_ONLY = None
|
||||
@final
|
||||
@classproperty
|
||||
def DEV_ONLY(cls): # noqa
|
||||
if cls._DEV_ONLY is None:
|
||||
cls.GET_SCHEMA()
|
||||
return cls._DEV_ONLY
|
||||
|
||||
_API_NODE = None
|
||||
@final
|
||||
@classproperty
|
||||
@ -1893,6 +1907,8 @@ class _ComfyNodeBaseInternal(_ComfyNodeInternal):
|
||||
cls._EXPERIMENTAL = schema.is_experimental
|
||||
if cls._DEPRECATED is None:
|
||||
cls._DEPRECATED = schema.is_deprecated
|
||||
if cls._DEV_ONLY is None:
|
||||
cls._DEV_ONLY = schema.is_dev_only
|
||||
if cls._API_NODE is None:
|
||||
cls._API_NODE = schema.is_api_node
|
||||
if cls._OUTPUT_NODE is None:
|
||||
|
||||
78
comfy_extras/nodes_sage3.py
Normal file
78
comfy_extras/nodes_sage3.py
Normal file
@ -0,0 +1,78 @@
|
||||
from typing import Callable
|
||||
|
||||
import torch
|
||||
from typing_extensions import override
|
||||
|
||||
from comfy.ldm.modules.attention import get_attention_function
|
||||
from comfy.model_patcher import ModelPatcher
|
||||
from comfy_api.latest import ComfyExtension, io
|
||||
from server import PromptServer
|
||||
|
||||
|
||||
class Sage3PatchModel(io.ComfyNode):
|
||||
@classmethod
|
||||
def define_schema(cls):
|
||||
return io.Schema(
|
||||
node_id="Sage3PatchModel",
|
||||
display_name="Patch SageAttention 3",
|
||||
description="Patch the model to use `attention3_sage` during the middle blocks and steps, keeping the default attention function for the first/last blocks and steps",
|
||||
category="_for_testing",
|
||||
inputs=[
|
||||
io.Model.Input("model"),
|
||||
],
|
||||
outputs=[io.Model.Output()],
|
||||
is_experimental=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, model: ModelPatcher) -> io.NodeOutput:
|
||||
sage3: Callable | None = get_attention_function("sage3", default=None)
|
||||
|
||||
if sage3 is None:
|
||||
PromptServer.instance.send_progress_text(
|
||||
"`sageattn3` is not installed / available...",
|
||||
cls.hidden.unique_id,
|
||||
)
|
||||
return io.NodeOutput(model)
|
||||
|
||||
def attention_override(func: Callable, *args, **kwargs):
|
||||
transformer_options: dict = kwargs.get("transformer_options", {})
|
||||
|
||||
block_index: int = transformer_options.get("block_index", 0)
|
||||
total_blocks: int = transformer_options.get("total_blocks", 1)
|
||||
|
||||
if block_index == 0 or block_index >= (total_blocks - 1):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
sample_sigmas: torch.Tensor = transformer_options["sample_sigmas"]
|
||||
sigmas: torch.Tensor = transformer_options["sigmas"]
|
||||
|
||||
total_steps: int = sample_sigmas.size(0)
|
||||
step: int = 0
|
||||
|
||||
for i in range(total_steps):
|
||||
if torch.allclose(sample_sigmas[i], sigmas):
|
||||
step = i
|
||||
break
|
||||
|
||||
if step == 0 or step >= (total_steps - 1):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return sage3(*args, **kwargs)
|
||||
|
||||
model = model.clone()
|
||||
model.model_options["transformer_options"][
|
||||
"optimized_attention_override"
|
||||
] = attention_override
|
||||
|
||||
return io.NodeOutput(model)
|
||||
|
||||
|
||||
class Sage3Extension(ComfyExtension):
|
||||
@override
|
||||
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
||||
return [Sage3PatchModel]
|
||||
|
||||
|
||||
async def comfy_entrypoint():
|
||||
return Sage3Extension()
|
||||
1
nodes.py
1
nodes.py
@ -2430,6 +2430,7 @@ async def init_builtin_extra_nodes():
|
||||
"nodes_nop.py",
|
||||
"nodes_kandinsky5.py",
|
||||
"nodes_wanmove.py",
|
||||
"nodes_sage3.py",
|
||||
"nodes_image_compare.py",
|
||||
"nodes_zimage.py",
|
||||
"nodes_lora_debug.py"
|
||||
|
||||
@ -679,6 +679,8 @@ class PromptServer():
|
||||
info['deprecated'] = True
|
||||
if getattr(obj_class, "EXPERIMENTAL", False):
|
||||
info['experimental'] = True
|
||||
if getattr(obj_class, "DEV_ONLY", False):
|
||||
info['dev_only'] = True
|
||||
|
||||
if hasattr(obj_class, 'API_NODE'):
|
||||
info['api_node'] = obj_class.API_NODE
|
||||
|
||||
Loading…
Reference in New Issue
Block a user