FrankHUP commited on
Commit
aaccd1d
·
1 Parent(s): d627632

local and hg setup

Browse files
Files changed (8) hide show
  1. app.py +5 -0
  2. app_ui.py +2 -3
  3. config.py +9 -56
  4. device_utils.py +18 -0
  5. env.py +24 -0
  6. runtime.py +3 -4
  7. search_service.py +10 -7
  8. spaces_compat.py +5 -4
app.py CHANGED
@@ -1,5 +1,10 @@
1
  from __future__ import annotations
2
 
 
 
 
 
 
3
  from app_ui import build_demo
4
 
5
  demo = build_demo()
 
1
  from __future__ import annotations
2
 
3
+ from env import IS_HF_SPACE
4
+
5
+ if IS_HF_SPACE:
6
+ import spaces # noqa: F401
7
+
8
  from app_ui import build_demo
9
 
10
  demo = build_demo()
app_ui.py CHANGED
@@ -8,12 +8,12 @@ from config import (
8
  K_PER_ANGLE_DEFAULT,
9
  TOPK_DEFAULT,
10
  )
 
11
  from runtime import RUNTIME
12
  from search_service import run_search
13
- from spaces_compat import GPU
14
 
15
 
16
- @GPU(duration=120)
17
  def run_search_entry(
18
  image,
19
  top_k,
@@ -41,7 +41,6 @@ def run_search_entry(
41
  a270=a270,
42
  )
43
 
44
-
45
  def build_demo() -> gr.Blocks:
46
  with gr.Blocks(title="MouseBrain 2D→3D Demo") as demo:
47
  gr.Markdown(
 
8
  K_PER_ANGLE_DEFAULT,
9
  TOPK_DEFAULT,
10
  )
11
+ from env import gpu_decorator
12
  from runtime import RUNTIME
13
  from search_service import run_search
 
14
 
15
 
16
+ @gpu_decorator(duration=120)
17
  def run_search_entry(
18
  image,
19
  top_k,
 
41
  a270=a270,
42
  )
43
 
 
44
  def build_demo() -> gr.Blocks:
45
  with gr.Blocks(title="MouseBrain 2D→3D Demo") as demo:
46
  gr.Markdown(
config.py CHANGED
@@ -3,55 +3,10 @@ from __future__ import annotations
3
  import os
4
  from pathlib import Path
5
 
6
- import torch
7
-
8
-
9
- def _env_flag(name: str, default: bool = False) -> bool:
10
- value = os.getenv(name)
11
- if value is None:
12
- return default
13
- return value.strip().lower() in {"1", "true", "yes", "on"}
14
-
15
-
16
- def _resolve_device() -> str:
17
- requested = os.getenv("MOUSE_DEMO_DEVICE", "auto").strip().lower()
18
-
19
- if requested == "auto":
20
- return "cuda" if torch.cuda.is_available() else "cpu"
21
-
22
- if requested == "cuda":
23
- return "cuda" if torch.cuda.is_available() else "cpu"
24
-
25
- if requested in {"cpu", "mps"}:
26
- return requested
27
-
28
- return "cpu"
29
-
30
-
31
  APP_DIR = Path(__file__).resolve().parent
32
  CACHE_DIR = Path(os.getenv("MOUSE_DEMO_CACHE", APP_DIR / ".cache"))
33
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
34
 
35
- # Environment
36
- IS_HF_SPACE = _env_flag("SPACE_ID", False) or _env_flag("HF_SPACE", False)
37
- LOCAL_DEV = _env_flag("MOUSE_DEMO_LOCAL_DEV", not IS_HF_SPACE)
38
-
39
- # Runtime
40
- DEVICE = _resolve_device()
41
- TOPK_DEFAULT = int(os.getenv("TOPK_DEFAULT", "12"))
42
- K_PER_ANGLE_DEFAULT = int(os.getenv("K_PER_ANGLE_DEFAULT", "100"))
43
- DEFAULT_RERANK_TOPK = int(os.getenv("DEFAULT_RERANK_TOPK", "100"))
44
- DEFAULT_RERANK_ALPHA = float(os.getenv("DEFAULT_RERANK_ALPHA", "1.0"))
45
- DEFAULT_ANGLES = (0.0, 90.0, 180.0, 270.0)
46
-
47
- # Unload behavior
48
- # In local dev, keep models loaded unless explicitly overridden.
49
- if "IDLE_UNLOAD_SECONDS" in os.environ:
50
- IDLE_UNLOAD_SECONDS = int(os.environ["IDLE_UNLOAD_SECONDS"])
51
- else:
52
- IDLE_UNLOAD_SECONDS = 0 if LOCAL_DEV else 120
53
-
54
- # Remote artifacts
55
  GCS_BASE = os.getenv(
56
  "GCS_BASE",
57
  "https://storage.googleapis.com/mouse-rag/demo/v2",
@@ -62,20 +17,18 @@ MANIFEST_URL = f"{GCS_BASE}/patch_manifest.parquet"
62
  VECTORS_URL = f"{GCS_BASE}/patch_vectors.npy"
63
  PATCH_PNG_BASE = f"{GCS_BASE}/patch_png"
64
 
65
- # Optional local overrides for development
66
- FAISS_LOCAL_OVERRIDE = os.getenv("FAISS_LOCAL_OVERRIDE")
67
- MANIFEST_LOCAL_OVERRIDE = os.getenv("MANIFEST_LOCAL_OVERRIDE")
68
- VECTORS_LOCAL_OVERRIDE = os.getenv("VECTORS_LOCAL_OVERRIDE")
69
- PATCH_PNG_BASE_OVERRIDE = os.getenv("PATCH_PNG_BASE_OVERRIDE")
70
-
71
- # Hugging Face reranker repo
72
  RERANKER_REPO_ID = os.getenv("RERANKER_REPO_ID", "FrankHUP/mousebrain-reranker")
73
  RERANKER_FILENAME = os.getenv("RERANKER_FILENAME", "reranker_listwise.pt")
74
-
75
- # DINO model id
76
  DINO_MODEL_ID = os.getenv("DINO_MODEL_ID", "facebook/dinov3-vitb16-pretrain-lvd1689m")
77
 
78
- # Cached artifact paths
79
  FAISS_LOCAL = CACHE_DIR / "patch_index.faiss"
80
  MANIFEST_LOCAL = CACHE_DIR / "patch_manifest.parquet"
81
- VECTORS_LOCAL = CACHE_DIR / "patch_vectors.npy"
 
 
 
 
 
 
 
 
 
3
  import os
4
  from pathlib import Path
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  APP_DIR = Path(__file__).resolve().parent
7
  CACHE_DIR = Path(os.getenv("MOUSE_DEMO_CACHE", APP_DIR / ".cache"))
8
  CACHE_DIR.mkdir(parents=True, exist_ok=True)
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  GCS_BASE = os.getenv(
11
  "GCS_BASE",
12
  "https://storage.googleapis.com/mouse-rag/demo/v2",
 
17
  VECTORS_URL = f"{GCS_BASE}/patch_vectors.npy"
18
  PATCH_PNG_BASE = f"{GCS_BASE}/patch_png"
19
 
 
 
 
 
 
 
 
20
  RERANKER_REPO_ID = os.getenv("RERANKER_REPO_ID", "FrankHUP/mousebrain-reranker")
21
  RERANKER_FILENAME = os.getenv("RERANKER_FILENAME", "reranker_listwise.pt")
 
 
22
  DINO_MODEL_ID = os.getenv("DINO_MODEL_ID", "facebook/dinov3-vitb16-pretrain-lvd1689m")
23
 
 
24
  FAISS_LOCAL = CACHE_DIR / "patch_index.faiss"
25
  MANIFEST_LOCAL = CACHE_DIR / "patch_manifest.parquet"
26
+ VECTORS_LOCAL = CACHE_DIR / "patch_vectors.npy"
27
+
28
+ TOPK_DEFAULT = int(os.getenv("TOPK_DEFAULT", "12"))
29
+ K_PER_ANGLE_DEFAULT = int(os.getenv("K_PER_ANGLE_DEFAULT", "100"))
30
+ IDLE_UNLOAD_SECONDS = int(os.getenv("IDLE_UNLOAD_SECONDS", "120"))
31
+
32
+ DEFAULT_ANGLES = (0.0, 90.0, 180.0, 270.0)
33
+ DEFAULT_RERANK_TOPK = 100
34
+ DEFAULT_RERANK_ALPHA = 1.0
device_utils.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+
6
+ def resolve_device() -> str:
7
+ requested = os.getenv("MOUSE_DEMO_DEVICE", "auto").strip().lower()
8
+
9
+ if requested == "cpu":
10
+ return "cpu"
11
+
12
+ if requested == "cuda":
13
+ import torch
14
+ return "cuda" if torch.cuda.is_available() else "cpu"
15
+
16
+ # auto
17
+ import torch
18
+ return "cuda" if torch.cuda.is_available() else "cpu"
env.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+
6
+ def _env_flag(name: str) -> bool:
7
+ value = os.getenv(name)
8
+ if value is None:
9
+ return False
10
+ return value.strip().lower() in {"1", "true", "yes", "on"}
11
+
12
+
13
+ IS_HF_SPACE = bool(os.getenv("SPACE_ID")) or _env_flag("HF_SPACE")
14
+
15
+
16
+ def gpu_decorator(*args, **kwargs):
17
+ if IS_HF_SPACE:
18
+ import spaces
19
+ return spaces.GPU(*args, **kwargs)
20
+
21
+ def decorator(fn):
22
+ return fn
23
+
24
+ return decorator
runtime.py CHANGED
@@ -10,7 +10,6 @@ from huggingface_hub import hf_hub_download
10
 
11
  from config import (
12
  CACHE_DIR,
13
- DEVICE,
14
  FAISS_LOCAL,
15
  FAISS_URL,
16
  IDLE_UNLOAD_SECONDS,
@@ -87,10 +86,10 @@ class RuntimeState:
87
 
88
  def status_text(self) -> str:
89
  if self.loading:
90
- return f"Loading assets on {DEVICE}..."
91
  if self.store is None:
92
- return f"Cold state ({DEVICE})"
93
- return f"Ready ({DEVICE})"
94
 
95
 
96
  RUNTIME = RuntimeState()
 
10
 
11
  from config import (
12
  CACHE_DIR,
 
13
  FAISS_LOCAL,
14
  FAISS_URL,
15
  IDLE_UNLOAD_SECONDS,
 
86
 
87
  def status_text(self) -> str:
88
  if self.loading:
89
+ return "Loading assets..."
90
  if self.store is None:
91
+ return "Cold state"
92
+ return "Ready"
93
 
94
 
95
  RUNTIME = RuntimeState()
search_service.py CHANGED
@@ -3,8 +3,9 @@ from __future__ import annotations
3
  import numpy as np
4
  import pandas as pd
5
 
6
- from config import DEVICE, PATCH_PNG_BASE, PATCH_PNG_BASE_OVERRIDE
7
  from runtime import RUNTIME
 
8
 
9
 
10
  def _angles_from_flags(a0: bool, a90: bool, a180: bool, a270: bool) -> tuple[float, ...]:
@@ -36,6 +37,8 @@ def run_search(
36
  if image is None:
37
  return [], pd.DataFrame(), "Please upload an image."
38
 
 
 
39
  RUNTIME.ensure_loaded()
40
  assert RUNTIME.store is not None
41
 
@@ -50,9 +53,9 @@ def run_search(
50
  use_reranker=bool(use_reranker),
51
  rerank_topk=int(rerank_topk),
52
  reranker_model_path=RUNTIME.reranker_model_path,
53
- reranker_device=DEVICE,
54
  reranker_batch_size=128,
55
- reranker_use_fp16=(DEVICE == "cuda"),
56
  reranker_compile=False,
57
  rerank_alpha=float(rerank_alpha),
58
  )
@@ -62,14 +65,14 @@ def run_search(
62
  df = searcher.to_dataframe(hits).copy()
63
 
64
  if df.empty:
65
- return [], df, f"No results ({DEVICE})."
66
 
67
- patch_base = PATCH_PNG_BASE_OVERRIDE or PATCH_PNG_BASE
68
- df["image_url"] = df["patch_id"].apply(lambda pid: f"{patch_base}/{int(pid):08d}.png")
69
 
70
  gallery = [
71
  (row["image_url"], f"patch_id={int(row['patch_id'])} score={row['score']:.4f}")
72
  for _, row in df.iterrows()
73
  ]
74
 
75
- return gallery, df, f"Completed on {DEVICE}"
 
 
3
  import numpy as np
4
  import pandas as pd
5
 
6
+ from config import PATCH_PNG_BASE
7
  from runtime import RUNTIME
8
+ from device_utils import resolve_device
9
 
10
 
11
  def _angles_from_flags(a0: bool, a90: bool, a180: bool, a270: bool) -> tuple[float, ...]:
 
37
  if image is None:
38
  return [], pd.DataFrame(), "Please upload an image."
39
 
40
+ device = resolve_device()
41
+
42
  RUNTIME.ensure_loaded()
43
  assert RUNTIME.store is not None
44
 
 
53
  use_reranker=bool(use_reranker),
54
  rerank_topk=int(rerank_topk),
55
  reranker_model_path=RUNTIME.reranker_model_path,
56
+ reranker_device=device,
57
  reranker_batch_size=128,
58
+ reranker_use_fp16=(device == "cuda"),
59
  reranker_compile=False,
60
  rerank_alpha=float(rerank_alpha),
61
  )
 
65
  df = searcher.to_dataframe(hits).copy()
66
 
67
  if df.empty:
68
+ return [], df, f"No results ({device})."
69
 
70
+ df["image_url"] = df["patch_id"].apply(lambda pid: f"{PATCH_PNG_BASE}/{int(pid):08d}.png")
 
71
 
72
  gallery = [
73
  (row["image_url"], f"patch_id={int(row['patch_id'])} score={row['score']:.4f}")
74
  for _, row in df.iterrows()
75
  ]
76
 
77
+ mode = "GPU (ZeroGPU)" if device == "cuda" else "CPU"
78
+ return gallery, df, f"Completed on {mode}"
spaces_compat.py CHANGED
@@ -1,11 +1,12 @@
1
  from __future__ import annotations
2
 
3
- from config import IS_HF_SPACE
4
 
5
- if IS_HF_SPACE:
6
- import spaces # type: ignore
7
 
8
- GPU = spaces.GPU
 
 
9
  else:
10
  def GPU(*args, **kwargs):
11
  def decorator(fn):
 
1
  from __future__ import annotations
2
 
3
+ import os
4
 
5
+ IS_HF_SPACE = bool(os.getenv("SPACE_ID"))
 
6
 
7
+ if IS_HF_SPACE:
8
+ import spaces as _spaces
9
+ GPU = _spaces.GPU
10
  else:
11
  def GPU(*args, **kwargs):
12
  def decorator(fn):