AdithyaSK HF Staff commited on
Commit
53508e7
·
verified ·
1 Parent(s): da5cba1

Deploy HF RL Explorer

Browse files
app/admin_app.py CHANGED
@@ -42,6 +42,26 @@ def _startup() -> None:
42
  threading.Thread(target=lambda: catalog.environments(include_hidden=True), daemon=True, name="warm").start()
43
 
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  @app.get("/api/me")
46
  def me(request: Request):
47
  u = auth.current_user(request)
 
42
  threading.Thread(target=lambda: catalog.environments(include_hidden=True), daemon=True, name="warm").start()
43
 
44
 
45
+ @app.get("/healthz", include_in_schema=False)
46
+ def healthz():
47
+ """Alive: the process answers."""
48
+ return {"ok": True}
49
+
50
+
51
+ @app.get("/readyz", include_in_schema=False)
52
+ def readyz():
53
+ """Ready: the bucket it keeps the settings in is mounted and writable (this app runs no rollouts or indexes)."""
54
+ from fastapi.responses import JSONResponse
55
+
56
+ try:
57
+ settings.DIR.mkdir(parents=True, exist_ok=True)
58
+ (settings.DIR / ".ready").write_text("ok")
59
+ store = "ok"
60
+ except OSError as e:
61
+ store = f"unwritable: {type(e).__name__}"
62
+ return JSONResponse({"ok": store == "ok", "store": store}, status_code=200 if store == "ok" else 503)
63
+
64
+
65
  @app.get("/api/me")
66
  def me(request: Request):
67
  u = auth.current_user(request)
scripts/deploy_spaces.py CHANGED
@@ -36,7 +36,7 @@ SPACES = {
36
  },
37
  "admin": {
38
  "repo": "FineEnvs/RL-Explorer-admin", "private": True, "readme": "ADMIN_SPACE_README.md", "hardware": "cpu-basic",
39
- "variables": {"RLX_APP": "app.admin_app:app", "RLX_OAUTH_SCOPES": "openid profile", "RLX_EXPLORER_URL": EXPLORER_URL},
40
  "volumes": [DATA],
41
  },
42
  }
 
36
  },
37
  "admin": {
38
  "repo": "FineEnvs/RL-Explorer-admin", "private": True, "readme": "ADMIN_SPACE_README.md", "hardware": "cpu-basic",
39
+ "variables": {"RLX_APP": "app.admin_app:app", "RLX_OAUTH_SCOPES": "openid profile read-memberships", "RLX_EXPLORER_URL": EXPLORER_URL},
40
  "volumes": [DATA],
41
  },
42
  }
scripts/schedule_indexer.py CHANGED
@@ -52,7 +52,8 @@ APP_DIR = "/home/user/app" # the Dockerfile's WORKDIR
52
 
53
  def spec(args: argparse.Namespace) -> dict:
54
  run = (f"cd {APP_DIR} && exec uv run --frozen --no-dev python -m app.indexer --store /data "
55
- f"--budget-min {args.budget_min:g} --max-builds {args.max_builds} --keep {args.keep} --use-token")
 
56
  return {
57
  "namespace": args.namespace,
58
  "name": args.name,
@@ -63,7 +64,9 @@ def spec(args: argparse.Namespace) -> dict:
63
  "flavor": args.flavor,
64
  "timeout": args.timeout,
65
  "env": {"STORAGE_DIR": "/data", "RLX_CACHE_DIR": "/tmp/rlx-cache", "HF_HOME": "/tmp/hf-home", "PYTHONUNBUFFERED": "1"},
66
- "secrets": ["HF_TOKEN"],
 
 
67
  "volumes": [{"type": "bucket", "source": args.bucket, "mount_path": "/data"}],
68
  "labels": {"app": "rl-explorer", "role": "indexer"},
69
  }
@@ -87,10 +90,16 @@ def cli(s: dict) -> str:
87
  def apply(s: dict, replace: bool) -> int:
88
  from huggingface_hub import HfApi, Volume, get_token
89
 
 
 
90
  token = get_token()
91
  if not token:
92
  print("No HF token: run `hf auth login` (a token that can create Jobs in the FineEnvs organization).", file=sys.stderr)
93
  return 2
 
 
 
 
94
  api = HfApi(token=token)
95
  existing = [j for j in api.list_scheduled_jobs(namespace=s["namespace"], labels={"name": s["name"]})]
96
  if existing and not replace:
@@ -102,7 +111,7 @@ def apply(s: dict, replace: bool) -> int:
102
  print(f"deleted scheduled Job {j.id}")
103
  job = api.create_scheduled_job(
104
  image=s["image"], command=s["command"], schedule=s["schedule"], concurrency=s["concurrency"], env=s["env"],
105
- secrets={k: token for k in s["secrets"]}, flavor=s["flavor"], timeout=s["timeout"], name=s["name"],
106
  labels=s["labels"], volumes=[Volume(**v) for v in s["volumes"]], namespace=s["namespace"])
107
  print(f"created scheduled Job {job.id} ({s['schedule']}); runs: hf jobs scheduled inspect {job.id} --namespace {s['namespace']}")
108
  return 0
@@ -112,6 +121,7 @@ def main(argv: list[str] | None = None) -> int:
112
  ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
113
  ap.add_argument("--apply", action="store_true", help="create the scheduled Job (otherwise only print it)")
114
  ap.add_argument("--replace", action="store_true", help="with --apply: delete a scheduled Job of the same name first")
 
115
  ap.add_argument("--namespace", default=NAMESPACE)
116
  ap.add_argument("--name", default=NAME)
117
  ap.add_argument("--image", default=IMAGE, help="a Docker image with this app: the explorer Space's, by default")
 
52
 
53
  def spec(args: argparse.Namespace) -> dict:
54
  run = (f"cd {APP_DIR} && exec uv run --frozen --no-dev python -m app.indexer --store /data "
55
+ f"--budget-min {args.budget_min:g} --max-builds {args.max_builds} --keep {args.keep}"
56
+ + (" --use-token" if args.with_token else ""))
57
  return {
58
  "namespace": args.namespace,
59
  "name": args.name,
 
64
  "flavor": args.flavor,
65
  "timeout": args.timeout,
66
  "env": {"STORAGE_DIR": "/data", "RLX_CACHE_DIR": "/tmp/rlx-cache", "HF_HOME": "/tmp/hf-home", "PYTHONUNBUFFERED": "1"},
67
+ # anonymous by default: public datasets need no token. --with-token sends RLX_INDEXER_TOKEN (a read-only token
68
+ # made for this) as HF_TOKEN, for the Hub's higher rate limits; never your login token, which can write
69
+ "secrets": ["HF_TOKEN"] if args.with_token else [],
70
  "volumes": [{"type": "bucket", "source": args.bucket, "mount_path": "/data"}],
71
  "labels": {"app": "rl-explorer", "role": "indexer"},
72
  }
 
90
  def apply(s: dict, replace: bool) -> int:
91
  from huggingface_hub import HfApi, Volume, get_token
92
 
93
+ import os
94
+
95
  token = get_token()
96
  if not token:
97
  print("No HF token: run `hf auth login` (a token that can create Jobs in the FineEnvs organization).", file=sys.stderr)
98
  return 2
99
+ job_token = os.environ.get("RLX_INDEXER_TOKEN")
100
+ if s["secrets"] and not job_token:
101
+ print("--with-token needs RLX_INDEXER_TOKEN: a read-only token made for the indexer (not your login token).", file=sys.stderr)
102
+ return 2
103
  api = HfApi(token=token)
104
  existing = [j for j in api.list_scheduled_jobs(namespace=s["namespace"], labels={"name": s["name"]})]
105
  if existing and not replace:
 
111
  print(f"deleted scheduled Job {j.id}")
112
  job = api.create_scheduled_job(
113
  image=s["image"], command=s["command"], schedule=s["schedule"], concurrency=s["concurrency"], env=s["env"],
114
+ secrets={k: job_token for k in s["secrets"]} or None, flavor=s["flavor"], timeout=s["timeout"], name=s["name"],
115
  labels=s["labels"], volumes=[Volume(**v) for v in s["volumes"]], namespace=s["namespace"])
116
  print(f"created scheduled Job {job.id} ({s['schedule']}); runs: hf jobs scheduled inspect {job.id} --namespace {s['namespace']}")
117
  return 0
 
121
  ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
122
  ap.add_argument("--apply", action="store_true", help="create the scheduled Job (otherwise only print it)")
123
  ap.add_argument("--replace", action="store_true", help="with --apply: delete a scheduled Job of the same name first")
124
+ ap.add_argument("--with-token", action="store_true", help="send RLX_INDEXER_TOKEN (a read-only token) as the Job's HF_TOKEN")
125
  ap.add_argument("--namespace", default=NAMESPACE)
126
  ap.add_argument("--name", default=NAME)
127
  ap.add_argument("--image", default=IMAGE, help="a Docker image with this app: the explorer Space's, by default")
tests/test_security.py CHANGED
@@ -545,3 +545,10 @@ def test_a_rollout_never_downloads_reference_solutions(monkeypatch):
545
  for f in ("tasks/t1/solution/solve.sh", "tasks/t1/steps/s1/solution/solve.sh", "tasks/t1/steps/two/solution/deep/x.py"):
546
  assert any(fnmatch(f, p) for p in seen["ignore_patterns"]), f
547
  assert not any(fnmatch("tasks/t1/steps/s1/instruction.md", p) for p in seen["ignore_patterns"])
 
 
 
 
 
 
 
 
545
  for f in ("tasks/t1/solution/solve.sh", "tasks/t1/steps/s1/solution/solve.sh", "tasks/t1/steps/two/solution/deep/x.py"):
546
  assert any(fnmatch(f, p) for p in seen["ignore_patterns"]), f
547
  assert not any(fnmatch("tasks/t1/steps/s1/instruction.md", p) for p in seen["ignore_patterns"])
548
+
549
+
550
+ def test_the_admin_space_has_health_checks_and_nothing_else_open():
551
+ assert admin.get("/healthz").json() == {"ok": True}
552
+ r = admin.get("/readyz")
553
+ assert r.status_code == 200 and r.json()["store"] == "ok"
554
+ assert admin.get("/api/admin/settings").status_code == 401