fffiloni commited on
Commit
70eda8d
·
verified ·
1 Parent(s): d63d1b2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -54
app.py CHANGED
@@ -19,8 +19,7 @@ os.environ.setdefault("DIFFUSERS_CACHE", os.path.join(_ASF_HF_CACHE_ROOT, "diffu
19
  os.environ.setdefault("HF_MODULES_CACHE", "/tmp/hf_modules")
20
  os.environ.setdefault("MPLCONFIGDIR", "/tmp/matplotlib")
21
 
22
- # Compatibility mode: avoid optional hub-kernels path unless explicitly enabled.
23
- # The first failure you saw came from transformers -> hub_kernels -> kernels.
24
  os.environ.setdefault("DIFFUSERS_ENABLE_HUB_KERNELS", "0")
25
  os.environ.setdefault("USE_HUB_KERNELS", "NO")
26
 
@@ -58,8 +57,8 @@ def _spaces_gpu(*args, **kwargs):
58
  """
59
  Wrapper around spaces.GPU.
60
 
61
- Some spaces versions may not support size=...
62
- In that case, fall back to the same decorator without size.
63
  """
64
  try:
65
  return spaces.GPU(*args, **kwargs)
@@ -73,14 +72,15 @@ def _spaces_gpu(*args, **kwargs):
73
  # ---------------------------------------------------------------------------
74
 
75
  import sys
 
76
  import threading
77
  import traceback
78
 
79
  import torch
80
 
81
  # ZeroGPU does not support torch.compile.
82
- # Krea remote code compiles FlexAttention at import/load time.
83
- # Therefore we no-op torch.compile globally for this Space.
84
  _ORIG_TORCH_COMPILE = getattr(torch, "compile", None)
85
 
86
 
@@ -159,6 +159,7 @@ def _runtime_report():
159
  "python": sys.version.replace("\n", " "),
160
  "torch": getattr(torch, "__version__", "unknown"),
161
  "cuda_available": bool(torch.cuda.is_available()),
 
162
  "has_spaces": HAS_SPACES,
163
  "torch_compile_bypassed": torch.compile is _asf_zerogpu_compile_bypass,
164
  "hf_home": os.environ.get("HF_HOME", ""),
@@ -166,13 +167,47 @@ def _runtime_report():
166
  }
167
 
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  def _load_pipeline():
170
  """
171
- Lazy-load the ModularPipeline.
172
 
173
- Important for ZeroGPU:
174
- - The GPU is only actually allocated inside @spaces.GPU-decorated functions.
175
- - Therefore loading inside generate() is safer than loading at module import.
 
 
176
  """
177
  global _pipeline, _pipeline_error
178
 
@@ -189,7 +224,7 @@ def _load_pipeline():
189
  _log(f"Runtime report: {_runtime_report()}")
190
  _log(f"Loading ModularPipeline from {MODEL_ID} ...")
191
 
192
- pipe = ModularPipeline.from_pretrained(
193
  MODEL_ID,
194
  trust_remote_code=True,
195
  token=HF_TOKEN,
@@ -197,9 +232,10 @@ def _load_pipeline():
197
 
198
  _log("Skeleton loaded; attaching components ...")
199
 
200
- # Primary path: device_map='cuda', as the Krea model card expects.
201
  try:
202
- pipe.load_components(
 
203
  trust_remote_code=True,
204
  device_map="cuda",
205
  torch_dtype={
@@ -215,16 +251,18 @@ def _load_pipeline():
215
  or "No CUDA GPUs are available" in msg
216
  or "libcudart" in msg
217
  or "CUDA error" in msg
 
218
  )
219
 
220
  if cuda_load_failed:
221
  _log(
222
- "device_map='cuda' failed at module level. "
223
- "Retrying with CPU-load + manual .to('cuda') ..."
224
  )
225
  _log(f"CUDA load error was: {msg}")
226
 
227
- pipe.load_components(
 
228
  trust_remote_code=True,
229
  torch_dtype={
230
  "default": torch.bfloat16,
@@ -233,13 +271,12 @@ def _load_pipeline():
233
  token=HF_TOKEN,
234
  )
235
 
236
- # On ZeroGPU, CUDA should be active inside @spaces.GPU.
237
  pipe = pipe.to("cuda")
238
  else:
239
  raise
240
 
241
- # Krea model-card optimization: fuse Q/K/V projections.
242
- # This is not torch.compile and should be safe.
243
  try:
244
  if hasattr(pipe, "transformer") and hasattr(pipe.transformer, "blocks"):
245
  fused = 0
@@ -264,9 +301,11 @@ def _load_pipeline():
264
  return None
265
 
266
 
267
- # Optional eager load for non-ZeroGPU debugging only.
268
- # Keep default lazy for ZeroGPU.
269
- if os.environ.get("ASF_LOAD_AT_STARTUP") == "1" and os.environ.get("SKIP_MODEL_LOAD") != "1":
 
 
270
  _load_pipeline()
271
 
272
 
@@ -276,7 +315,7 @@ if os.environ.get("ASF_LOAD_AT_STARTUP") == "1" and os.environ.get("SKIP_MODEL_L
276
 
277
  def health():
278
  return {
279
- "status": "ok" if _pipeline is not None else "not_loaded",
280
  "model_ready": _pipeline is not None,
281
  "pipeline_ready": _pipeline is not None,
282
  "model_id": MODEL_ID,
@@ -289,21 +328,51 @@ def health():
289
  }
290
 
291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  # ---------------------------------------------------------------------------
293
  # Generation endpoint — real inference guarded by @spaces.GPU
294
  # ---------------------------------------------------------------------------
295
 
296
  def _gpu_duration(prompt, num_blocks, num_inference_steps, seed):
297
- # Reserve enough time for lazy load + forward passes.
298
- # Keep conservative for ZeroGPU quota/queue behavior.
299
  try:
300
  blocks = int(num_blocks)
301
  steps = int(num_inference_steps)
302
  except Exception:
303
- blocks = 1
304
  steps = 4
305
 
306
- return min(900, max(300, 180 + blocks * steps * 20))
 
 
 
 
 
 
 
 
307
 
308
 
309
  @_spaces_gpu(duration=_gpu_duration, size="xlarge")
@@ -321,26 +390,19 @@ def generate(prompt, num_blocks, num_inference_steps, seed):
321
  num_inference_steps = int(num_inference_steps)
322
  seed = int(seed)
323
 
324
- # Conservative bounds for ZeroGPU compatibility mode.
325
- if num_blocks < 1 or num_blocks > 3:
326
- raise ValueError("num_blocks must be between 1 and 3 in ZeroGPU compatibility mode.")
327
 
328
  if num_inference_steps < 1 or num_inference_steps > 8:
329
- raise ValueError(
330
- "num_inference_steps must be between 1 and 8 in ZeroGPU compatibility mode."
331
- )
332
 
333
  device = "cuda"
334
 
335
- # Ensure the pipeline is on CUDA inside the ZeroGPU-decorated function.
336
  try:
337
- if hasattr(pipe, "device"):
338
- current = str(pipe.device)
339
- if "cuda" not in current:
340
- _log("Moving pipeline to cuda ...")
341
- pipe.to(device)
342
  except Exception as e:
343
- _log(f"Pipeline device check/move warning: {type(e).__name__}: {e}")
344
 
345
  frames = []
346
  state = PipelineState()
@@ -380,7 +442,7 @@ def generate(prompt, num_blocks, num_inference_steps, seed):
380
  if not frames:
381
  raise RuntimeError("No frames were generated.")
382
 
383
- output_path = "/tmp/krea_output.mp4"
384
  export_to_video(frames, output_path, fps=24)
385
 
386
  _log(f"Saved video to {output_path}")
@@ -394,11 +456,12 @@ def generate(prompt, num_blocks, num_inference_steps, seed):
394
  with gr.Blocks(title="Krea Realtime Video 14B") as demo:
395
  gr.Markdown(
396
  "# Krea Realtime Video 14B\n\n"
397
- "This Space attempts **real local inference** for the Krea Realtime 14B "
398
- "text-to-video model using the Diffusers `ModularPipeline` path.\n\n"
399
  "⚠️ **ZeroGPU compatibility mode**: `torch.compile` is disabled because "
400
  "ZeroGPU does not support it. This may be slower than the optimized Krea runtime.\n\n"
401
- "Start with 1 block and 4 steps to validate the runtime before increasing settings."
 
402
  )
403
 
404
  with gr.Row():
@@ -411,10 +474,10 @@ with gr.Blocks(title="Krea Realtime Video 14B") as demo:
411
 
412
  num_blocks = gr.Slider(
413
  minimum=1,
414
- maximum=3,
415
- value=1,
416
  step=1,
417
- label="Number of Blocks (ZeroGPU-safe range)",
418
  )
419
 
420
  num_inference_steps = gr.Slider(
@@ -427,16 +490,20 @@ with gr.Blocks(title="Krea Realtime Video 14B") as demo:
427
 
428
  seed = gr.Number(value=42, precision=0, label="Seed")
429
 
430
- generate_btn = gr.Button("Generate Video", variant="primary")
 
 
 
 
431
 
432
  with gr.Column():
433
  output_video = gr.Video(label="Generated Video")
434
 
435
  gr.Examples(
436
  examples=[
437
- ["a cat sitting on a boat", 1, 4, 42],
438
- ["a futuristic city at sunset", 1, 4, 123],
439
- ["a panda playing guitar in a forest", 1, 4, 7],
440
  ],
441
  inputs=[prompt, num_blocks, num_inference_steps, seed],
442
  outputs=output_video,
@@ -444,6 +511,13 @@ with gr.Blocks(title="Krea Realtime Video 14B") as demo:
444
  cache_examples=False,
445
  )
446
 
 
 
 
 
 
 
 
447
  generate_btn.click(
448
  generate,
449
  inputs=[prompt, num_blocks, num_inference_steps, seed],
@@ -451,11 +525,10 @@ with gr.Blocks(title="Krea Realtime Video 14B") as demo:
451
  api_name="generate",
452
  )
453
 
454
- # Structured health endpoint.
455
  demo.load(
456
  lambda: health(),
457
- None,
458
- gr.JSON(),
459
  api_name="health",
460
  )
461
 
 
19
  os.environ.setdefault("HF_MODULES_CACHE", "/tmp/hf_modules")
20
  os.environ.setdefault("MPLCONFIGDIR", "/tmp/matplotlib")
21
 
22
+ # Keep hub kernels disabled in this ZeroGPU compatibility mode.
 
23
  os.environ.setdefault("DIFFUSERS_ENABLE_HUB_KERNELS", "0")
24
  os.environ.setdefault("USE_HUB_KERNELS", "NO")
25
 
 
57
  """
58
  Wrapper around spaces.GPU.
59
 
60
+ Some versions of the spaces package may not support size=...
61
+ In that case, we fall back to the same decorator without size.
62
  """
63
  try:
64
  return spaces.GPU(*args, **kwargs)
 
72
  # ---------------------------------------------------------------------------
73
 
74
  import sys
75
+ import time
76
  import threading
77
  import traceback
78
 
79
  import torch
80
 
81
  # ZeroGPU does not support torch.compile.
82
+ # Krea remote code compiles torch.nn.attention.flex_attention.
83
+ # We no-op torch.compile globally for this compatibility Space.
84
  _ORIG_TORCH_COMPILE = getattr(torch, "compile", None)
85
 
86
 
 
159
  "python": sys.version.replace("\n", " "),
160
  "torch": getattr(torch, "__version__", "unknown"),
161
  "cuda_available": bool(torch.cuda.is_available()),
162
+ "cuda_device_count": int(torch.cuda.device_count()) if torch.cuda.is_available() else 0,
163
  "has_spaces": HAS_SPACES,
164
  "torch_compile_bypassed": torch.compile is _asf_zerogpu_compile_bypass,
165
  "hf_home": os.environ.get("HF_HOME", ""),
 
167
  }
168
 
169
 
170
+ def _call_from_pretrained_compat(*args, **kwargs):
171
+ """
172
+ Compatibility wrapper because some diffusers/HF Hub combinations
173
+ may use token= while older ones expect use_auth_token= or no token.
174
+ """
175
+ try:
176
+ return ModularPipeline.from_pretrained(*args, **kwargs)
177
+ except TypeError as e:
178
+ if "token" in str(e):
179
+ kwargs.pop("token", None)
180
+ if HF_TOKEN:
181
+ kwargs["use_auth_token"] = HF_TOKEN
182
+ return ModularPipeline.from_pretrained(*args, **kwargs)
183
+ raise
184
+
185
+
186
+ def _load_components_compat(pipe, **kwargs):
187
+ """
188
+ Compatibility wrapper around pipe.load_components().
189
+ """
190
+ try:
191
+ return pipe.load_components(**kwargs)
192
+ except TypeError as e:
193
+ msg = str(e)
194
+ if "token" in msg:
195
+ kwargs.pop("token", None)
196
+ if HF_TOKEN:
197
+ kwargs["use_auth_token"] = HF_TOKEN
198
+ return pipe.load_components(**kwargs)
199
+ raise
200
+
201
+
202
  def _load_pipeline():
203
  """
204
+ Load the ModularPipeline once at app startup.
205
 
206
+ For ZeroGPU, this is the preferred UX path: the app warms up at runtime,
207
+ while actual generation remains protected by @spaces.GPU.
208
+
209
+ If startup loading fails because CUDA is not fully available yet,
210
+ generate() will retry loading under @spaces.GPU.
211
  """
212
  global _pipeline, _pipeline_error
213
 
 
224
  _log(f"Runtime report: {_runtime_report()}")
225
  _log(f"Loading ModularPipeline from {MODEL_ID} ...")
226
 
227
+ pipe = _call_from_pretrained_compat(
228
  MODEL_ID,
229
  trust_remote_code=True,
230
  token=HF_TOKEN,
 
232
 
233
  _log("Skeleton loaded; attaching components ...")
234
 
235
+ # Primary path: Krea model-card style.
236
  try:
237
+ _load_components_compat(
238
+ pipe,
239
  trust_remote_code=True,
240
  device_map="cuda",
241
  torch_dtype={
 
251
  or "No CUDA GPUs are available" in msg
252
  or "libcudart" in msg
253
  or "CUDA error" in msg
254
+ or "CUDA driver" in msg
255
  )
256
 
257
  if cuda_load_failed:
258
  _log(
259
+ "device_map='cuda' failed during startup. "
260
+ "Retrying CPU-load + manual .to('cuda') ..."
261
  )
262
  _log(f"CUDA load error was: {msg}")
263
 
264
+ _load_components_compat(
265
+ pipe,
266
  trust_remote_code=True,
267
  torch_dtype={
268
  "default": torch.bfloat16,
 
271
  token=HF_TOKEN,
272
  )
273
 
 
274
  pipe = pipe.to("cuda")
275
  else:
276
  raise
277
 
278
+ # Krea model-card optimization: fuse projections.
279
+ # This is safe; it is not torch.compile.
280
  try:
281
  if hasattr(pipe, "transformer") and hasattr(pipe.transformer, "blocks"):
282
  fused = 0
 
301
  return None
302
 
303
 
304
+ # ---------------------------------------------------------------------------
305
+ # Eager app runtime warm-up
306
+ # ---------------------------------------------------------------------------
307
+
308
+ if os.environ.get("SKIP_MODEL_LOAD") != "1":
309
  _load_pipeline()
310
 
311
 
 
315
 
316
  def health():
317
  return {
318
+ "status": "ready" if _pipeline is not None else "not_loaded",
319
  "model_ready": _pipeline is not None,
320
  "pipeline_ready": _pipeline is not None,
321
  "model_id": MODEL_ID,
 
328
  }
329
 
330
 
331
+ def warmup_model():
332
+ """
333
+ Manual warm-up button.
334
+
335
+ Usually the model is already loaded at app startup.
336
+ This remains useful if startup load failed and we want to retry from the UI.
337
+ """
338
+ pipe = _load_pipeline()
339
+ if pipe is None:
340
+ return {
341
+ "status": "error",
342
+ "error": _pipeline_error or "Pipeline failed to load",
343
+ "runtime": _runtime_report(),
344
+ }
345
+
346
+ return {
347
+ "status": "ready",
348
+ "model_id": MODEL_ID,
349
+ "runtime_mode": "zerogpu_compatibility_compile_bypass",
350
+ "message": "Model loaded and cached in this Space process.",
351
+ "runtime": _runtime_report(),
352
+ }
353
+
354
+
355
  # ---------------------------------------------------------------------------
356
  # Generation endpoint — real inference guarded by @spaces.GPU
357
  # ---------------------------------------------------------------------------
358
 
359
  def _gpu_duration(prompt, num_blocks, num_inference_steps, seed):
 
 
360
  try:
361
  blocks = int(num_blocks)
362
  steps = int(num_inference_steps)
363
  except Exception:
364
+ blocks = 3
365
  steps = 4
366
 
367
+ # Model is loaded at app startup.
368
+ # Duration only covers generation.
369
+ # Aggressive ZeroGPU reservation:
370
+ # 1x4 -> 30s
371
+ # 3x4 -> 51s
372
+ # 6x4 -> 87s
373
+ # 9x4 -> 123s
374
+ # 9x8 -> 180s cap
375
+ return min(180, max(30, 15 + blocks * steps * 3))
376
 
377
 
378
  @_spaces_gpu(duration=_gpu_duration, size="xlarge")
 
390
  num_inference_steps = int(num_inference_steps)
391
  seed = int(seed)
392
 
393
+ if num_blocks < 1 or num_blocks > 9:
394
+ raise ValueError("num_blocks must be between 1 and 9.")
 
395
 
396
  if num_inference_steps < 1 or num_inference_steps > 8:
397
+ raise ValueError("num_inference_steps must be between 1 and 8.")
 
 
398
 
399
  device = "cuda"
400
 
401
+ # Make sure the pipeline is on CUDA inside the ZeroGPU-decorated function.
402
  try:
403
+ pipe = pipe.to(device)
 
 
 
 
404
  except Exception as e:
405
+ _log(f"Pipeline .to('cuda') warning: {type(e).__name__}: {e}")
406
 
407
  frames = []
408
  state = PipelineState()
 
442
  if not frames:
443
  raise RuntimeError("No frames were generated.")
444
 
445
+ output_path = f"/tmp/krea_output_{int(time.time())}.mp4"
446
  export_to_video(frames, output_path, fps=24)
447
 
448
  _log(f"Saved video to {output_path}")
 
456
  with gr.Blocks(title="Krea Realtime Video 14B") as demo:
457
  gr.Markdown(
458
  "# Krea Realtime Video 14B\n\n"
459
+ "This Space runs **real local inference** for the Krea Realtime 14B "
460
+ "text-to-video model using Diffusers `ModularPipeline`.\n\n"
461
  "⚠️ **ZeroGPU compatibility mode**: `torch.compile` is disabled because "
462
  "ZeroGPU does not support it. This may be slower than the optimized Krea runtime.\n\n"
463
+ "**Video length** is controlled by the number of blocks. "
464
+ "Roughly: 1 block ≈ ~1 second, 3 blocks ≈ ~3 seconds, 9 blocks ≈ ~9 seconds."
465
  )
466
 
467
  with gr.Row():
 
474
 
475
  num_blocks = gr.Slider(
476
  minimum=1,
477
+ maximum=9,
478
+ value=3,
479
  step=1,
480
+ label="Video Length / Number of Blocks",
481
  )
482
 
483
  num_inference_steps = gr.Slider(
 
490
 
491
  seed = gr.Number(value=42, precision=0, label="Seed")
492
 
493
+ with gr.Row():
494
+ warmup_btn = gr.Button("Load / Warm up model", variant="secondary")
495
+ generate_btn = gr.Button("Generate Video", variant="primary")
496
+
497
+ warmup_status = gr.JSON(label="Model Status")
498
 
499
  with gr.Column():
500
  output_video = gr.Video(label="Generated Video")
501
 
502
  gr.Examples(
503
  examples=[
504
+ ["a cat sitting on a boat", 3, 4, 42],
505
+ ["a futuristic city at sunset", 3, 4, 123],
506
+ ["a panda playing guitar in a forest", 3, 4, 7],
507
  ],
508
  inputs=[prompt, num_blocks, num_inference_steps, seed],
509
  outputs=output_video,
 
511
  cache_examples=False,
512
  )
513
 
514
+ warmup_btn.click(
515
+ warmup_model,
516
+ inputs=None,
517
+ outputs=warmup_status,
518
+ api_name="warmup",
519
+ )
520
+
521
  generate_btn.click(
522
  generate,
523
  inputs=[prompt, num_blocks, num_inference_steps, seed],
 
525
  api_name="generate",
526
  )
527
 
 
528
  demo.load(
529
  lambda: health(),
530
+ inputs=None,
531
+ outputs=warmup_status,
532
  api_name="health",
533
  )
534