techfreakworm commited on
Commit
d35d872
·
unverified ·
1 Parent(s): bce3dbc

feat(spaces): registry entries for renamed BF16 aliases + seed inputs + bootstrap

Browse files

- MODEL_REGISTRY: 6 new entries for the renamed-but-actually-BF16 files the
master workflow references (gemma_3_12B_it_fp4_mixed → Comfy-Org/ltx-2,
_transformer_only_fp8_scaled → Kijai _bf16, ltx-2-3-22b-dev-Q4_K_M.gguf →
unsloth -BF16.gguf, taeltx2_3, distilled-lora-dynamic, gemma_3_12B_it).
ModelEntry gets `source_filename` so the HF-side name and the
workflow-expected name can differ.
- ensure_models honors source_filename, joins subfolder into the HF path,
and stages at comfyui/models/<comfy_type>/<workflow_filename>.
- requirements.txt: add ComfyUI core deps + custom-node deps that are
required for the LTX 2.3 workflow but aren't in their nodes' own
requirements.txt (gguf, imageio_ffmpeg, opencv-python, matplotlib,
diffusers, yt-dlp, psutil).
- assets/seed_inputs/: tiny placeholder image/audio/video files that the
bootstrap stages into comfyui/input/ so the workflow's always-on loaders
don't fail on cold start when the user hasn't uploaded yet.
- _bootstrap also pip-installs ComfyUI's own requirements.txt on Spaces
cold start (previously only custom-node deps were installed).

app.py CHANGED
@@ -52,6 +52,7 @@ def _bootstrap() -> None:
52
  comfy_dir = pathlib.Path("/data/comfyui" if on_spaces else "comfyui")
53
 
54
  if on_spaces and not comfy_dir.exists():
 
55
  comfy_dir.parent.mkdir(parents=True, exist_ok=True)
56
  _git_clone(COMFYUI_REPO, comfy_dir, ref=COMFYUI_COMMIT)
57
  for node_url, node_ref in CUSTOM_NODES_PINNED:
@@ -59,10 +60,16 @@ def _bootstrap() -> None:
59
  _git_clone(node_url, comfy_dir / "custom_nodes" / name, ref=node_ref)
60
  import subprocess
61
 
62
- for cn in (comfy_dir / "custom_nodes").iterdir():
63
- req = cn / "requirements.txt"
64
- if req.exists():
65
- subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", str(req)])
 
 
 
 
 
 
66
 
67
  if str(comfy_dir) not in sys.path:
68
  sys.path.insert(0, str(comfy_dir))
@@ -71,6 +78,26 @@ def _bootstrap() -> None:
71
  str(pathlib.Path("/data/models") if on_spaces else (comfy_dir / "models")),
72
  )
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  _bootstrap()
76
 
 
52
  comfy_dir = pathlib.Path("/data/comfyui" if on_spaces else "comfyui")
53
 
54
  if on_spaces and not comfy_dir.exists():
55
+ print(f"[bootstrap] cold start on Spaces; cloning ComfyUI to {comfy_dir}", flush=True)
56
  comfy_dir.parent.mkdir(parents=True, exist_ok=True)
57
  _git_clone(COMFYUI_REPO, comfy_dir, ref=COMFYUI_COMMIT)
58
  for node_url, node_ref in CUSTOM_NODES_PINNED:
 
60
  _git_clone(node_url, comfy_dir / "custom_nodes" / name, ref=node_ref)
61
  import subprocess
62
 
63
+ # ComfyUI core requirements + each custom node's requirements
64
+ for req_path in [
65
+ comfy_dir / "requirements.txt",
66
+ *(cn / "requirements.txt" for cn in (comfy_dir / "custom_nodes").iterdir()),
67
+ ]:
68
+ if req_path.exists():
69
+ print(f"[bootstrap] pip install -r {req_path}", flush=True)
70
+ subprocess.check_call(
71
+ [sys.executable, "-m", "pip", "install", "--quiet", "-r", str(req_path)]
72
+ )
73
 
74
  if str(comfy_dir) not in sys.path:
75
  sys.path.insert(0, str(comfy_dir))
 
78
  str(pathlib.Path("/data/models") if on_spaces else (comfy_dir / "models")),
79
  )
80
 
81
+ # Stage placeholder input files so the workflow's hard-referenced loaders
82
+ # (LoadImage/VHS_Load*) don't error at runtime even when the active mode
83
+ # doesn't actually use the file. Real user uploads are placed alongside via
84
+ # `_stage_to_comfy_input` later.
85
+ seed_dir = pathlib.Path(__file__).parent / "assets" / "seed_inputs"
86
+ inputs_dir = comfy_dir / "input"
87
+ inputs_dir.mkdir(parents=True, exist_ok=True)
88
+ if seed_dir.exists():
89
+ import shutil
90
+
91
+ for src in seed_dir.iterdir():
92
+ if not src.is_file():
93
+ continue
94
+ dst = inputs_dir / src.name
95
+ if not dst.exists():
96
+ try:
97
+ shutil.copy2(src, dst)
98
+ except OSError as exc:
99
+ print(f"[bootstrap] could not seed {src.name}: {exc}", flush=True)
100
+
101
 
102
  _bootstrap()
103
 
assets/seed_inputs/5.FLF2.png ADDED
assets/seed_inputs/IMG-20210721-WA0008.jpg ADDED
assets/seed_inputs/beauty_pagent_dialogue.mp3 ADDED
Binary file (17 kB). View file
 
assets/seed_inputs/influencer_mic_hd.png ADDED
models.py CHANGED
@@ -20,8 +20,14 @@ logger = logging.getLogger(__name__)
20
  @dataclass(frozen=True)
21
  class ModelEntry:
22
  repo_id: str
23
- subfolder: str = ""
24
  comfy_type: str = "checkpoints" # ComfyUI models/<comfy_type>/ subdirectory
 
 
 
 
 
 
25
 
26
 
27
  MODEL_REGISTRY: dict[str, ModelEntry] = {
@@ -94,6 +100,51 @@ MODEL_REGISTRY: dict[str, ModelEntry] = {
94
  "jib-down",
95
  )
96
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
 
99
 
@@ -202,23 +253,25 @@ def ensure_models(filenames: set[str]) -> Iterator[DownloadEvent]:
202
  entry = MODEL_REGISTRY[filename]
203
 
204
  # Short-circuit: if the file is already present at its expected location
205
- # in comfyui/models/<type>/[<subfolder>/]<filename>, skip the HF dance
206
- # entirely. This is the local-dev hot path where the user's existing
207
- # ComfyUI models/ is symlinked in.
208
- existing_dest_dir = comfy_models / entry.comfy_type
209
- if entry.subfolder:
210
- existing_dest_dir = existing_dest_dir / entry.subfolder
211
- existing_dest = existing_dest_dir / filename
212
  if existing_dest.exists() or existing_dest.is_symlink():
213
  yield DownloadEvent(filename, 0.0, 0.0)
214
  continue
215
 
216
- # Resolve source: hf_hub_download returns the cache path (or downloads).
 
 
 
 
 
217
  try:
218
  source = pathlib.Path(
219
  hf_hub_download(
220
  repo_id=entry.repo_id,
221
- filename=filename,
222
  cache_dir=str(cache_dir),
223
  local_dir=None,
224
  )
@@ -226,22 +279,21 @@ def ensure_models(filenames: set[str]) -> Iterator[DownloadEvent]:
226
  size_mb = source.stat().st_size / 1024 / 1024
227
  yield DownloadEvent(filename, size_mb, size_mb)
228
  except Exception as exc:
229
- # Fall back to scanning the cache for a placeholder file (test mode + offline mode).
230
- candidates = list(cache_dir.rglob(filename))
 
 
231
  if not candidates:
232
  logger.warning(
233
- "could not download or locate %r in HF cache: %s; skipping",
234
- filename,
235
- exc,
236
  )
237
  continue
238
  source = candidates[0]
239
  yield DownloadEvent(filename, 0.0, 0.0)
240
 
241
- # Build symlink target inside comfy_models
242
  dest_dir = comfy_models / entry.comfy_type
243
- if entry.subfolder:
244
- dest_dir = dest_dir / entry.subfolder
245
  dest_dir.mkdir(parents=True, exist_ok=True)
246
  dest = dest_dir / filename
247
 
 
20
  @dataclass(frozen=True)
21
  class ModelEntry:
22
  repo_id: str
23
+ subfolder: str = "" # path within the HF repo
24
  comfy_type: str = "checkpoints" # ComfyUI models/<comfy_type>/ subdirectory
25
+ # If the workflow expects a different filename than what's in the HF repo
26
+ # (e.g. user's local "ltx-2.3-22b-dev_transformer_only_fp8_scaled.safetensors"
27
+ # is actually `_transformer_only_bf16.safetensors` in Kijai's repo), set
28
+ # source_filename to the actual repo filename. The local symlink/copy uses
29
+ # the registry key as its name.
30
+ source_filename: str | None = None
31
 
32
 
33
  MODEL_REGISTRY: dict[str, ModelEntry] = {
 
100
  "jib-down",
101
  )
102
  },
103
+ # ----- Renamed/aliased filenames the user's master workflow references.
104
+ # The names look like quantized variants (FP4, FP8, GGUF) but the actual
105
+ # bytes behind them are BF16 — the user's local setup uses symlinks to
106
+ # canonical sources. On Spaces we download the same canonical sources via
107
+ # huggingface_hub and place them under the workflow-expected filename.
108
+ # All of these entries set `subfolder` to the path within the repo and
109
+ # rely on hf_hub_download returning the cached snapshot path (which we
110
+ # then symlink to comfy_models/<comfy_type>/<filename>).
111
+ "gemma_3_12B_it_fp4_mixed.safetensors": ModelEntry(
112
+ # Comfy-Org/ltx-2 ships BF16 Gemma packed as `gemma_3_12B_it.safetensors`
113
+ # in split_files/text_encoders/. The workflow expects the FP4-named
114
+ # variant; we serve the same file under that name.
115
+ "Comfy-Org/ltx-2",
116
+ subfolder="split_files/text_encoders",
117
+ comfy_type="text_encoders",
118
+ source_filename="gemma_3_12B_it.safetensors",
119
+ ),
120
+ "gemma_3_12B_it.safetensors": ModelEntry(
121
+ "Comfy-Org/ltx-2",
122
+ subfolder="split_files/text_encoders",
123
+ comfy_type="text_encoders",
124
+ ),
125
+ "ltx-2.3-22b-dev_transformer_only_fp8_scaled.safetensors": ModelEntry(
126
+ # Kijai's BF16 transformer-only — actual repo filename has `_bf16` suffix.
127
+ "Kijai/LTX2.3_comfy",
128
+ subfolder="diffusion_models",
129
+ comfy_type="diffusion_models",
130
+ source_filename="ltx-2.3-22b-dev_transformer_only_bf16.safetensors",
131
+ ),
132
+ "ltx-2-3-22b-dev-Q4_K_M.gguf": ModelEntry(
133
+ # Unsloth's GGUF in BF16 (named `…-BF16.gguf` in repo).
134
+ "unsloth/LTX-2.3-GGUF",
135
+ comfy_type="diffusion_models",
136
+ source_filename="ltx-2.3-22b-dev-BF16.gguf",
137
+ ),
138
+ "taeltx2_3.safetensors": ModelEntry(
139
+ "Kijai/LTX2.3_comfy",
140
+ subfolder="vae",
141
+ comfy_type="vae",
142
+ ),
143
+ "ltx-2.3-22b-distilled-lora-dynamic_fro09_avg_rank_105_bf16.safetensors": ModelEntry(
144
+ "Kijai/LTX2.3_comfy",
145
+ subfolder="loras",
146
+ comfy_type="loras",
147
+ ),
148
  }
149
 
150
 
 
253
  entry = MODEL_REGISTRY[filename]
254
 
255
  # Short-circuit: if the file is already present at its expected location
256
+ # comfyui/models/<comfy_type>/<filename>, skip. Subfolder is part of the
257
+ # HF source path, not the destination, so the dest is always a flat
258
+ # comfyui/models/<comfy_type>/<filename>.
259
+ existing_dest = comfy_models / entry.comfy_type / filename
 
 
 
260
  if existing_dest.exists() or existing_dest.is_symlink():
261
  yield DownloadEvent(filename, 0.0, 0.0)
262
  continue
263
 
264
+ # The HF-side filename may differ from the workflow-expected name
265
+ # (e.g. user's `_fp8_scaled.safetensors` is actually `_bf16.safetensors`
266
+ # in the upstream repo). Honor `source_filename` when set.
267
+ hf_filename = entry.source_filename or filename
268
+ hf_path = f"{entry.subfolder}/{hf_filename}" if entry.subfolder else hf_filename
269
+
270
  try:
271
  source = pathlib.Path(
272
  hf_hub_download(
273
  repo_id=entry.repo_id,
274
+ filename=hf_path,
275
  cache_dir=str(cache_dir),
276
  local_dir=None,
277
  )
 
279
  size_mb = source.stat().st_size / 1024 / 1024
280
  yield DownloadEvent(filename, size_mb, size_mb)
281
  except Exception as exc:
282
+ # Fall back to scanning the cache for a matching file (test mode +
283
+ # offline mode). Look for either the workflow filename OR the
284
+ # HF-side filename — both might exist locally as symlinks.
285
+ candidates = list(cache_dir.rglob(filename)) or list(cache_dir.rglob(hf_filename))
286
  if not candidates:
287
  logger.warning(
288
+ "could not download or locate %r (hf=%r) in HF cache: %s; skipping",
289
+ filename, hf_filename, exc,
 
290
  )
291
  continue
292
  source = candidates[0]
293
  yield DownloadEvent(filename, 0.0, 0.0)
294
 
295
+ # Stage at comfy_models/<comfy_type>/<filename> (workflow-expected name).
296
  dest_dir = comfy_models / entry.comfy_type
 
 
297
  dest_dir.mkdir(parents=True, exist_ok=True)
298
  dest = dest_dir / filename
299
 
requirements.txt CHANGED
@@ -1,6 +1,8 @@
1
  gradio>=5.0,<6.0
2
  spaces>=0.30.0
3
  huggingface_hub>=0.27.0
 
 
4
  torch>=2.4.0
5
  torchvision
6
  torchaudio
@@ -10,6 +12,43 @@ einops
10
  safetensors
11
  tqdm
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  # Dev / test
14
  pytest>=8.0
15
  pytest-asyncio>=0.23
 
1
  gradio>=5.0,<6.0
2
  spaces>=0.30.0
3
  huggingface_hub>=0.27.0
4
+ psutil # used by app.py model status badge
5
+
6
  torch>=2.4.0
7
  torchvision
8
  torchaudio
 
12
  safetensors
13
  tqdm
14
 
15
+ # ComfyUI core requirements (also pip-installed by setup.sh from comfyui/requirements.txt
16
+ # locally, but Spaces won't run setup.sh — these are the deps ComfyUI itself needs)
17
+ transformers>=4.50,<6
18
+ tokenizers
19
+ sentencepiece
20
+ av
21
+ kornia
22
+ spandrel
23
+ torchsde
24
+ scipy
25
+ aiohttp
26
+ pydantic
27
+ pydantic-settings
28
+ python-dotenv
29
+ yarl
30
+ PyOpenGL
31
+ glfw
32
+ SQLAlchemy
33
+ alembic
34
+ Mako
35
+ networkx
36
+ sympy
37
+ mpmath
38
+ blake3
39
+ comfyui-frontend-package>=1.42
40
+ comfyui-workflow-templates>=0.9
41
+ comfyui-embedded-docs
42
+
43
+ # Custom-node deps that we discovered are required for the LTX 2.3 workflow
44
+ # but aren't auto-installed by their respective nodes' requirements.txt.
45
+ gguf # ComfyUI-GGUF (UnetLoaderGGUF)
46
+ imageio_ffmpeg # ComfyUI-VideoHelperSuite (video write/read backend)
47
+ opencv-python # ComfyUI_LayerStyle, multiple custom nodes
48
+ matplotlib # comfyui_controlnet_aux dwpose / pose preprocessors
49
+ diffusers # ComfyUI-SeedVR2 (used during init even when the node isn't called)
50
+ yt-dlp # ComfyUI-MediaMixer (init-time import)
51
+
52
  # Dev / test
53
  pytest>=8.0
54
  pytest-asyncio>=0.23