Avoid TorchCodec for tensor-native inference

This commit is contained in:
Ethanfel
2026-08-23 15:41:34 +02:00
parent 1f27b8d316
commit e6fe77dbbe
6 changed files with 121 additions and 6 deletions
+2
View File
@@ -27,6 +27,8 @@ python ComfyUI-SAM-Audio/install.py
Then restart ComfyUI. If installed through ComfyUI Manager, the installer should run automatically.
The node uses ComfyUI's decoded media tensors and does not install or load TorchCodec.
## Model download
Select a model in **SAM-Audio Model Loader** and queue the workflow. The loader first tries Meta's official repository. If Hugging Face denies gated access, it automatically downloads the matching pinned and checksum-verified checkpoint from the [public mirror collection](https://huggingface.co/collections/mrfakename/sam-audio).
-1
View File
@@ -13,7 +13,6 @@ dependencies = [
"regex",
"safetensors",
"timm",
"torchcodec",
"torchdiffeq",
"tqdm",
"transformers>=4.54.0",
-1
View File
@@ -6,7 +6,6 @@ huggingface-hub
regex
safetensors
timm
torchcodec
torchdiffeq
tqdm
transformers>=4.54.0
+7 -3
View File
@@ -13,6 +13,7 @@ import torch
from .attention import validate_attention_backend
from .audiotools_compat import audiotools_import_compatibility
from .torchcodec_compat import torchcodec_import_compatibility
from .xformers_compat import xformers_import_compatibility
LOGGER = logging.getLogger(__name__)
@@ -305,14 +306,17 @@ def import_sam_audio() -> tuple[Any, Any]:
LOGGER.info(
"Loading SAM-Audio through isolated inference-only compatibility layers"
)
with xformers_import_compatibility(), audiotools_import_compatibility():
with (
xformers_import_compatibility(),
audiotools_import_compatibility(),
torchcodec_import_compatibility(),
):
module = importlib.import_module("sam_audio")
return module.SAMAudio, module.SAMAudioProcessor
except Exception as error:
raise RuntimeError(
"SAM-Audio could not be imported. Install this node's requirements with "
"ComfyUI's Python interpreter, then restart ComfyUI. This node does not "
"require or install xFormers."
"ComfyUI's Python interpreter, then restart ComfyUI."
) from error
+64
View File
@@ -0,0 +1,64 @@
"""Import-only TorchCodec surface for tensor-native ComfyUI inference."""
from __future__ import annotations
import contextlib
import sys
import types
from collections.abc import Iterator
class _TensorInputsOnly:
def __init__(self, *_args, **_kwargs):
raise RuntimeError(
"TorchCodec file decoding is disabled in ComfyUI-SAM-Audio. Pass decoded "
"ComfyUI AUDIO, IMAGE, and MASK tensors to the node instead."
)
class AudioDecoder(_TensorInputsOnly):
"""Placeholder for an upstream file-I/O path unused by the ComfyUI node."""
class VideoDecoder(_TensorInputsOnly):
"""Placeholder for an upstream file-I/O path unused by the ComfyUI node."""
class AudioEncoder(_TensorInputsOnly):
"""Placeholder for an optional upstream ranker disabled by this integration."""
def _compatibility_modules() -> dict[str, types.ModuleType]:
torchcodec = types.ModuleType("torchcodec")
torchcodec.__path__ = []
decoders = types.ModuleType("torchcodec.decoders")
encoders = types.ModuleType("torchcodec.encoders")
decoders.AudioDecoder = AudioDecoder
decoders.VideoDecoder = VideoDecoder
encoders.AudioEncoder = AudioEncoder
torchcodec.decoders = decoders
torchcodec.encoders = encoders
return {
"torchcodec": torchcodec,
"torchcodec.decoders": decoders,
"torchcodec.encoders": encoders,
}
@contextlib.contextmanager
def torchcodec_import_compatibility() -> Iterator[None]:
"""Temporarily provide upstream's unused decoder imports without loading FFmpeg."""
compatibility_modules = _compatibility_modules()
missing = object()
previous = {
name: sys.modules.get(name, missing) for name in compatibility_modules
}
sys.modules.update(compatibility_modules)
try:
yield
finally:
for name, module in previous.items():
if module is missing:
sys.modules.pop(name, None)
else:
sys.modules[name] = module
+48 -1
View File
@@ -1,9 +1,15 @@
import sys
import types
from pathlib import Path
import pytest
import torch
from sam_audio_comfy import audiotools_compat, xformers_compat
from sam_audio_comfy import (
audiotools_compat,
torchcodec_compat,
xformers_compat,
)
def test_node_does_not_require_xformers():
@@ -21,6 +27,17 @@ def test_node_does_not_require_xformers():
assert not any("xformers" in line for line in dependency_lines)
def test_node_does_not_require_torchcodec():
root = Path(__file__).parents[1]
for filename in ("requirements.txt", "pyproject.toml"):
dependency_lines = [
line
for line in (root / filename).read_text().lower().splitlines()
if line.strip() and not line.lstrip().startswith("#")
]
assert not any("torchcodec" in line for line in dependency_lines)
def test_upstream_installer_uses_no_deps():
root = Path(__file__).parents[1]
installer = (root / "install.py").read_text().lower()
@@ -58,3 +75,33 @@ def test_audiotools_compatibility_is_import_only_and_scoped(monkeypatch):
assert audiotools.ml.BaseModel.INTERN == []
assert "audiotools" not in sys.modules
def test_torchcodec_compatibility_is_import_only_and_scoped(monkeypatch):
monkeypatch.delitem(sys.modules, "torchcodec", raising=False)
monkeypatch.delitem(sys.modules, "torchcodec.decoders", raising=False)
monkeypatch.delitem(sys.modules, "torchcodec.encoders", raising=False)
with torchcodec_compat.torchcodec_import_compatibility():
decoders = sys.modules["torchcodec.decoders"]
assert decoders.AudioDecoder is torchcodec_compat.AudioDecoder
assert decoders.VideoDecoder is torchcodec_compat.VideoDecoder
with pytest.raises(RuntimeError, match="decoded ComfyUI"):
decoders.AudioDecoder("input.wav")
assert "torchcodec" not in sys.modules
assert "torchcodec.decoders" not in sys.modules
def test_existing_torchcodec_modules_are_restored(monkeypatch):
original = types.ModuleType("torchcodec")
original_decoders = types.ModuleType("torchcodec.decoders")
monkeypatch.setitem(sys.modules, "torchcodec", original)
monkeypatch.setitem(sys.modules, "torchcodec.decoders", original_decoders)
with torchcodec_compat.torchcodec_import_compatibility():
assert sys.modules["torchcodec"] is not original
assert sys.modules["torchcodec.decoders"] is not original_decoders
assert sys.modules["torchcodec"] is original
assert sys.modules["torchcodec.decoders"] is original_decoders