whojavumusic commited on
Commit
14ce963
·
1 Parent(s): 1ba7218

lets try running on different gpu machines

Browse files
EVALUATION_SCALING.md CHANGED
@@ -48,19 +48,20 @@ When enabled, the queue worker **does not** call `run_evaluation` inside the Spa
48
  | `FFASR_REMOTE_JOBS` | `1` / `true` to enable Hub Job dispatch. |
49
  | `FFASR_REMOTE_EVAL_REPO_URL` | Git URL cloned inside the job (required unless `FFASR_REMOTE_JOB_COMMAND` is set). |
50
  | `FFASR_REMOTE_EVAL_GIT_BRANCH` | Branch for clone (default `main`). |
51
- | `FFASR_REMOTE_JOB_IMAGE` | Default Docker image if no `remote_job_images.json` match (default `python:3.10-bookworm`). |
52
- | `FFASR_REMOTE_JOB_FLAVOR` | Hub Job hardware flavor (default `cpu-basic`). |
53
  | `FFASR_REMOTE_JOB_NAMESPACE` | Optional Hub namespace for the job. |
54
  | `FFASR_REMOTE_JOB_TIMEOUT` | Job timeout (Hub format). |
55
  | `FFASR_REMOTE_JOB_MAX_WAIT_S` | Max seconds to poll for terminal state (default `14400`). |
56
  | `FFASR_REMOTE_JOB_POLL_S` | Poll interval seconds (default `10`). |
57
  | `FFASR_REMOTE_JOB_COMMAND` | Optional JSON array of strings: full command override for `run_job`. |
58
- | `FFASR_REMOTE_WORKER_DEVICE` | Passed into the job as `FFASR_DEVICE` (default `cpu`). |
59
  | `FFASR_REMOTE_WORKER_DISABLE_ZEROGPU` | Passed as `FFASR_DISABLE_ZEROGPU` (default `1`). |
 
60
 
61
  **Per-model Docker images**: copy [remote_job_images.example.json](remote_job_images.example.json) to `remote_job_images.json` in the repo root. Longest matching prefix key wins; use `"default"` as fallback.
62
 
63
- **Job entrypoint**: [scripts/run_hf_remote_job.py](scripts/run_hf_remote_job.py) runs `run_evaluation`, builds the artifact, and uploads it with `HF_TOKEN` (passed as a Hub Job secret).
64
 
65
  ---
66
 
@@ -90,6 +91,6 @@ Do not execute **untrusted** `pip install` lines from users on shared infrastruc
90
 
91
  ## Suggested evolution path
92
 
93
- 1. **Now**: FIFO queue + CSV lock + ZeroGPU **or** fixed CPU (`FFASR_DEVICE` / `FFASR_DISABLE_ZEROGPU`) **or** Hub Jobs (`FFASR_REMOTE_JOBS=1`).
94
  2. **Next**: richer job metadata in CSV (remote ids, artifact paths); optional webhooks when remote workers finish.
95
  3. **Later**: split UI and workers entirely (multiple runners, priority tiers).
 
48
  | `FFASR_REMOTE_JOBS` | `1` / `true` to enable Hub Job dispatch. |
49
  | `FFASR_REMOTE_EVAL_REPO_URL` | Git URL cloned inside the job (required unless `FFASR_REMOTE_JOB_COMMAND` is set). |
50
  | `FFASR_REMOTE_EVAL_GIT_BRANCH` | Branch for clone (default `main`). |
51
+ | `FFASR_REMOTE_JOB_IMAGE` | Default Docker image if no `remote_job_images.json` match (default CUDA PyTorch: `pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel`). |
52
+ | `FFASR_REMOTE_JOB_FLAVOR` | Hub Job hardware flavor (default `l4x1` — NVIDIA L4, 1× GPU per [Hub Jobs](https://huggingface.co/docs/huggingface_hub/guides/jobs)). |
53
  | `FFASR_REMOTE_JOB_NAMESPACE` | Optional Hub namespace for the job. |
54
  | `FFASR_REMOTE_JOB_TIMEOUT` | Job timeout (Hub format). |
55
  | `FFASR_REMOTE_JOB_MAX_WAIT_S` | Max seconds to poll for terminal state (default `14400`). |
56
  | `FFASR_REMOTE_JOB_POLL_S` | Poll interval seconds (default `10`). |
57
  | `FFASR_REMOTE_JOB_COMMAND` | Optional JSON array of strings: full command override for `run_job`. |
58
+ | `FFASR_REMOTE_WORKER_DEVICE` | Passed into the job as `FFASR_DEVICE` (default `auto`: CUDA in GPU jobs, else CPU). |
59
  | `FFASR_REMOTE_WORKER_DISABLE_ZEROGPU` | Passed as `FFASR_DISABLE_ZEROGPU` (default `1`). |
60
+ | `FFASR_REMOTE_SKIP_OPTIONAL_PIP` | If `1` / `true`, the remote worker skips allowlisted `pip install` for optional stacks (NeMo, Qwen ASR). |
61
 
62
  **Per-model Docker images**: copy [remote_job_images.example.json](remote_job_images.example.json) to `remote_job_images.json` in the repo root. Longest matching prefix key wins; use `"default"` as fallback.
63
 
64
+ **Job entrypoint**: [scripts/run_hf_remote_job.py](scripts/run_hf_remote_job.py) optionally installs missing **allowlisted** dependencies (e.g. `nemo_toolkit[asr]` for Parakeet-style ids, `qwen-asr` for Qwen ASR ids) when imports fail, then runs `run_evaluation`, builds the artifact, and uploads it with `HF_TOKEN` (passed as a Hub Job secret).
65
 
66
  ---
67
 
 
91
 
92
  ## Suggested evolution path
93
 
94
+ 1. **Now**: FIFO queue + CSV lock + ZeroGPU **or** fixed CPU/CUDA in-process (`FFASR_DEVICE` / `FFASR_DISABLE_ZEROGPU`) **or** Hub Jobs on GPU (`FFASR_REMOTE_JOBS=1`, default flavor `l4x1` + CUDA image).
95
  2. **Next**: richer job metadata in CSV (remote ids, artifact paths); optional webhooks when remote workers finish.
96
  3. **Later**: split UI and workers entirely (multiple runners, priority tiers).
remote_job_images.example.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "default": "python:3.10-bookworm",
3
  "coherelabs/": "your-registry/cohere-asr-eval:pin",
4
  "ibm-granite/": "your-registry/granite-speech-eval:pin"
5
  }
 
1
  {
2
+ "default": "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel",
3
  "coherelabs/": "your-registry/cohere-asr-eval:pin",
4
  "ibm-granite/": "your-registry/granite-speech-eval:pin"
5
  }
remote_jobs.py CHANGED
@@ -7,8 +7,8 @@ Environment (Space / operator):
7
 
8
  - ``FFASR_REMOTE_JOBS`` — ``1`` / ``true`` to route queue worker through Hub Jobs.
9
  - ``HF_TOKEN`` — token with permission to run jobs and read/write the results bucket.
10
- - ``FFASR_REMOTE_JOB_IMAGE`` — Docker image for the job (default ``python:3.10-bookworm``).
11
- - ``FFASR_REMOTE_JOB_FLAVOR`` — hardware flavor (default ``cpu-basic``). See ``HfApi.list_jobs_hardware``.
12
  - ``FFASR_REMOTE_JOB_NAMESPACE`` — optional Hub namespace/user for the job (default: token owner).
13
  - ``FFASR_REMOTE_JOB_TIMEOUT`` — job timeout string/int (optional).
14
  - ``FFASR_REMOTE_EVAL_REPO_URL`` — git clone URL for this repo inside the job (required unless
@@ -17,7 +17,7 @@ Environment (Space / operator):
17
 
18
  Per-model Docker image: optional JSON file ``remote_job_images.json`` in the repo root::
19
 
20
- {"cohere/": "org/cohere-asr-image:tag", "default": "python:3.10-bookworm"}
21
 
22
  Longest key match wins; ``default`` fallback.
23
  """
@@ -32,6 +32,11 @@ from typing import Any
32
  from huggingface_hub import HfApi
33
  from huggingface_hub._jobs_api import JobInfo, JobStage
34
 
 
 
 
 
 
35
  _IMAGE_MAP_CACHE: dict[str, str] | None = None
36
 
37
 
@@ -85,7 +90,7 @@ def resolve_job_image(model_id: str) -> str:
85
  if default_map:
86
  return default_map
87
  env_img = os.environ.get("FFASR_REMOTE_JOB_IMAGE", "").strip()
88
- return env_img or "python:3.10-bookworm"
89
 
90
 
91
  def _default_bash_command() -> list[str]:
@@ -99,7 +104,7 @@ def _default_bash_command() -> list[str]:
99
  "cd \"$WORKDIR\" && "
100
  "python -m pip install --no-cache-dir -U pip && "
101
  "python -m pip install --no-cache-dir -r requirements.txt && "
102
- "export FFASR_DEVICE=${FFASR_DEVICE:-cpu} FFASR_DISABLE_ZEROGPU=${FFASR_DISABLE_ZEROGPU:-1} && "
103
  "python scripts/run_hf_remote_job.py"
104
  )
105
  return ["bash", "-lc", script]
@@ -130,7 +135,10 @@ def submit_eval_job(
130
  api = HfApi(token=token)
131
  image = resolve_job_image(model_id)
132
  namespace = os.environ.get("FFASR_REMOTE_JOB_NAMESPACE", "").strip() or None
133
- flavor = os.environ.get("FFASR_REMOTE_JOB_FLAVOR", "cpu-basic").strip() or "cpu-basic"
 
 
 
134
  timeout_raw = os.environ.get("FFASR_REMOTE_JOB_TIMEOUT", "").strip()
135
  timeout: int | float | str | None
136
  if not timeout_raw:
@@ -164,7 +172,10 @@ def submit_eval_job(
164
  "FFASR_SPACE_JOB_ID": space_job_id,
165
  "FFASR_REMOTE_ARTIFACT_PATH": artifact_path,
166
  "FFASR_BUCKET_ID": bucket,
167
- "FFASR_DEVICE": os.environ.get("FFASR_REMOTE_WORKER_DEVICE", "cpu"),
 
 
 
168
  "FFASR_DISABLE_ZEROGPU": os.environ.get("FFASR_REMOTE_WORKER_DISABLE_ZEROGPU", "1"),
169
  "FFASR_REMOTE_EVAL_REPO_URL": eval_repo,
170
  "FFASR_REMOTE_EVAL_GIT_BRANCH": eval_branch,
 
7
 
8
  - ``FFASR_REMOTE_JOBS`` — ``1`` / ``true`` to route queue worker through Hub Jobs.
9
  - ``HF_TOKEN`` — token with permission to run jobs and read/write the results bucket.
10
+ - ``FFASR_REMOTE_JOB_IMAGE`` — Docker image for the job (default CUDA PyTorch image for GPU eval).
11
+ - ``FFASR_REMOTE_JOB_FLAVOR`` — hardware flavor (default ``l4x1``). See ``HfApi.list_jobs_hardware``.
12
  - ``FFASR_REMOTE_JOB_NAMESPACE`` — optional Hub namespace/user for the job (default: token owner).
13
  - ``FFASR_REMOTE_JOB_TIMEOUT`` — job timeout string/int (optional).
14
  - ``FFASR_REMOTE_EVAL_REPO_URL`` — git clone URL for this repo inside the job (required unless
 
17
 
18
  Per-model Docker image: optional JSON file ``remote_job_images.json`` in the repo root::
19
 
20
+ {"cohere/": "org/cohere-asr-image:tag", "default": "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"}
21
 
22
  Longest key match wins; ``default`` fallback.
23
  """
 
32
  from huggingface_hub import HfApi
33
  from huggingface_hub._jobs_api import JobInfo, JobStage
34
 
35
+ # Defaults for L4 GPU Hub Jobs (override with FFASR_REMOTE_JOB_* / remote_job_images.json).
36
+ _DEFAULT_REMOTE_JOB_IMAGE = "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"
37
+ _DEFAULT_REMOTE_JOB_FLAVOR = "l4x1"
38
+ _DEFAULT_REMOTE_WORKER_DEVICE = "auto"
39
+
40
  _IMAGE_MAP_CACHE: dict[str, str] | None = None
41
 
42
 
 
90
  if default_map:
91
  return default_map
92
  env_img = os.environ.get("FFASR_REMOTE_JOB_IMAGE", "").strip()
93
+ return env_img or _DEFAULT_REMOTE_JOB_IMAGE
94
 
95
 
96
  def _default_bash_command() -> list[str]:
 
104
  "cd \"$WORKDIR\" && "
105
  "python -m pip install --no-cache-dir -U pip && "
106
  "python -m pip install --no-cache-dir -r requirements.txt && "
107
+ "export FFASR_DEVICE=${FFASR_DEVICE:-auto} FFASR_DISABLE_ZEROGPU=${FFASR_DISABLE_ZEROGPU:-1} && "
108
  "python scripts/run_hf_remote_job.py"
109
  )
110
  return ["bash", "-lc", script]
 
135
  api = HfApi(token=token)
136
  image = resolve_job_image(model_id)
137
  namespace = os.environ.get("FFASR_REMOTE_JOB_NAMESPACE", "").strip() or None
138
+ flavor = (
139
+ os.environ.get("FFASR_REMOTE_JOB_FLAVOR", _DEFAULT_REMOTE_JOB_FLAVOR).strip()
140
+ or _DEFAULT_REMOTE_JOB_FLAVOR
141
+ )
142
  timeout_raw = os.environ.get("FFASR_REMOTE_JOB_TIMEOUT", "").strip()
143
  timeout: int | float | str | None
144
  if not timeout_raw:
 
172
  "FFASR_SPACE_JOB_ID": space_job_id,
173
  "FFASR_REMOTE_ARTIFACT_PATH": artifact_path,
174
  "FFASR_BUCKET_ID": bucket,
175
+ "FFASR_DEVICE": os.environ.get(
176
+ "FFASR_REMOTE_WORKER_DEVICE", _DEFAULT_REMOTE_WORKER_DEVICE
177
+ ).strip()
178
+ or _DEFAULT_REMOTE_WORKER_DEVICE,
179
  "FFASR_DISABLE_ZEROGPU": os.environ.get("FFASR_REMOTE_WORKER_DISABLE_ZEROGPU", "1"),
180
  "FFASR_REMOTE_EVAL_REPO_URL": eval_repo,
181
  "FFASR_REMOTE_EVAL_GIT_BRANCH": eval_branch,
scripts/run_hf_remote_job.py CHANGED
@@ -9,24 +9,107 @@ Environment (set by the Space when dispatching ``remote_jobs.submit_eval_job``):
9
  - ``FFASR_BUCKET_ID``, ``HF_TOKEN`` — bucket write access.
10
  - ``FFASR_SPACE_JOB_ID`` — optional correlating id from the Space queue.
11
 
12
- Also respects ``FFASR_DEVICE`` / ``FFASR_DISABLE_ZEROGPU`` (defaults should be CPU + no ZeroGPU).
 
 
 
 
13
 
14
  Repo root must be the current working directory (after git clone in the job).
15
  """
16
 
17
  from __future__ import annotations
18
 
 
19
  import io
20
  import json
21
  import os
 
22
  import sys
23
- import traceback
24
 
25
  ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26
  if ROOT not in sys.path:
27
  sys.path.insert(0, ROOT)
28
 
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  def main() -> int:
31
  from evaluation.remote_artifact import ARTIFACT_SCHEMA_VERSION, build_artifact
32
  from evaluation.runtime import apply_cpu_thread_settings_once
@@ -58,6 +141,7 @@ def main() -> int:
58
 
59
  try:
60
  _log(f"run_hf_remote_job: model_id={model_id!r} family_id={family_id!r}")
 
61
  result = run_evaluation(model_id, family_id=family_id, progress_cb=None)
62
  artifact = build_artifact(
63
  model_id=model_id,
 
9
  - ``FFASR_BUCKET_ID``, ``HF_TOKEN`` — bucket write access.
10
  - ``FFASR_SPACE_JOB_ID`` — optional correlating id from the Space queue.
11
 
12
+ Also respects ``FFASR_DEVICE`` / ``FFASR_DISABLE_ZEROGPU`` (GPU Hub Jobs: ``auto`` uses CUDA when available).
13
+
14
+ Optional preflight (before ``run_evaluation``):
15
+
16
+ - ``FFASR_REMOTE_SKIP_OPTIONAL_PIP`` — if ``1``/``true``, skip targeted ``pip install`` for NeMo / Qwen ASR.
17
 
18
  Repo root must be the current working directory (after git clone in the job).
19
  """
20
 
21
  from __future__ import annotations
22
 
23
+ import importlib
24
  import io
25
  import json
26
  import os
27
+ import subprocess
28
  import sys
29
+ from collections.abc import Callable
30
 
31
  ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
32
  if ROOT not in sys.path:
33
  sys.path.insert(0, ROOT)
34
 
35
 
36
+ def _skip_optional_pip() -> bool:
37
+ return os.environ.get("FFASR_REMOTE_SKIP_OPTIONAL_PIP", "").strip().lower() in (
38
+ "1",
39
+ "true",
40
+ "yes",
41
+ "on",
42
+ )
43
+
44
+
45
+ def _needs_nemo_stack(model_id: str) -> bool:
46
+ m = model_id.lower()
47
+ return "nvidia/parakeet" in m or "parakeet-tdt" in m
48
+
49
+
50
+ def _needs_qwen_asr_stack(model_id: str) -> bool:
51
+ m = model_id.lower()
52
+ return m.startswith("qwen/") or "qwen3-asr" in m or "qwen3_asr" in m
53
+
54
+
55
+ def _pip_install(packages: list[str], log: Callable[[str], None]) -> None:
56
+ cmd = [sys.executable, "-m", "pip", "install", "--no-cache-dir", *packages]
57
+ log(f"run_hf_remote_job: running {' '.join(cmd)}")
58
+ proc = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
59
+ if proc.returncode != 0:
60
+ out = (proc.stdout or "") + (proc.stderr or "")
61
+ raise RuntimeError(
62
+ f"pip install failed (exit {proc.returncode}): {out[-4000:]}"
63
+ )
64
+ if proc.stdout:
65
+ log(proc.stdout.strip()[-2000:])
66
+ if proc.stderr and proc.stderr.strip():
67
+ log(proc.stderr.strip()[-2000:])
68
+
69
+
70
+ def _try_import(module: str) -> bool:
71
+ try:
72
+ importlib.import_module(module)
73
+ return True
74
+ except Exception:
75
+ return False
76
+
77
+
78
+ def maybe_install_optional_eval_deps(
79
+ model_id: str, family_id: str, log: Callable[[str], None]
80
+ ) -> None:
81
+ """
82
+ Install known optional stacks only when the model/family likely needs them
83
+ and imports fail. Allowlisted packages only.
84
+ """
85
+ if _skip_optional_pip():
86
+ log("run_hf_remote_job: FFASR_REMOTE_SKIP_OPTIONAL_PIP set — skipping optional pip installs")
87
+ return
88
+
89
+ fid = (family_id or "auto").strip().lower()
90
+
91
+ want_nemo = _needs_nemo_stack(model_id) or fid == "nemo_asr"
92
+ want_qwen = _needs_qwen_asr_stack(model_id) or fid in ("qwen_asr", "qwen3_asr")
93
+
94
+ if want_nemo:
95
+ if _try_import("nemo.collections.asr"):
96
+ log("run_hf_remote_job: NeMo ASR import OK")
97
+ else:
98
+ log("run_hf_remote_job: NeMo not importable — installing nemo_toolkit[asr]")
99
+ _pip_install(["nemo_toolkit[asr]"], log)
100
+ if not _try_import("nemo.collections.asr"):
101
+ raise RuntimeError("NeMo install finished but nemo.collections.asr still not importable")
102
+
103
+ if want_qwen:
104
+ if _try_import("qwen_asr"):
105
+ log("run_hf_remote_job: qwen_asr import OK")
106
+ else:
107
+ log("run_hf_remote_job: qwen_asr not importable — installing qwen-asr")
108
+ _pip_install(["qwen-asr"], log)
109
+ if not _try_import("qwen_asr"):
110
+ raise RuntimeError("qwen-asr install finished but qwen_asr still not importable")
111
+
112
+
113
  def main() -> int:
114
  from evaluation.remote_artifact import ARTIFACT_SCHEMA_VERSION, build_artifact
115
  from evaluation.runtime import apply_cpu_thread_settings_once
 
141
 
142
  try:
143
  _log(f"run_hf_remote_job: model_id={model_id!r} family_id={family_id!r}")
144
+ maybe_install_optional_eval_deps(model_id, family_id, _log)
145
  result = run_evaluation(model_id, family_id=family_id, progress_cb=None)
146
  artifact = build_artifact(
147
  model_id=model_id,