Spaces:
Running
Running
Restore runtime.py from Make-It-Animatable
Browse files- runtime.py +107 -0
runtime.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import importlib.metadata
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
import platform
|
| 6 |
+
import sys
|
| 7 |
+
import time
|
| 8 |
+
import uuid
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
ROOT = Path("/data")
|
| 12 |
+
UPSTREAM = Path("/opt/space/upstream")
|
| 13 |
+
MODEL_ROOT = ROOT / "models/make-it-animatable"
|
| 14 |
+
WEIGHTS = {"bw.pth":424693698, "bw_normal.pth":429008352,
|
| 15 |
+
"joints.pth":525833771, "joints_coarse.pth":424706373,
|
| 16 |
+
"pose.pth":525848433}
|
| 17 |
+
|
| 18 |
+
def configure_storage():
|
| 19 |
+
if not ROOT.is_dir():
|
| 20 |
+
raise RuntimeError("/data is not mounted. Mount the existing persistent bucket at /data.")
|
| 21 |
+
for name in ("cache/huggingface","cache/torch","cache/xdg","cache/pip",
|
| 22 |
+
"models","inputs","outputs","temp","logs"):
|
| 23 |
+
(ROOT/name).mkdir(parents=True,exist_ok=True)
|
| 24 |
+
os.environ.update(HF_HOME=str(ROOT/"cache/huggingface"),
|
| 25 |
+
HF_HUB_CACHE=str(ROOT/"cache/huggingface/hub"),
|
| 26 |
+
TORCH_HOME=str(ROOT/"cache/torch"),
|
| 27 |
+
XDG_CACHE_HOME=str(ROOT/"cache/xdg"),
|
| 28 |
+
PIP_CACHE_DIR=str(ROOT/"cache/pip"),
|
| 29 |
+
TMPDIR=str(ROOT/"temp"),
|
| 30 |
+
GRADIO_TEMP_DIR=str(ROOT/"temp/gradio"))
|
| 31 |
+
probe = ROOT/"temp"/("write-check-"+uuid.uuid4().hex)
|
| 32 |
+
with probe.open("x") as stream:
|
| 33 |
+
stream.write("ok")
|
| 34 |
+
if probe.read_text() != "ok":
|
| 35 |
+
raise RuntimeError("/data read/write verification failed")
|
| 36 |
+
probe.unlink()
|
| 37 |
+
import tempfile
|
| 38 |
+
tempfile.tempdir = str(ROOT/"temp")
|
| 39 |
+
missing = [str(MODEL_ROOT/"output/best/new"/name) for name,size in WEIGHTS.items()
|
| 40 |
+
if not (MODEL_ROOT/"output/best/new"/name).is_file()
|
| 41 |
+
or (MODEL_ROOT/"output/best/new"/name).stat().st_size != size]
|
| 42 |
+
bones = MODEL_ROOT/"data/Mixamo/bones.fbx"
|
| 43 |
+
if not bones.is_file() or bones.stat().st_size == 0:
|
| 44 |
+
missing.append(str(bones))
|
| 45 |
+
if missing:
|
| 46 |
+
raise RuntimeError("Required cached assets missing or incomplete; startup stopped without "
|
| 47 |
+
"overwriting or redownloading assets: "+", ".join(missing))
|
| 48 |
+
for name in ("data","output"):
|
| 49 |
+
target = UPSTREAM/name
|
| 50 |
+
expected = MODEL_ROOT/name
|
| 51 |
+
if target.is_symlink():
|
| 52 |
+
if target.resolve() != expected.resolve():
|
| 53 |
+
raise RuntimeError("Unexpected asset link: "+str(target))
|
| 54 |
+
elif target.exists():
|
| 55 |
+
raise RuntimeError("Asset path must be an image-managed symlink: "+str(target))
|
| 56 |
+
else:
|
| 57 |
+
target.symlink_to(expected, target_is_directory=True)
|
| 58 |
+
print("STORAGE_READY /data writable; cached weights reused; downloads=0; installs=0",flush=True)
|
| 59 |
+
|
| 60 |
+
def startup_checks():
|
| 61 |
+
import torch
|
| 62 |
+
import torch_cluster
|
| 63 |
+
import pytorch3d
|
| 64 |
+
from pytorch3d.ops import sample_farthest_points
|
| 65 |
+
versions = {}
|
| 66 |
+
for name in ("torch","torchvision","torch-cluster","torch-geometric",
|
| 67 |
+
"torch-scatter","torch-sparse","torch-spline-conv","pytorch3d","numpy","gradio"):
|
| 68 |
+
try:
|
| 69 |
+
versions[name] = importlib.metadata.version(name)
|
| 70 |
+
except importlib.metadata.PackageNotFoundError:
|
| 71 |
+
versions[name] = "not required / not installed"
|
| 72 |
+
report = dict(python=platform.python_version(),packages=versions,cuda=torch.version.cuda,
|
| 73 |
+
cuda_available=torch.cuda.is_available(),
|
| 74 |
+
gpu=torch.cuda.get_device_name(0) if torch.cuda.is_available() else None)
|
| 75 |
+
print("RUNTIME_VERSIONS "+json.dumps(report),flush=True)
|
| 76 |
+
if not torch.cuda.is_available():
|
| 77 |
+
raise RuntimeError("CUDA GPU unavailable. Dedicated GPU required; CPU inference is disabled.")
|
| 78 |
+
if torch.__version__ != "2.1.2+cu121" or torch.version.cuda != "12.1":
|
| 79 |
+
raise RuntimeError("Runtime Torch/CUDA differs from the pinned build")
|
| 80 |
+
x = torch.rand((128,3),device="cuda")
|
| 81 |
+
out = torch_cluster.fps(x,ratio=0.25)
|
| 82 |
+
_, indices = sample_farthest_points(x[None],K=32)
|
| 83 |
+
y = torch.ones((32,32),device="cuda") @ torch.ones((32,32),device="cuda")
|
| 84 |
+
torch.cuda.synchronize()
|
| 85 |
+
if len(out) != 32 or indices.shape != (1,32) or y[0,0].item() != 32:
|
| 86 |
+
raise RuntimeError("CUDA binary smoke test failed")
|
| 87 |
+
report["cuda_execution"] = "PASS: matmul, torch_cluster.fps, pytorch3d FPS"
|
| 88 |
+
persistence = {}
|
| 89 |
+
for name in ("cache/huggingface","cache/torch","cache/xdg","cache/pip","inputs","outputs"):
|
| 90 |
+
marker = ROOT/name/".persistence-check"
|
| 91 |
+
existed = marker.is_file()
|
| 92 |
+
if not existed:
|
| 93 |
+
with marker.open("x") as stream:
|
| 94 |
+
stream.write(uuid.uuid4().hex)
|
| 95 |
+
persistence[name] = {"marker":marker.read_text(),"survived_previous_start":existed}
|
| 96 |
+
report["persistence"] = persistence
|
| 97 |
+
report["cached_weights"] = WEIGHTS
|
| 98 |
+
report["startup_time"] = time.time()
|
| 99 |
+
report["startup_id"] = uuid.uuid4().hex
|
| 100 |
+
report["outputs_present"] = sorted(str(p.relative_to(ROOT)) for p in (ROOT/"outputs").glob("*"))
|
| 101 |
+
previous = ROOT/"logs/last-startup.json"
|
| 102 |
+
if previous.is_file():
|
| 103 |
+
report["previous_startup"] = json.loads(previous.read_text()).get("startup_id")
|
| 104 |
+
(ROOT/"logs"/(report["startup_id"]+".json")).write_text(json.dumps(report,indent=2))
|
| 105 |
+
previous.write_text(json.dumps(report,indent=2))
|
| 106 |
+
print("CUDA_EXECUTION_PASS "+json.dumps(report),flush=True)
|
| 107 |
+
return report
|