techfreakworm commited on
Commit
84c4769
·
unverified ·
1 Parent(s): a30ea36

fix(models): walk_workflow_for_models reads API format

Browse files

The walker was iterating workflow.get('nodes', []) which is editor format;
our workflows are API format ({node_id: {class_type, inputs}}), so the
walk returned an empty set, ensure_models was a no-op, and Spaces
crashed at runtime with 'LTX23_audio_vae_bf16.safetensors not found'.
Locally everything worked because the user's models are symlinked in.

Also unstash the Sway Dance Lesson seed video — *.mp4 in .gitignore was
silently excluding it from the deploy, so VHS_LoadVideo warned at submit
time.

Files changed (1) hide show
  1. models.py +18 -29
models.py CHANGED
@@ -162,44 +162,33 @@ LOADER_NODE_TYPES: tuple[str, ...] = (
162
  )
163
 
164
 
 
 
 
 
165
  def walk_workflow_for_models(workflow: dict) -> set[str]:
166
- """Return the set of model filenames referenced by loader nodes in the workflow.
167
 
168
- Pulls filenames from nodes whose `type` matches a known loader. Filenames are
169
- typically in `widgets_values[0]` (CheckpointLoaderSimple) or in nested rows
170
- (Power Lora Loader). Falls back to scanning all string-valued widget entries
171
- for `*.safetensors` / `*.gguf`.
 
172
  """
173
  needed: set[str] = set()
174
- for node in workflow.get("nodes", []):
175
- if node.get("type") not in LOADER_NODE_TYPES:
 
 
176
  continue
177
- widgets = node.get("widgets_values") or []
178
- for value in _flatten_widget_values(widgets):
179
- if isinstance(value, str) and (
180
- value.endswith(".safetensors")
181
- or value.endswith(".gguf")
182
- or value == "tokenizer.model"
183
- or value.endswith(".json")
184
- ):
185
  needed.add(value)
186
  return needed
187
 
188
 
189
- def _flatten_widget_values(values):
190
- """Walk nested list/dict widget structures, yielding leaf values."""
191
- if isinstance(values, dict):
192
- yield from _flatten_widget_values(list(values.values()))
193
- return
194
- for v in values:
195
- if isinstance(v, (list, tuple)):
196
- yield from _flatten_widget_values(v)
197
- elif isinstance(v, dict):
198
- yield from _flatten_widget_values(list(v.values()))
199
- else:
200
- yield v
201
-
202
-
203
  @dataclass
204
  class DownloadEvent:
205
  filename: str
 
162
  )
163
 
164
 
165
+ _USER_INPUT_LOADERS = {"LoadImage", "VHS_LoadVideo", "VHS_LoadAudioUpload"}
166
+ _MODEL_EXTS = (".safetensors", ".gguf", ".pt", ".bin", ".ckpt")
167
+
168
+
169
  def walk_workflow_for_models(workflow: dict) -> set[str]:
170
+ """Return the set of model filenames referenced by the API-format workflow.
171
 
172
+ Walks `{node_id: {class_type, inputs}}` looking for any input value that
173
+ ends in a model extension. Skips loaders that read user-supplied files
174
+ (LoadImage, VHS_LoadVideo, VHS_LoadAudioUpload). Unknown filenames are
175
+ harmless `ensure_models` log-warns and skips anything not in the
176
+ registry, so being inclusive here costs nothing.
177
  """
178
  needed: set[str] = set()
179
+ for node in workflow.values():
180
+ if not isinstance(node, dict):
181
+ continue
182
+ if node.get("class_type") in _USER_INPUT_LOADERS:
183
  continue
184
+ for value in (node.get("inputs") or {}).values():
185
+ if isinstance(value, str) and value.endswith(_MODEL_EXTS):
186
+ needed.add(value)
187
+ elif isinstance(value, str) and value == "tokenizer.model":
 
 
 
 
188
  needed.add(value)
189
  return needed
190
 
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  @dataclass
193
  class DownloadEvent:
194
  filename: str