Fix current Hub loading and demo sampler values

This commit is contained in:
Ethanfel
2026-08-23 15:50:30 +02:00
parent e6fe77dbbe
commit 50ffce7c88
8 changed files with 106 additions and 10 deletions
+1 -1
View File
@@ -65,7 +65,7 @@
{"name": "residual", "type": "AUDIO", "links": [5], "slot_index": 1}
],
"properties": {"Node name for S&R": "SAMAudioSpanSeparator"},
"widgets_values": ["car honking", 0, 32]
"widgets_values": ["car honking", 0, "fixed", 32]
},
{
"id": 5,
+1 -1
View File
@@ -49,7 +49,7 @@
{"name": "residual", "type": "AUDIO", "links": [4], "slot_index": 1}
],
"properties": {"Node name for S&R": "SAMAudioTextSeparator"},
"widgets_values": ["man speaking", false, 0, 32]
"widgets_values": ["man speaking", false, 0, "fixed", 32]
},
{
"id": 4,
+1 -1
View File
@@ -67,7 +67,7 @@
{"name": "residual", "type": "AUDIO", "links": [6], "slot_index": 1}
],
"properties": {"Node name for S&R": "SAMAudioVisualSeparator"},
"widgets_values": ["", 0, 32]
"widgets_values": ["", 0, "fixed", 32]
},
{
"id": 5,
+1
View File
@@ -41,6 +41,7 @@ def _sampling_inputs() -> dict[str, tuple[Any, ...]]:
"default": 0,
"min": 0,
"max": MAX_SEED,
"control_after_generate": True,
"tooltip": "Controls SAM-Audio's initial noise for reproducible separation.",
},
),
+37 -7
View File
@@ -320,6 +320,42 @@ def import_sam_audio() -> tuple[Any, Any]:
) from error
def _load_sam_audio_checkpoint(SAMAudio: Any, model_path: Path) -> Any:
"""Load a local checkpoint across old and new huggingface_hub call signatures."""
model_kwargs = {
"map_location": "cpu",
"strict": False,
"text_ranker": None,
"visual_ranker": None,
}
try:
return SAMAudio.from_pretrained(str(model_path), **model_kwargs)
except TypeError as error:
message = str(error)
if not (
"_from_pretrained()" in message
and "proxies" in message
and "resume_download" in message
):
raise
LOGGER.info(
"Using SAM-Audio's legacy local-checkpoint loader with the current "
"huggingface_hub API"
)
return SAMAudio._from_pretrained(
model_id=str(model_path),
revision=None,
cache_dir=None,
force_download=False,
proxies=None,
resume_download=False,
local_files_only=True,
token=None,
**model_kwargs,
)
def load_pipeline(
model_name: str,
folder_paths: Any,
@@ -334,13 +370,7 @@ def load_pipeline(
LOGGER.info("Loading SAM-Audio model from %s", model_path)
try:
model = SAMAudio.from_pretrained(
str(model_path),
map_location="cpu",
strict=False,
text_ranker=None,
visual_ranker=None,
)
model = _load_sam_audio_checkpoint(SAMAudio, model_path)
processor = SAMAudioProcessor.from_pretrained(str(model_path))
except Exception as error:
raise RuntimeError(
+4
View File
@@ -97,6 +97,10 @@ def test_text_node_runs_full_audio_batch_with_standard_shapes(monkeypatch, tmp_p
"SAMAudioPipelineLoader"
].INPUT_TYPES()["required"]
assert loader_inputs["attention_backend"][0] == ["pytorch", "comfy_kitchen"]
sampler_inputs = package.NODE_CLASS_MAPPINGS[
"SAMAudioTextSeparator"
].INPUT_TYPES()["required"]
assert sampler_inputs["seed"][1]["control_after_generate"] is True
assert set(package.NODE_CLASS_MAPPINGS) == {
"SAMAudioPipelineLoader",
"SAMAudioSpanPrompt",
+23
View File
@@ -1,3 +1,4 @@
import json
import sys
import types
from pathlib import Path
@@ -38,6 +39,28 @@ def test_node_does_not_require_torchcodec():
assert not any("torchcodec" in line for line in dependency_lines)
def test_example_sampler_widgets_include_seed_control():
root = Path(__file__).parents[1]
expected = {
"text_separation_workflow.json": (
"SAMAudioTextSeparator",
["man speaking", False, 0, "fixed", 32],
),
"span_separation_workflow.json": (
"SAMAudioSpanSeparator",
["car honking", 0, "fixed", 32],
),
"visual_separation_workflow.json": (
"SAMAudioVisualSeparator",
["", 0, "fixed", 32],
),
}
for filename, (node_type, widget_values) in expected.items():
workflow = json.loads((root / "examples" / filename).read_text())
sampler = next(node for node in workflow["nodes"] if node["type"] == node_type)
assert sampler["widgets_values"] == widget_values
def test_upstream_installer_uses_no_deps():
root = Path(__file__).parents[1]
installer = (root / "install.py").read_text().lower()
+38
View File
@@ -141,6 +141,44 @@ def test_mirror_checksum_mismatch_is_rejected(tmp_path, monkeypatch):
runtime._download_official_model("facebook/sam-audio-small", tmp_path)
def test_local_checkpoint_falls_back_for_legacy_hub_signature(tmp_path):
calls = []
class FakeSAMAudio:
@classmethod
def from_pretrained(cls, model_id, **kwargs):
calls.append(("public", model_id, kwargs))
raise TypeError(
"BaseModel._from_pretrained() missing 2 required keyword-only "
"arguments: 'proxies' and 'resume_download'"
)
@classmethod
def _from_pretrained(cls, **kwargs):
calls.append(("legacy", kwargs))
return "model"
assert runtime._load_sam_audio_checkpoint(FakeSAMAudio, tmp_path) == "model"
assert calls[1][0] == "legacy"
assert calls[1][1]["model_id"] == str(tmp_path)
assert calls[1][1]["local_files_only"] is True
assert calls[1][1]["proxies"] is None
assert calls[1][1]["resume_download"] is False
assert calls[1][1]["strict"] is False
assert calls[1][1]["text_ranker"] is None
assert calls[1][1]["visual_ranker"] is None
def test_local_checkpoint_does_not_hide_unrelated_type_errors(tmp_path):
class FakeSAMAudio:
@classmethod
def from_pretrained(cls, model_id, **kwargs):
raise TypeError("bad model config")
with pytest.raises(TypeError, match="bad model config"):
runtime._load_sam_audio_checkpoint(FakeSAMAudio, tmp_path)
def test_ode_options_match_upstream_default():
assert ode_options(32) == {
"method": "midpoint",