Spaces:
Running on Zero
Running on Zero
Architecture firm marketing aide - Expansion
#5
by isr223 - opened
- README.md +1 -1
- models.py +1 -3
- tests/conftest.py +1 -1
- tests/test_models.py +0 -17
- tests/test_workflow.py +0 -19
- workflow.py +1 -26
README.md
CHANGED
|
@@ -16,7 +16,7 @@ preload_from_hub:
|
|
| 16 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Jib-Down ltx-2-19b-lora-camera-control-jib-down.safetensors
|
| 17 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Jib-Up ltx-2-19b-lora-camera-control-jib-up.safetensors
|
| 18 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Static ltx-2-19b-lora-camera-control-static.safetensors
|
| 19 |
-
- Lightricks/LTX-2.3 ltx-2.3-22b-distilled-lora-384.safetensors,ltx-2.3-spatial-upscaler-x2-1.
|
| 20 |
- Lightricks/LTX-2.3-22b-IC-LoRA-Union-Control ltx-2.3-22b-ic-lora-union-control-ref0.5.safetensors
|
| 21 |
- google/gemma-3-12b-it-qat-q4_0-unquantized gemma-3-12b-it/model-00001-of-00005.safetensors,gemma-3-12b-it/model-00002-of-00005.safetensors,gemma-3-12b-it/model-00003-of-00005.safetensors,gemma-3-12b-it/model-00004-of-00005.safetensors,gemma-3-12b-it/model-00005-of-00005.safetensors,gemma-3-12b-it/model.safetensors.index.json,gemma-3-12b-it/preprocessor_config.json,gemma-3-12b-it/tokenizer.model
|
| 22 |
---
|
|
|
|
| 16 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Jib-Down ltx-2-19b-lora-camera-control-jib-down.safetensors
|
| 17 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Jib-Up ltx-2-19b-lora-camera-control-jib-up.safetensors
|
| 18 |
- Lightricks/LTX-2-19b-LoRA-Camera-Control-Static ltx-2-19b-lora-camera-control-static.safetensors
|
| 19 |
+
- Lightricks/LTX-2.3 ltx-2.3-22b-distilled-lora-384.safetensors,ltx-2.3-spatial-upscaler-x2-1.0.safetensors
|
| 20 |
- Lightricks/LTX-2.3-22b-IC-LoRA-Union-Control ltx-2.3-22b-ic-lora-union-control-ref0.5.safetensors
|
| 21 |
- google/gemma-3-12b-it-qat-q4_0-unquantized gemma-3-12b-it/model-00001-of-00005.safetensors,gemma-3-12b-it/model-00002-of-00005.safetensors,gemma-3-12b-it/model-00003-of-00005.safetensors,gemma-3-12b-it/model-00004-of-00005.safetensors,gemma-3-12b-it/model-00005-of-00005.safetensors,gemma-3-12b-it/model.safetensors.index.json,gemma-3-12b-it/preprocessor_config.json,gemma-3-12b-it/tokenizer.model
|
| 22 |
---
|
models.py
CHANGED
|
@@ -34,9 +34,7 @@ MODEL_REGISTRY: dict[str, ModelEntry] = {
|
|
| 34 |
# Main LTX 2.3 transformer + LoRAs + upscalers
|
| 35 |
"ltx-2.3-22b-distilled.safetensors": ModelEntry("Lightricks/LTX-2.3", comfy_type="checkpoints"),
|
| 36 |
"ltx-2.3-22b-dev.safetensors": ModelEntry("Lightricks/LTX-2.3", comfy_type="checkpoints"),
|
| 37 |
-
|
| 38 |
-
# rewrites the templates' references to this name.
|
| 39 |
-
"ltx-2.3-spatial-upscaler-x2-1.1.safetensors": ModelEntry(
|
| 40 |
"Lightricks/LTX-2.3", comfy_type="latent_upscale_models"
|
| 41 |
),
|
| 42 |
"ltx-2.3-22b-distilled-lora-384.safetensors": ModelEntry(
|
|
|
|
| 34 |
# Main LTX 2.3 transformer + LoRAs + upscalers
|
| 35 |
"ltx-2.3-22b-distilled.safetensors": ModelEntry("Lightricks/LTX-2.3", comfy_type="checkpoints"),
|
| 36 |
"ltx-2.3-22b-dev.safetensors": ModelEntry("Lightricks/LTX-2.3", comfy_type="checkpoints"),
|
| 37 |
+
"ltx-2.3-spatial-upscaler-x2-1.0.safetensors": ModelEntry(
|
|
|
|
|
|
|
| 38 |
"Lightricks/LTX-2.3", comfy_type="latent_upscale_models"
|
| 39 |
),
|
| 40 |
"ltx-2.3-22b-distilled-lora-384.safetensors": ModelEntry(
|
tests/conftest.py
CHANGED
|
@@ -132,7 +132,7 @@ def fake_hf_cache(tmp_path: pathlib.Path) -> pathlib.Path:
|
|
| 132 |
layouts = {
|
| 133 |
"models--Lightricks--LTX-2.3": [
|
| 134 |
"ltx-2.3-22b-distilled.safetensors",
|
| 135 |
-
"ltx-2.3-spatial-upscaler-x2-1.
|
| 136 |
"ltx-2.3-22b-distilled-lora-384.safetensors",
|
| 137 |
],
|
| 138 |
"models--google--gemma-3-12b-it-qat-q4_0-unquantized": [
|
|
|
|
| 132 |
layouts = {
|
| 133 |
"models--Lightricks--LTX-2.3": [
|
| 134 |
"ltx-2.3-22b-distilled.safetensors",
|
| 135 |
+
"ltx-2.3-spatial-upscaler-x2-1.0.safetensors",
|
| 136 |
"ltx-2.3-22b-distilled-lora-384.safetensors",
|
| 137 |
],
|
| 138 |
"models--google--gemma-3-12b-it-qat-q4_0-unquantized": [
|
tests/test_models.py
CHANGED
|
@@ -29,23 +29,6 @@ def test_walk_workflow_for_models_finds_t2v_loaders():
|
|
| 29 |
assert any("gemma" in name.lower() for name in needed)
|
| 30 |
|
| 31 |
|
| 32 |
-
def test_model_registry_has_no_retired_filenames():
|
| 33 |
-
"""A registry key that 404s on the Hub makes every generation abort mid-graph."""
|
| 34 |
-
assert "ltx-2.3-spatial-upscaler-x2-1.0.safetensors" not in models.MODEL_REGISTRY
|
| 35 |
-
assert "ltx-2.3-spatial-upscaler-x2-1.1.safetensors" in models.MODEL_REGISTRY
|
| 36 |
-
entry = models.MODEL_REGISTRY["ltx-2.3-spatial-upscaler-x2-1.1.safetensors"]
|
| 37 |
-
assert entry.repo_id == "Lightricks/LTX-2.3"
|
| 38 |
-
assert entry.comfy_type == "latent_upscale_models"
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def test_walk_workflow_for_models_requests_current_upscaler_for_every_mode():
|
| 42 |
-
"""The download pass must fetch the upscaler the workflow actually loads."""
|
| 43 |
-
for mode in workflow.VALID_MODES:
|
| 44 |
-
needed = models.walk_workflow_for_models(workflow.load_template(mode))
|
| 45 |
-
assert "ltx-2.3-spatial-upscaler-x2-1.1.safetensors" in needed
|
| 46 |
-
assert "ltx-2.3-spatial-upscaler-x2-1.0.safetensors" not in needed
|
| 47 |
-
|
| 48 |
-
|
| 49 |
def test_ensure_models_creates_symlinks_local(tmp_path, monkeypatch, fake_hf_cache):
|
| 50 |
"""In local mode, ensure_models creates symlinks from comfy/models -> HF cache."""
|
| 51 |
monkeypatch.setenv("HF_HUB_CACHE", str(fake_hf_cache))
|
|
|
|
| 29 |
assert any("gemma" in name.lower() for name in needed)
|
| 30 |
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
def test_ensure_models_creates_symlinks_local(tmp_path, monkeypatch, fake_hf_cache):
|
| 33 |
"""In local mode, ensure_models creates symlinks from comfy/models -> HF cache."""
|
| 34 |
monkeypatch.setenv("HF_HUB_CACHE", str(fake_hf_cache))
|
tests/test_workflow.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
| 1 |
"""Unit tests for workflow.py — pure functions over JSON dicts."""
|
| 2 |
|
| 3 |
-
import json
|
| 4 |
-
|
| 5 |
import pytest
|
| 6 |
|
| 7 |
import workflow
|
|
@@ -59,23 +57,6 @@ def test_validate_rejects_orphan_link():
|
|
| 59 |
workflow.validate(wf)
|
| 60 |
|
| 61 |
|
| 62 |
-
def test_load_template_remaps_retired_upscaler_filename():
|
| 63 |
-
"""Lightricks deleted the x2 spatial upscaler v1.0; templates still name it."""
|
| 64 |
-
for mode in workflow.VALID_MODES:
|
| 65 |
-
wf = workflow.load_template(mode)
|
| 66 |
-
assert wf["101"]["inputs"]["model_name"] == "ltx-2.3-spatial-upscaler-x2-1.1.safetensors", (
|
| 67 |
-
f"{mode} still loads a retired upscaler"
|
| 68 |
-
)
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
def test_load_template_leaves_no_retired_filenames_in_any_mode():
|
| 72 |
-
"""No retired model filename may reach the workflow handed to PromptExecutor."""
|
| 73 |
-
for mode in workflow.VALID_MODES:
|
| 74 |
-
serialized = json.dumps(workflow.load_template(mode))
|
| 75 |
-
for retired in ("ltx-2.3-spatial-upscaler-x2-1.0.safetensors",):
|
| 76 |
-
assert retired not in serialized, f"{mode} references retired {retired}"
|
| 77 |
-
|
| 78 |
-
|
| 79 |
def test_set_input_handles_dict_widgets_values():
|
| 80 |
"""VHS_* nodes carry dict-style widgets_values; set_input must support str keys."""
|
| 81 |
wf = workflow.load_template("a2v")
|
|
|
|
| 1 |
"""Unit tests for workflow.py — pure functions over JSON dicts."""
|
| 2 |
|
|
|
|
|
|
|
| 3 |
import pytest
|
| 4 |
|
| 5 |
import workflow
|
|
|
|
| 57 |
workflow.validate(wf)
|
| 58 |
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
def test_set_input_handles_dict_widgets_values():
|
| 61 |
"""VHS_* nodes carry dict-style widgets_values; set_input must support str keys."""
|
| 62 |
wf = workflow.load_template("a2v")
|
workflow.py
CHANGED
|
@@ -16,38 +16,13 @@ WORKFLOWS_DIR = pathlib.Path(__file__).parent / "workflows"
|
|
| 16 |
|
| 17 |
VALID_MODES: tuple[str, ...] = ("t2v", "a2v", "i2v", "lipsync", "keyframe", "style")
|
| 18 |
|
| 19 |
-
# Model files the upstream HF repos have deleted, mapped to their replacements.
|
| 20 |
-
# The templates are machine-generated by tools/extract_modes.py from the user's
|
| 21 |
-
# master ComfyUI export, so patching the JSON would be undone on the next
|
| 22 |
-
# re-export — the remap has to live in code. load_template is the only reader of
|
| 23 |
-
# workflows/*.json, which puts it upstream of both consumers: the dict handed to
|
| 24 |
-
# PromptExecutor and models.walk_workflow_for_models' download pass.
|
| 25 |
-
RETIRED_MODEL_RENAMES: dict[str, str] = {
|
| 26 |
-
# Lightricks deleted x2 spatial upscaler v1.0 on 2026-08-10 (v1.1 is the
|
| 27 |
-
# long-video hotfix). Loading the retired name 404s and aborts the graph
|
| 28 |
-
# right after sampling, leaving the user with an empty output.
|
| 29 |
-
"ltx-2.3-spatial-upscaler-x2-1.0.safetensors": "ltx-2.3-spatial-upscaler-x2-1.1.safetensors",
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def _rename_retired_models(value: Any) -> Any:
|
| 34 |
-
"""Return *value* with every retired model filename swapped for its successor."""
|
| 35 |
-
if isinstance(value, str):
|
| 36 |
-
return RETIRED_MODEL_RENAMES.get(value, value)
|
| 37 |
-
if isinstance(value, dict):
|
| 38 |
-
return {k: _rename_retired_models(v) for k, v in value.items()}
|
| 39 |
-
if isinstance(value, list):
|
| 40 |
-
return [_rename_retired_models(v) for v in value]
|
| 41 |
-
return value
|
| 42 |
-
|
| 43 |
|
| 44 |
def load_template(mode: str) -> dict[str, Any]:
|
| 45 |
"""Load a fresh, independent copy of the named mode's API workflow template."""
|
| 46 |
if mode not in VALID_MODES:
|
| 47 |
raise ValueError(f"unknown mode {mode!r}; expected one of {VALID_MODES}")
|
| 48 |
path = WORKFLOWS_DIR / f"{mode}.json"
|
| 49 |
-
|
| 50 |
-
return _rename_retired_models(template)
|
| 51 |
|
| 52 |
|
| 53 |
def set_input(workflow: dict[str, Any], node_id: int | str, field: str, value: Any) -> None:
|
|
|
|
| 16 |
|
| 17 |
VALID_MODES: tuple[str, ...] = ("t2v", "a2v", "i2v", "lipsync", "keyframe", "style")
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
def load_template(mode: str) -> dict[str, Any]:
|
| 21 |
"""Load a fresh, independent copy of the named mode's API workflow template."""
|
| 22 |
if mode not in VALID_MODES:
|
| 23 |
raise ValueError(f"unknown mode {mode!r}; expected one of {VALID_MODES}")
|
| 24 |
path = WORKFLOWS_DIR / f"{mode}.json"
|
| 25 |
+
return copy.deepcopy(json.loads(path.read_text()))
|
|
|
|
| 26 |
|
| 27 |
|
| 28 |
def set_input(workflow: dict[str, Any], node_id: int | str, field: str, value: Any) -> None:
|