Pacific-i64 commited on
Commit
03cff8a
·
verified ·
1 Parent(s): 35b838e

Fix Shuffle 404 with embedded gallery manifest

Browse files
Files changed (5) hide show
  1. .gitignore +2 -0
  2. Dockerfile +1 -1
  3. download_demo_gallery.py +10 -0
  4. landing/index.html +6 -2
  5. validate_space.py +15 -0
.gitignore CHANGED
@@ -1,3 +1,5 @@
1
  __pycache__/
2
  *.py[cod]
3
  .cache/
 
 
 
1
  __pycache__/
2
  *.py[cod]
3
  .cache/
4
+ landing/gallery/
5
+ landing/gallery-manifest.js
Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
  FROM python:3.11-slim
2
 
3
- LABEL org.opencontainers.image.revision="space-startup-idempotency-v2"
4
 
5
  ENV DEBIAN_FRONTEND=noninteractive \
6
  PIP_NO_CACHE_DIR=1 \
 
1
  FROM python:3.11-slim
2
 
3
+ LABEL org.opencontainers.image.revision="embedded-gallery-manifest-v3"
4
 
5
  ENV DEBIAN_FRONTEND=noninteractive \
6
  PIP_NO_CACHE_DIR=1 \
download_demo_gallery.py CHANGED
@@ -12,6 +12,7 @@ from huggingface_hub import snapshot_download
12
 
13
  ROOT = Path(__file__).resolve().parent
14
  GALLERY = ROOT / "landing" / "gallery"
 
15
  REPO_ID = os.environ.get(
16
  "TRHASH_DEMO_IMAGE_REPO",
17
  "Pacific-i64/TR-HASH-Vision-v8-Demo-Images",
@@ -48,6 +49,15 @@ def main() -> None:
48
  raise RuntimeError(f"Invalid demo image payload: {filename}")
49
  shutil.copyfile(source, GALLERY / filename)
50
 
 
 
 
 
 
 
 
 
 
51
  print(f"Downloaded {len(images)} demo images from {REPO_ID}")
52
 
53
 
 
12
 
13
  ROOT = Path(__file__).resolve().parent
14
  GALLERY = ROOT / "landing" / "gallery"
15
+ GALLERY_SCRIPT = ROOT / "landing" / "gallery-manifest.js"
16
  REPO_ID = os.environ.get(
17
  "TRHASH_DEMO_IMAGE_REPO",
18
  "Pacific-i64/TR-HASH-Vision-v8-Demo-Images",
 
49
  raise RuntimeError(f"Invalid demo image payload: {filename}")
50
  shutil.copyfile(source, GALLERY / filename)
51
 
52
+ # Embed the manifest in the landing page assets. Shuffle must remain usable
53
+ # even when a proxy/cache layer does not expose the JSON route yet.
54
+ GALLERY_SCRIPT.write_text(
55
+ "window.__TR_HASH_DEMO_GALLERY__ = "
56
+ + json.dumps(manifest, ensure_ascii=False, separators=(",", ":"))
57
+ + ";\n",
58
+ encoding="utf-8",
59
+ )
60
+
61
  print(f"Downloaded {len(images)} demo images from {REPO_ID}")
62
 
63
 
landing/index.html CHANGED
@@ -359,6 +359,7 @@
359
  <p class="note">Final checkpoint from the first 50-epoch pretraining phase. The public Space runs on CPU; the first request after wake-up also loads the checkpoint and can take longer.</p>
360
  </div>
361
 
 
362
  <script>
363
  const elements = {
364
  input: document.querySelector("#image-input"),
@@ -435,7 +436,7 @@
435
  }
436
  elements.galleryAttribution.append(`COCO image ${image.image_id} · `);
437
  const source = document.createElement("a");
438
- source.href = image.source_url;
439
  source.target = "_blank";
440
  source.rel = "noopener";
441
  source.textContent = "source";
@@ -504,7 +505,10 @@
504
 
505
  async function loadGallery() {
506
  if (galleryLoaded) return;
507
- const manifest = await requestJson("/gallery/manifest.json", {cache: "force-cache"});
 
 
 
508
  galleryImages = manifest.images || [];
509
  galleryLoaded = true;
510
  refillGalleryDeck();
 
359
  <p class="note">Final checkpoint from the first 50-epoch pretraining phase. The public Space runs on CPU; the first request after wake-up also loads the checkpoint and can take longer.</p>
360
  </div>
361
 
362
+ <script src="/gallery-manifest.js?v=20260816-5"></script>
363
  <script>
364
  const elements = {
365
  input: document.querySelector("#image-input"),
 
436
  }
437
  elements.galleryAttribution.append(`COCO image ${image.image_id} · `);
438
  const source = document.createElement("a");
439
+ source.href = `https://huggingface.co/datasets/Pacific-i64/TR-HASH-Vision-v8-Demo-Images/blob/main/images/${encodeURIComponent(image.file)}`;
440
  source.target = "_blank";
441
  source.rel = "noopener";
442
  source.textContent = "source";
 
505
 
506
  async function loadGallery() {
507
  if (galleryLoaded) return;
508
+ const manifest = window.__TR_HASH_DEMO_GALLERY__;
509
+ if (!manifest || !Array.isArray(manifest.images)) {
510
+ throw new Error("The embedded demo image bank is unavailable");
511
+ }
512
  galleryImages = manifest.images || [];
513
  galleryLoaded = true;
514
  refillGalleryDeck();
validate_space.py CHANGED
@@ -11,7 +11,9 @@ from urllib.request import urlopen
11
  ROOT = Path(__file__).resolve().parent
12
  WORKFLOW_PATH = ROOT / "workflows" / "TR-HASH-Vision-v8-COCO-api.json"
13
  GALLERY_PATH = ROOT / "landing" / "gallery"
 
14
  ENTRYPOINT_PATH = ROOT / "entrypoint.sh"
 
15
 
16
 
17
  def validate_entrypoint_contract() -> None:
@@ -39,6 +41,14 @@ def load_json(path: Path) -> dict:
39
 
40
  def validate_static() -> set[str]:
41
  validate_entrypoint_contract()
 
 
 
 
 
 
 
 
42
  workflow = load_json(WORKFLOW_PATH)
43
  node_types = {node["class_type"] for node in workflow.values()}
44
  required = {"LoadImage", "TRHashLoadModel", "TRHashDetect", "TRHashOutput"}
@@ -64,6 +74,11 @@ def validate_static() -> set[str]:
64
  invalid.append(f"{name} (not JPEG)")
65
  if invalid:
66
  raise RuntimeError(f"Gallery contains invalid image payloads: {invalid[:5]}")
 
 
 
 
 
67
  return node_types
68
 
69
 
 
11
  ROOT = Path(__file__).resolve().parent
12
  WORKFLOW_PATH = ROOT / "workflows" / "TR-HASH-Vision-v8-COCO-api.json"
13
  GALLERY_PATH = ROOT / "landing" / "gallery"
14
+ GALLERY_SCRIPT_PATH = ROOT / "landing" / "gallery-manifest.js"
15
  ENTRYPOINT_PATH = ROOT / "entrypoint.sh"
16
+ LANDING_PATH = ROOT / "landing" / "index.html"
17
 
18
 
19
  def validate_entrypoint_contract() -> None:
 
41
 
42
  def validate_static() -> set[str]:
43
  validate_entrypoint_contract()
44
+ landing = LANDING_PATH.read_text(encoding="utf-8")
45
+ manifest_tag = '<script src="/gallery-manifest.js?v=20260816-5"></script>'
46
+ if manifest_tag not in landing:
47
+ raise RuntimeError("The landing page must load the embedded gallery manifest")
48
+ if landing.index(manifest_tag) > landing.index("window.__TR_HASH_DEMO_GALLERY__"):
49
+ raise RuntimeError("The gallery manifest must load before Shuffle reads it")
50
+ if 'requestJson("/gallery/manifest.json"' in landing:
51
+ raise RuntimeError("Shuffle must not depend on a runtime manifest request")
52
  workflow = load_json(WORKFLOW_PATH)
53
  node_types = {node["class_type"] for node in workflow.values()}
54
  required = {"LoadImage", "TRHashLoadModel", "TRHashDetect", "TRHashOutput"}
 
74
  invalid.append(f"{name} (not JPEG)")
75
  if invalid:
76
  raise RuntimeError(f"Gallery contains invalid image payloads: {invalid[:5]}")
77
+ embedded_manifest = GALLERY_SCRIPT_PATH.read_text(encoding="utf-8")
78
+ if not embedded_manifest.startswith("window.__TR_HASH_DEMO_GALLERY__ = "):
79
+ raise RuntimeError("The landing page is missing its embedded gallery manifest")
80
+ if embedded_manifest.count('"file":') != 300:
81
+ raise RuntimeError("The embedded gallery manifest must contain 300 images")
82
  return node_types
83
 
84