Accelerator declarations¶
Why this convention exists¶
Packs on the registry install for everyone, whatever the machine, and then fail at install or execution when the hardware does not match (example: TRELLIS2 nodepack on Mac, no CUDA GPU).
This bites quite hard for nodepacks like ComfyUI-GeometryPack: - Some nodes need a CUDA GPU, most run fine on CPU, and the CUDA remeshing backends should simply disappear for a user without an NVIDIA card instead of greeting them with a stack trace. - Testing has a similar disease in another form: python files importing CUDA packages forced CPU test lanes to mock those imports.
The cure for both of these problems is one tag on the node.
What you write¶
ACCELERATOR is a class attribute holding one value, or a list of values,
from a closed vocabulary. An unrecognized value fails the metadata scan
loudly, naming the node and the vocabulary.
| Value | Hardware | Status |
|---|---|---|
"cuda" |
NVIDIA | wired end to end (wheel index, [cuda], probes) |
"rocm" |
AMD | valid and availability gated; no wheel story yet |
"xpu" |
Intel | valid and availability gated; no wheel story yet |
"mps" |
Apple | valid and availability gated; no wheel story yet |
class RemeshGPUNode(io.ComfyNode):
ACCELERATOR = "cuda" # REQUIRES CUDA at execution
class SegmentGPUNode(io.ComfyNode):
ACCELERATOR = ["cuda", "mps"] # runs on either; not on ROCm or CPU
def execute(cls, mesh, ...):
import cumesh # lazy: only runs when the node actually executes
...
The second half of the convention is visible in that example: accelerator packages are imported lazily, inside the nodes that declare them, never at module top level. Enforcement covers who checks this and what each checker can see.
What it buys you¶
| # | Behavior | Mechanism |
|---|---|---|
| 1 | Precise degradation on the wrong machine. The node still registers with its real inputs and outputs (shared workflows load, dispatcher node ids resolve) but is hidden from the node picker, its description badged "(requires CUDA -- unavailable on this machine)", a startup line names it, and executing it raises a named reason error instead of a raw torch stack trace | the unavailable stub; the gate is machine backend ∈ declared list (ADR-0012) |
| 2 | Import hygiene, enforced twice. The declaration tells both checkers which nodes may lazily import the [cuda] packages |
Enforcement |
| 3 | Honest CPU test lanes. comfy-test skips declared GPU nodes as "requires cuda" instead of faking their imports with empty mock modules | What it buys you |
| 4 | A machine readable tag, _comfy_env_accelerator on the proxy class, for harnesses and future UI badging |
registration |
One honest limit: the declaration is consumed, never audited. Nothing verifies the node actually needs what it declares.
Over-declare and a node that runs fine on CPU is needlessly hidden on Macs.
Under-declare and CPU users get the raw error instead of the named one.
Auditing implies executing the node on real hardware, which is the job of comfy-test's execution level, not a metadata scan.
The fine print¶
- A list means any of these will do.
["cuda", "mps"]says CUDA or Metal, not ROCm. - Absent means CPU capable, and the meaning is strictly "requires one of these backends at execution", never "can use them". A node with a real CPU fallback declares nothing.
- Values are normalized at scan time: lowercased, deduplicated, sorted.
-
Registered but hidden from the menu (ADR-0012) is row 1 above, mechanically: the hiding rides ComfyUI's own
DEPRECATEDhandling (hidden from picker and search, still registered), and the named reason error reads:Node 'GeomPackRemesh_GPU' requires CUDA; this machine has backend 'cpu' (no NVIDIA GPU detected). Use a CPU-capable alternative node or run on a machine with CUDA.Full unregistration was rejected deliberately: a missing node type breaks shared workflow loading with an inscrutable "node type not found". Hiding gives the clean picker without that cost. 5. Lazy imports only. Packages from the env's
[cuda]list may only be imported inside function bodies (typicallyexecute()) of nodes that declare that accelerator. A module top level accelerator import is an error anywhere: comfy-env skips CUDA wheels on machines with no GPU, so on every CPU machine that import kills the metadata scan and every node in the env silently vanishes, CPU nodes included.
CPU or GPU: the dispatch pattern¶
A declaration can express which GPUs, never GPU or CPU, because absent means CPU capable. So a node offering both should declare nothing and act as an accelerator neutral dispatcher, routing to hidden per backend leaf nodes (by node id, without importing them), each declaring its own requirement.
GeometryPack's flagship nodes (Remesh, UV Unwrap, Fix Normals) already work this way, and it stays the blessed shape.
Opportunistic GPU use, device = "cuda" if torch.cuda.is_available() else
"cpu" with a genuine CPU path, is legal anywhere and declares nothing.
Prefer comfy.model_management.get_torch_device(), which is ComfyUI's own
answer to the same question and respects flags like --cpu.
Enforcement¶
Three mechanisms, in order of authority:
- Scan time observation (authoritative). The metadata scan checks
sys.modulesafter importing the pack: nothing has executed during a scan, so any[cuda]package present was imported at module top level. Import names are mapped from distribution names via package metadata, sofaithc-aotimporting asfaithcontouris caught too. Violations are reported loudly at everyregister_nodes(). -
Static check (CI).
comfy-test lint --check accelAST walks each env on a bare checkout, no env built, no server, so a violation is caught before the pack ships rather than after it is installed. Unguarded top level accelerator imports are errors; guarded (try/except) ones andtorch.cudause in undeclared modules are warnings. It resolves import names fromenv.stamp.json'saccel_imports, sofaithc-aotis matched asfaithcontourexactly rather than guessed; a package with no recorded mapping is reported as unverifiable, not passed. Static analysis can still be defeated by dynamic imports, which is why the scan time check remains the authority.This lived in comfy-env as a
comfy-env doctorsection until 0.4.27. It moved because the check is only useful before shipping, which is CI's job, and because guessing import names byname.replace("-", "_"), the best a checker inside comfy-env could do, passes a top levelimport faithcontoursilently. -
Registration gate.
build_proxy_classbuilds the unavailable stub for declared nodes the machine cannot serve. Available nodes carry_comfy_env_accelerator(a list) on the proxy class for downstream consumers.