File size: 3,970 Bytes
54f0281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ecd292c
 
 
 
 
 
 
 
 
 
 
 
54f0281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""Container entrypoint (deploy/Dockerfile): fetch data if missing → ensure the SQLite schema → exec uvicorn.

1. If `$BLINDSPOT_DATA_DIR/processed/cases.jsonl` is missing, download the processed-data bundle from the PRIVATE
   Hugging Face dataset repo `$BLINDSPOT_DATA_REPO` (made by deploy/make_data_bundle.py --upload) with `$HF_TOKEN`
   into `$BLINDSPOT_DATA_DIR/processed`. The image itself never contains dataset files.
2. Create the SQLite schema at `$BLINDSPOT_DB_PATH` (default /data/blindspot.sqlite). Without a persistent volume
   it starts empty on every restart; that is expected for the demo.
3. Optional: `BLINDSPOT_SEED_DEMO=1` seeds the demo learner + playlist (offline templates; no API call).
4. exec uvicorn on 0.0.0.0:$PORT (7860 on Hugging Face), trusting X-Forwarded-* from the proxy.

Never prints secrets (HF_TOKEN, ANTHROPIC_API_KEY, access codes).
"""

from __future__ import annotations

import os
import subprocess
import sys
import time
from pathlib import Path

APP_DIR = Path(__file__).resolve().parents[1]


def log(msg: str) -> None:
    print(f"[entrypoint] {msg}", flush=True)


def data_dir() -> Path:
    p = Path(os.environ.get("BLINDSPOT_DATA_DIR", "/data"))
    return p if p.is_absolute() else (APP_DIR / p).resolve()


def has_data(processed: Path) -> bool:
    return (processed / "cases.jsonl").is_file() and (processed / "images").is_dir()


def fetch_data(processed: Path) -> None:
    repo = os.environ.get("BLINDSPOT_DATA_REPO", "").strip()
    if not repo:
        log("BLINDSPOT_DATA_REPO is not set and no data is mounted; the API will start with 0 cases.")
        return
    token = os.environ.get("HF_TOKEN") or None
    from huggingface_hub import snapshot_download

    processed.mkdir(parents=True, exist_ok=True)
    t0 = time.time()
    log(f"downloading dataset repo {repo} into {processed} (private; token {'set' if token else 'NOT set'})")
    snapshot_download(
        repo_id=repo,
        repo_type="dataset",
        local_dir=str(processed),
        token=token,
        max_workers=8,
        allow_patterns=[
            "cases.jsonl",
            "cases_msd.jsonl",
            "splits.json",
            "*.json",
            "images/*",
            "masks/*",
            "zones/*",
            "volumes/*",
            "zones3d/*",
            "previews/*",
        ],
    )
    n = sum(1 for _ in (processed / "cases.jsonl").open()) if (processed / "cases.jsonl").exists() else 0
    log(f"data ready: {n} cases in {time.time() - t0:.0f} s")


def ensure_db() -> None:
    subprocess.run([sys.executable, "-m", "backend.app.db"], cwd=APP_DIR, check=True)
    if os.environ.get("BLINDSPOT_SEED_DEMO", "").strip() in ("1", "true", "yes"):
        env = dict(os.environ, BLINDSPOT_OFFLINE="1")  # seeding never makes live calls
        r = subprocess.run([sys.executable, "-m", "backend.app.demo_seed"], cwd=APP_DIR, env=env)
        log(f"demo seed exit code {r.returncode}")


def main() -> None:
    processed = data_dir() / "processed"
    if not has_data(processed):
        try:
            fetch_data(processed)
        except Exception as e:  # noqa: BLE001 — start anyway; /api/health reports cases=0
            log(f"data download failed: {type(e).__name__}: {e}")
    else:
        log(f"data present at {processed}")
    ensure_db()
    port = os.environ.get("PORT", "7860")
    log(f"starting uvicorn on :{port} base={os.environ.get('BLINDSPOT_BASE_PATH', '') or '/'}")
    os.chdir(APP_DIR)
    os.execvp(
        sys.executable,
        [
            sys.executable,
            "-m",
            "uvicorn",
            "backend.app.main:app",
            "--host",
            os.environ.get("HOST", "0.0.0.0"),
            "--port",
            port,
            "--proxy-headers",
            "--forwarded-allow-ips",
            "*",
            "--workers",
            os.environ.get("WEB_CONCURRENCY", "1"),
        ],
    )


if __name__ == "__main__":
    main()