techfreakworm commited on
Commit
8b4f49e
·
unverified ·
1 Parent(s): e168977

fix(models): walker recurses into Power Lora Loader nested dict + spatial upscaler comfy_type

Browse files

Two issues surfaced when models actually downloaded but the workflow still
failed at execution:

1. walk_workflow_for_models was a flat values() loop — Power Lora Loader
stores rows as inputs.lora_1 = {on, lora, strength} (nested dict), so
ltx-2.3-22b-distilled-lora-dynamic_fro09_avg_rank_105_bf16.safetensors
was never returned to ensure_models. Recurse through dicts/lists.

2. ltx-2.3-spatial-upscaler-x2-1.0.safetensors was registered with
comfy_type="upscale_models" but ComfyUI core's
LatentUpscaleModelLoader reads from "latent_upscale_models" — same
filesystem prefix, different folder. Fix the registry to match.

Files changed (1) hide show
  1. models.py +25 -11
models.py CHANGED
@@ -35,7 +35,7 @@ MODEL_REGISTRY: dict[str, ModelEntry] = {
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="upscale_models"
39
  ),
40
  "ltx-2.3-22b-distilled-lora-384.safetensors": ModelEntry(
41
  "Lightricks/LTX-2.3", comfy_type="loras"
@@ -171,14 +171,32 @@ _USER_INPUT_LOADERS = {"LoadImage", "VHS_LoadVideo", "VHS_LoadAudioUpload"}
171
  _MODEL_EXTS = (".safetensors", ".gguf", ".pt", ".bin", ".ckpt")
172
 
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  def walk_workflow_for_models(workflow: dict) -> set[str]:
175
  """Return the set of model filenames referenced by the API-format workflow.
176
 
177
- Walks `{node_id: {class_type, inputs}}` looking for any input value that
178
- ends in a model extension. Skips loaders that read user-supplied files
179
- (LoadImage, VHS_LoadVideo, VHS_LoadAudioUpload). Unknown filenames are
180
- harmless — `ensure_models` log-warns and skips anything not in the
181
- registry, so being inclusive here costs nothing.
182
  """
183
  needed: set[str] = set()
184
  for node in workflow.values():
@@ -186,11 +204,7 @@ def walk_workflow_for_models(workflow: dict) -> set[str]:
186
  continue
187
  if node.get("class_type") in _USER_INPUT_LOADERS:
188
  continue
189
- for value in (node.get("inputs") or {}).values():
190
- if isinstance(value, str) and value.endswith(_MODEL_EXTS):
191
- needed.add(value)
192
- elif isinstance(value, str) and value == "tokenizer.model":
193
- needed.add(value)
194
  return needed
195
 
196
 
 
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(
41
  "Lightricks/LTX-2.3", comfy_type="loras"
 
171
  _MODEL_EXTS = (".safetensors", ".gguf", ".pt", ".bin", ".ckpt")
172
 
173
 
174
+ def _walk_for_filenames(value, into: set[str]) -> None:
175
+ """Depth-first walk of a node's inputs, picking out model filenames.
176
+
177
+ Power Lora Loader stores its rows nested as `inputs.lora_1 = {on, lora,
178
+ strength}` and similar — a flat values() loop misses these. Recurse
179
+ through dicts and lists/tuples so nested filenames are caught.
180
+ """
181
+ if isinstance(value, str):
182
+ if value.endswith(_MODEL_EXTS) or value == "tokenizer.model":
183
+ into.add(value)
184
+ elif isinstance(value, dict):
185
+ for v in value.values():
186
+ _walk_for_filenames(v, into)
187
+ elif isinstance(value, (list, tuple)):
188
+ for v in value:
189
+ _walk_for_filenames(v, into)
190
+
191
+
192
  def walk_workflow_for_models(workflow: dict) -> set[str]:
193
  """Return the set of model filenames referenced by the API-format workflow.
194
 
195
+ Walks `{node_id: {class_type, inputs}}` and recursively scans each node's
196
+ inputs for strings ending in a model extension. Skips loaders that read
197
+ user-supplied files (LoadImage, VHS_LoadVideo, VHS_LoadAudioUpload).
198
+ Unknown filenames are harmless — `ensure_models` log-warns and skips
199
+ anything not in the registry, so being inclusive here costs nothing.
200
  """
201
  needed: set[str] = set()
202
  for node in workflow.values():
 
204
  continue
205
  if node.get("class_type") in _USER_INPUT_LOADERS:
206
  continue
207
+ _walk_for_filenames(node.get("inputs") or {}, needed)
 
 
 
 
208
  return needed
209
 
210