[cuda] packages was ["detectron2", "torch-scatter", "torch-sparse",
"torch-cluster", "flash-attn", "sageattention"]. Four of those six are not
used by this pack in any way:
- No .py file references them, by module name or by API (scatter_add,
segment_csr, knn_graph, SparseTensor, spmm, coalesce all return zero).
- None of the pack's eight declared PyPI dependencies requires them
(checked against live PyPI metadata).
- Upstream facebookresearch/sam-3d-body needs detectron2 only for
tools/build_detector.py, its ViTDet person detector -- a stage this port
replaces with mask inputs from the graph, which is why the workflows are
named multiple_masks*. torch-scatter/-sparse/-cluster appear nowhere in
upstream's INSTALL.md at all: there are no sparse convolutions in SAM 3D
Body outside the JIT'd MHR module, which runs fp32 regardless.
Confirmed by ablation rather than by grep: uninstalled from the isolated env,
the metadata scan still imports every node module (real scan, cache deleted --
a cached scan had made an earlier attempt look green), all 15 nodes register
through the worker, and comfy-test passes syntax/install/registration/
javascript/instantiation plus every CUDA workflow.
SageAttention is removed for a different reason. comfy-env's worker
auto-enables it on mere importability (_persistent_worker.py:1457-1474,
no opt-out), and it is INT8-quantised attention: measured at this model's
real shapes it is 1.34e-2 rel-L2 vs 3.51e-3 for SDPA and flash -- which also
erases the entire fp16 advantage, since its floor is quantisation, not dtype.
Propagated through the 6D->SO(3) head that decodes joint rotations, that is
7.06 deg of max joint error against 2.96 deg, and it compounds down the
kinematic chain. This is a coordinate-regression network, not a diffusion
model; it has no denoising steps to absorb the error.
flash-attn stays: measured bit-identical to PyTorch SDPA at every shape and
both dtypes, so removing it would cost speed and buy nothing.
attention.py passes low_precision_attention=False as belt-and-braces, so the
fix holds even if sageattention reaches the env by another route. Verified
end-to-end with sageattention still installed AND auto-enabled: the worker
logged "Auto-enabled sage attention" and the workflows passed anyway. Every
attention backend takes **kwargs, so the flag is safe on all of them; only
attention_sage reads it. This is the same flag ComfyUI core sets for its own
geometry models (ldm/sam3/sam.py, ldm/triposplat/model.py,
ldm/hunyuan3dv2_1/hunyuandit.py).
The section never held nodes -- every entry is a whole pack repo. Read
literally, `[node_reqs]` says "the requirements of my node", which is pip
packages, and that section genuinely exists one file over as [dependencies]
in comfy-env.toml. `[node_packs]` names the unit and sits parallel to the
existing [cuda_packages].
Requires comfy-env >= 0.4.27, which renames the key with no shim: the root
file has a closed schema, so an old comfy-env rejects [node_packs] by name
and a new one rejects [node_reqs] by name. Neither fails silently.
The frontend directory is declared once, in pyproject.toml as
`[tool.comfy] web = "javascript"`. This `WEB_DIRECTORY = "./web"` pointed at
a directory that no longer exists -- it was left behind when the JS moved to
javascript/ -- so it is dead today. It is also a trap.
ComfyUI registers the web folder in TWO independent code paths, and they key
the same dict differently (nodes.py):
pyproject [tool.comfy] web -> EXTENSION_WEB_DIRS[project.name] (:2280)
WEB_DIRECTORY -> EXTENSION_WEB_DIRS[module_name] (:2289)
They are separate `if` blocks guarded by os.path.isdir(), not a fallback
chain, and project.name is rarely the directory name. So the moment a `web/`
directory exists again -- a revert, a merge, a stray file -- BOTH paths
register the same folder under two different keys. GET /extensions then lists
every file twice, the browser imports each twice, and app.registerExtension
runs twice with the same name: the collision the javascript test level exists
to catch, and invisible to it, because the duplication is in registration
rather than in source.
Removing the stale declaration makes that unreachable instead of dormant.
Vendor the viewer JavaScript into javascript/ and drop the
comfy-3d-viewers dependency. The viewers were previously copied into web/ at
every startup by prestartup_script.py, which made the served frontend depend on
a second package resolving at runtime.
Bundles are vendored as .mjs so ComfyUI's **/*.js auto-import glob skips them:
they are iframe-only and must never enter the shared main browser realm. Only
the widget entry points stay .js.
Also replace comfy_env.copy_files with a local helper. Its two extra features
(frame-derived source dir, silent no-op on a missing source) were unused here,
and the asset seeding now resolves the input directory through
folder_paths.get_input_directory() so it follows ComfyUI Desktop and
--input-directory instead of writing into the code tree.
comfy-test.toml moves to the current schema: [test.workflows] cuda (there is no
"gpu" key -- the accelerator is named by backend, and the old spelling parsed to
an empty list, so CUDA runners silently ran the full unfiltered set), an
explicit [test.platforms] platforms allowlist (per-platform booleans are no
longer supported), and the javascript level.
Restrict FBX_PREVIEW_NODES to this pack's own preview node. The shared upstream
copy of mesh_preview_fbx.js listed every sibling pack's node too, so installing
two of them registered the same extension names twice and double-attached the
widget. Guard the message listener on event.source.