Davidscut commited on
Commit
28777ad
·
1 Parent(s): 0f1c8f6
Files changed (2) hide show
  1. app.py +8 -3
  2. inference.py +61 -37
app.py CHANGED
@@ -1,6 +1,7 @@
1
  from __future__ import annotations
2
 
3
  import base64
 
4
  import random
5
  import time
6
  import traceback
@@ -50,6 +51,7 @@ APP_DIR = Path(__file__).resolve().parent
50
  LOGO_PATH = APP_DIR / "assets" / "tryon_logo_compact.png"
51
  SHOWCASE_DIR = APP_DIR / "assets" / "showcase"
52
  SHOWCASE_IMAGES = [SHOWCASE_DIR / f"case_{idx}.png" for idx in range(4)]
 
53
 
54
  def _image_data_uri(path: Path) -> str:
55
  if not path.exists():
@@ -298,12 +300,12 @@ def _predict_from_prompt_files(prompt: str, files):
298
  )
299
 
300
 
301
- @spaces.GPU(size="xlarge", duration=180)
302
  def predict_from_inputs(prompt: str, files):
303
  return _predict_from_prompt_files(prompt, files)
304
 
305
 
306
- @spaces.GPU(size="xlarge", duration=180)
307
  def predict_from_message(message):
308
  prompt, files = _message_to_prompt_files(message)
309
  return _predict_from_prompt_files(prompt, files)
@@ -674,8 +676,11 @@ with gr.Blocks(title="JoyAI Virtual Try-On", css=CSS) as demo:
674
  inputs=[prompt_box],
675
  outputs=predict_outputs,
676
  api_name="tryon",
 
 
 
677
  )
678
 
679
 
680
  if __name__ == "__main__":
681
- demo.queue(max_size=20).launch(show_error=True)
 
1
  from __future__ import annotations
2
 
3
  import base64
4
+ import os
5
  import random
6
  import time
7
  import traceback
 
51
  LOGO_PATH = APP_DIR / "assets" / "tryon_logo_compact.png"
52
  SHOWCASE_DIR = APP_DIR / "assets" / "showcase"
53
  SHOWCASE_IMAGES = [SHOWCASE_DIR / f"case_{idx}.png" for idx in range(4)]
54
+ ZERO_GPU_DURATION_SECONDS = int(os.getenv("ZERO_GPU_DURATION_SECONDS", "240"))
55
 
56
  def _image_data_uri(path: Path) -> str:
57
  if not path.exists():
 
300
  )
301
 
302
 
303
+ @spaces.GPU(size="xlarge", duration=ZERO_GPU_DURATION_SECONDS)
304
  def predict_from_inputs(prompt: str, files):
305
  return _predict_from_prompt_files(prompt, files)
306
 
307
 
308
+ @spaces.GPU(size="xlarge", duration=ZERO_GPU_DURATION_SECONDS)
309
  def predict_from_message(message):
310
  prompt, files = _message_to_prompt_files(message)
311
  return _predict_from_prompt_files(prompt, files)
 
676
  inputs=[prompt_box],
677
  outputs=predict_outputs,
678
  api_name="tryon",
679
+ trigger_mode="once",
680
+ concurrency_limit=1,
681
+ concurrency_id="tryon_gpu",
682
  )
683
 
684
 
685
  if __name__ == "__main__":
686
+ demo.queue(max_size=4, default_concurrency_limit=1).launch(show_error=True)
inference.py CHANGED
@@ -10,6 +10,7 @@ import random
10
  import re
11
  import sys
12
  import contextlib
 
13
  import time
14
  from dataclasses import dataclass
15
  from pathlib import Path
@@ -73,9 +74,14 @@ class LoadedTryOnModel:
73
 
74
  _MODEL: LoadedTryOnModel | None = None
75
  _MODEL_DIR: Path | None = None
 
76
  _PE_CACHE: dict[str, str] = {}
77
 
78
 
 
 
 
 
79
  def _get_bool_env(name: str, default: bool = False) -> bool:
80
  value = os.getenv(name)
81
  if value is None:
@@ -502,43 +508,61 @@ def load_model() -> LoadedTryOnModel:
502
  if _MODEL is not None:
503
  return _MODEL
504
 
505
- _MODEL_DIR = _download_model_repo()
506
- _add_code_paths(_MODEL_DIR)
507
-
508
- from xvideo.config import load_config_class_from_pyfile
509
- from xvideo.models import load_dit, load_pipeline
510
-
511
- config_path = _find_default_config(_MODEL_DIR)
512
- transformer_path = _find_transformer_path(_MODEL_DIR)
513
- text_encoder_path = _resolve_text_encoder_path(_MODEL_DIR)
514
- processor_path = _resolve_processor_path(_MODEL_DIR)
515
- tokenizer_path = _resolve_tokenizer_path(_MODEL_DIR)
516
-
517
- config_class = load_config_class_from_pyfile(str(config_path))
518
- cfg = config_class()
519
- cfg.dit_ckpt = str(transformer_path)
520
- cfg.dit_ckpt_type = os.getenv(
521
- "JOY_DIT_CKPT_TYPE",
522
- "safetensor" if transformer_path.is_dir() else getattr(cfg, "dit_ckpt_type", "pt"),
523
- )
524
- cfg.training_mode = False
525
- cfg.use_fsdp_inference = False
526
- cfg.hsdp_shard_dim = 1
527
- _patch_config_paths(cfg, _MODEL_DIR)
528
-
529
- device = _device()
530
- with _patch_split_firered_paths(
531
- text_encoder_path=text_encoder_path,
532
- processor_path=processor_path,
533
- tokenizer_path=tokenizer_path,
534
- ):
535
- dit = load_dit(cfg, device=device)
536
- dit.requires_grad_(False)
537
- dit.eval()
538
- pipeline = load_pipeline(cfg, dit, device)
539
-
540
- _MODEL = LoadedTryOnModel(pipeline=pipeline, cfg=cfg, device=device, model_dir=_MODEL_DIR)
541
- return _MODEL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
 
544
  def get_bucket_size(img: Image.Image) -> tuple[int, int]:
 
10
  import re
11
  import sys
12
  import contextlib
13
+ import threading
14
  import time
15
  from dataclasses import dataclass
16
  from pathlib import Path
 
74
 
75
  _MODEL: LoadedTryOnModel | None = None
76
  _MODEL_DIR: Path | None = None
77
+ _MODEL_LOCK = threading.Lock()
78
  _PE_CACHE: dict[str, str] = {}
79
 
80
 
81
+ def _log_stage(message: str) -> None:
82
+ print(f"[tryon] {time.strftime('%Y-%m-%d %H:%M:%S')} | {message}", flush=True)
83
+
84
+
85
  def _get_bool_env(name: str, default: bool = False) -> bool:
86
  value = os.getenv(name)
87
  if value is None:
 
508
  if _MODEL is not None:
509
  return _MODEL
510
 
511
+ with _MODEL_LOCK:
512
+ if _MODEL is not None:
513
+ return _MODEL
514
+
515
+ started_at = time.time()
516
+ _log_stage("load_model start")
517
+ _MODEL_DIR = _download_model_repo()
518
+ _add_code_paths(_MODEL_DIR)
519
+
520
+ from xvideo.config import load_config_class_from_pyfile
521
+ from xvideo.models import load_dit, load_pipeline
522
+
523
+ config_path = _find_default_config(_MODEL_DIR)
524
+ transformer_path = _find_transformer_path(_MODEL_DIR)
525
+ text_encoder_path = _resolve_text_encoder_path(_MODEL_DIR)
526
+ processor_path = _resolve_processor_path(_MODEL_DIR)
527
+ tokenizer_path = _resolve_tokenizer_path(_MODEL_DIR)
528
+ _log_stage(f"resolved config={config_path}")
529
+ _log_stage(f"resolved transformer={transformer_path}")
530
+ _log_stage(f"resolved text_encoder={text_encoder_path}")
531
+ _log_stage(f"resolved processor={processor_path}")
532
+ _log_stage(f"resolved tokenizer={tokenizer_path}")
533
+
534
+ config_class = load_config_class_from_pyfile(str(config_path))
535
+ cfg = config_class()
536
+ cfg.dit_ckpt = str(transformer_path)
537
+ cfg.dit_ckpt_type = os.getenv(
538
+ "JOY_DIT_CKPT_TYPE",
539
+ "safetensor" if transformer_path.is_dir() else getattr(cfg, "dit_ckpt_type", "pt"),
540
+ )
541
+ cfg.training_mode = False
542
+ cfg.use_fsdp_inference = False
543
+ cfg.hsdp_shard_dim = 1
544
+ _patch_config_paths(cfg, _MODEL_DIR)
545
+
546
+ device = _device()
547
+ _log_stage(f"loading DiT on {device}")
548
+ with _patch_split_firered_paths(
549
+ text_encoder_path=text_encoder_path,
550
+ processor_path=processor_path,
551
+ tokenizer_path=tokenizer_path,
552
+ ):
553
+ dit_started_at = time.time()
554
+ dit = load_dit(cfg, device=device)
555
+ _log_stage(f"loaded DiT in {time.time() - dit_started_at:.1f}s")
556
+ dit.requires_grad_(False)
557
+ dit.eval()
558
+ pipeline_started_at = time.time()
559
+ _log_stage("loading pipeline")
560
+ pipeline = load_pipeline(cfg, dit, device)
561
+ _log_stage(f"loaded pipeline in {time.time() - pipeline_started_at:.1f}s")
562
+
563
+ _MODEL = LoadedTryOnModel(pipeline=pipeline, cfg=cfg, device=device, model_dir=_MODEL_DIR)
564
+ _log_stage(f"load_model done in {time.time() - started_at:.1f}s")
565
+ return _MODEL
566
 
567
 
568
  def get_bucket_size(img: Image.Image) -> tuple[int, int]: