Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """HF Hub bucket (or S3) access: download files and upload results. | |
| Storage configuration is read from environment variables so the Space can be moved | |
| between users / organisations without editing the code: | |
| FFASR_BUCKET_ID HF dataset repo acting as the read/write bucket. | |
| Default: ``whojavumusic/FFASR_Leaderboard-storage``. | |
| FFASR_RESULTS_PATH Path inside the bucket for the leaderboard CSV. | |
| Default: ``results/leaderboard.csv``. | |
| FFASR_JOBS_PATH Path inside the bucket for the job-state CSV. | |
| Default: ``results/jobs_state.csv``. | |
| HF_TOKEN Hub token with **write** access to ``FFASR_BUCKET_ID``. | |
| ``ds_token`` is also accepted as a legacy fallback. | |
| token_for_ffasr_jobs (or ``FFASR_JOBS_TOKEN``) Hub token used **only** to submit | |
| and poll Hugging Face **Jobs** (billing account with Job credits). | |
| ``HF_TOKEN`` is still passed into the job for bucket artifact upload. | |
| For a private Space + private bucket, set ``HF_TOKEN`` as a Space repository secret | |
| belonging to (or granted access to) the organisation that owns the bucket. | |
| Set ``token_for_ffasr_jobs`` to the account/org that pays for Hub Jobs (not the Space | |
| runtime default token). | |
| """ | |
| import io | |
| import os | |
| import shutil | |
| import subprocess | |
| import tempfile | |
| from contextlib import nullcontext | |
| from pathlib import Path | |
| # --- Active: HuggingFace Bucket storage --- | |
| STORAGE_BACKEND = "hf_bucket" | |
| HF_BUCKET_ID = os.environ.get("FFASR_BUCKET_ID", "treble-technologies/FFASR_Leaderboard-storage") | |
| DATASET_PREFIX = "" | |
| RESULTS_PATH = os.environ.get("FFASR_RESULTS_PATH", "results/leaderboard.csv") | |
| JOBS_STATE_PATH = os.environ.get("FFASR_JOBS_PATH", "results/jobs_state.csv") | |
| # Append-only leaderboard history (one row per changed model per version). Used to | |
| # reconstruct and display past leaderboard states from the version dropdown. | |
| HISTORY_PATH = os.environ.get("FFASR_HISTORY_PATH", "results/leaderboard_history.csv") | |
| HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("ds_token") | |
| # Env names tried in order (HF Space secrets are often lowercase like ``token_for_ffasr_jobs``). | |
| _JOBS_TOKEN_ENV_KEYS = ( | |
| "token_for_ffasr_jobs", | |
| "TOKEN_FOR_FFASR_JOBS", | |
| "FFASR_JOBS_TOKEN", | |
| "HF_JOBS_TOKEN", | |
| ) | |
| def token_for_ffasr_jobs() -> str | None: | |
| """Hub token for submitting/polling FFASR remote Jobs (billing account).""" | |
| for key in _JOBS_TOKEN_ENV_KEYS: | |
| tok = os.environ.get(key, "").strip() | |
| if tok: | |
| return tok | |
| return None | |
| def require_token_for_ffasr_jobs() -> str: | |
| """Jobs billing token; never falls back to ``HF_TOKEN`` (avoids wrong namespace / 402).""" | |
| tok = token_for_ffasr_jobs() | |
| if tok: | |
| return tok | |
| keys = ", ".join(f"`{k}`" for k in _JOBS_TOKEN_ENV_KEYS) | |
| raise RuntimeError( | |
| f"Remote Hub Jobs require a dedicated jobs token: set one of {keys} as a Space secret " | |
| "(Hub token with Job credits and permission to run jobs). " | |
| "HF_TOKEN is only used for dataset bucket read/write on the Space and inside the job." | |
| ) | |
| try: | |
| from huggingface_hub import ( | |
| HfApi, | |
| list_bucket_tree, | |
| download_bucket_files, | |
| batch_bucket_files, | |
| ) | |
| hf_api = HfApi(token=HF_TOKEN) | |
| except Exception: | |
| hf_api = None | |
| download_bucket_files = None | |
| batch_bucket_files = None | |
| list_bucket_tree = None | |
| # --- Optional: AWS S3 (uncomment in init / wire here if needed) --- | |
| # STORAGE_BACKEND = "s3" | |
| # import boto3 | |
| # s3 = boto3.client("s3") | |
| # DATASET_BUCKET = os.environ.get("DATASET_S3_BUCKET", "") | |
| # RESULTS_BUCKET = os.environ.get("RESULTS_S3_BUCKET", DATASET_BUCKET) | |
| # RESULTS_KEY = os.environ.get("RESULTS_S3_KEY", "results/leaderboard.csv") | |
| def _ensure_xet_progress_reporter_compatible() -> None: | |
| """ | |
| Some container images mix an older ``XetProgressReporter`` with a newer ``hf_api`` that | |
| calls ``notify_upload_complete`` between chunked uploads. | |
| """ | |
| try: | |
| from huggingface_hub.utils._xet_progress_reporting import XetProgressReporter | |
| except ImportError: | |
| return | |
| if hasattr(XetProgressReporter, "notify_upload_complete"): | |
| return | |
| def notify_upload_complete(self) -> None: | |
| try: | |
| self._total_bytes_offset = getattr(self.data_processing_bar, "total", None) or 0 | |
| self._total_transfer_bytes_offset = getattr(self.upload_bar, "total", None) or 0 | |
| except Exception: | |
| pass | |
| XetProgressReporter.notify_upload_complete = notify_upload_complete # type: ignore[method-assign] | |
| def upload_to_bucket( | |
| bucket_id: str, | |
| *, | |
| add: list[tuple[str | Path | bytes, str]] | None = None, | |
| copy: list[tuple[str, str, str, str]] | None = None, | |
| delete: list[str] | None = None, | |
| token: str | bool | None = None, | |
| ) -> None: | |
| """ | |
| Upload/copy/delete bucket files with progress bars disabled and Xet reporter compatibility. | |
| """ | |
| if batch_bucket_files is None: | |
| # In-process hub lacks the bucket API (e.g. qwen-asr stack pins hub<1.0). | |
| # Fall back to an isolated uv env that has it. | |
| if not _bucket_uv_available(): | |
| raise RuntimeError("huggingface_hub bucket support is not available") | |
| _upload_to_bucket_via_uv( | |
| bucket_id, add=add, copy=copy, delete=delete, token=token | |
| ) | |
| return | |
| _ensure_xet_progress_reporter_compatible() | |
| try: | |
| from huggingface_hub.utils import disable_progress_bars | |
| except ImportError: | |
| ctx = nullcontext() | |
| else: | |
| ctx = disable_progress_bars() | |
| with ctx: | |
| batch_bucket_files( | |
| bucket_id, | |
| add=add, | |
| copy=copy, | |
| delete=delete, | |
| token=token, | |
| ) | |
| # huggingface_hub version that ships the bucket API, used by the isolated-subprocess | |
| # fallback when the in-process hub is pinned <1.0 (e.g. the qwen-asr stack, whose | |
| # transformers==4.57.x pin forces huggingface-hub<1.0 and so lacks bucket support). | |
| _BUCKET_FALLBACK_HUB_SPEC = os.environ.get( | |
| "FFASR_BUCKET_FALLBACK_HUB_SPEC", "huggingface-hub>=1.14.0" | |
| ) | |
| _BUCKET_DOWNLOAD_SNIPPET = """\ | |
| import os, sys | |
| from huggingface_hub import download_bucket_files | |
| bucket_id, remote_path, local_path = sys.argv[1], sys.argv[2], sys.argv[3] | |
| download_bucket_files( | |
| bucket_id, | |
| files=[(remote_path, local_path)], | |
| token=os.environ.get("FFASR_BUCKET_DL_TOKEN") or None, | |
| ) | |
| """ | |
| _BUCKET_UPLOAD_SNIPPET = """\ | |
| import json, os, sys | |
| from huggingface_hub import batch_bucket_files | |
| try: | |
| from huggingface_hub.utils import disable_progress_bars | |
| disable_progress_bars() | |
| except Exception: | |
| pass | |
| manifest_path, bucket_id = sys.argv[1], sys.argv[2] | |
| with open(manifest_path, encoding="utf-8") as f: | |
| manifest = json.load(f) | |
| add = [(src, dst) for src, dst in manifest.get("add", [])] or None | |
| copy = [tuple(c) for c in manifest.get("copy", [])] or None | |
| delete = list(manifest.get("delete", [])) or None | |
| batch_bucket_files( | |
| bucket_id, | |
| add=add, | |
| copy=copy, | |
| delete=delete, | |
| token=os.environ.get("FFASR_BUCKET_DL_TOKEN") or None, | |
| ) | |
| """ | |
| def _bucket_uv_available() -> bool: | |
| return shutil.which("uv") is not None | |
| def _run_bucket_uv_snippet( | |
| snippet: str, args: list[str], *, token: str | None = None | |
| ) -> subprocess.CompletedProcess: | |
| """Run ``snippet`` in an isolated ``uv`` env that has the hub bucket API. | |
| Used when this process' ``huggingface_hub`` is pinned <1.0 (no bucket support). | |
| ``uv`` resolves an ephemeral, cached environment with a recent ``huggingface_hub`` | |
| (plus ``hf_xet`` for Xet-backed buckets), independent of the job's pinned stack. | |
| The Hub token is passed via ``FFASR_BUCKET_DL_TOKEN`` (never on argv). | |
| """ | |
| uv = shutil.which("uv") or "uv" | |
| cmd = [ | |
| uv, | |
| "run", | |
| "--no-project", | |
| "--python", | |
| "3.12", | |
| "--with", | |
| _BUCKET_FALLBACK_HUB_SPEC, | |
| "--with", | |
| "hf_xet", | |
| "python", | |
| "-c", | |
| snippet, | |
| *args, | |
| ] | |
| env = dict(os.environ) | |
| tok = token or HF_TOKEN | |
| if tok: | |
| env["FFASR_BUCKET_DL_TOKEN"] = tok | |
| env.setdefault("HF_HUB_DISABLE_PROGRESS_BARS", "1") | |
| try: | |
| return subprocess.run( | |
| cmd, | |
| env=env, | |
| check=False, | |
| capture_output=True, | |
| text=True, | |
| ) | |
| except FileNotFoundError as exc: | |
| raise RuntimeError( | |
| "huggingface_hub bucket support is not available in this environment and the " | |
| "`uv` fallback could not be launched (uv not found on PATH)." | |
| ) from exc | |
| def _download_bucket_file_via_uv(path: str, local_path: str) -> None: | |
| proc = _run_bucket_uv_snippet( | |
| _BUCKET_DOWNLOAD_SNIPPET, [HF_BUCKET_ID, path, local_path] | |
| ) | |
| if proc.returncode != 0 or not os.path.exists(local_path): | |
| tail = (proc.stderr or proc.stdout or "").strip()[-2000:] | |
| raise RuntimeError( | |
| "Bucket download via isolated uv environment failed " | |
| f"(exit {proc.returncode}) for '{path}'.\n{tail}" | |
| ) | |
| def _upload_to_bucket_via_uv( | |
| bucket_id: str, | |
| *, | |
| add: list[tuple[str | Path | bytes, str]] | None, | |
| copy: list[tuple[str, str, str, str]] | None, | |
| delete: list[str] | None, | |
| token: str | bool | None = None, | |
| ) -> None: | |
| """Upload/copy/delete bucket files via the isolated ``uv`` env (hub <1.0 fallback). | |
| ``bytes`` sources in ``add`` are materialized to temp files so the subprocess can | |
| pass plain file paths to ``batch_bucket_files``. | |
| """ | |
| tmpdir = tempfile.mkdtemp(prefix="ffasr_bucket_up_") | |
| try: | |
| add_items: list[tuple[str, str]] = [] | |
| for i, (src, dst) in enumerate(add or []): | |
| if isinstance(src, (bytes, bytearray)): | |
| src_path = os.path.join(tmpdir, f"add_{i}_{os.path.basename(dst) or 'blob'}") | |
| with open(src_path, "wb") as fh: | |
| fh.write(src) | |
| else: | |
| src_path = os.fspath(src) | |
| add_items.append((src_path, dst)) | |
| manifest = { | |
| "add": add_items, | |
| "copy": [list(c) for c in (copy or [])], | |
| "delete": list(delete or []), | |
| } | |
| import json as _json | |
| manifest_path = os.path.join(tmpdir, "manifest.json") | |
| with open(manifest_path, "w", encoding="utf-8") as fh: | |
| _json.dump(manifest, fh) | |
| snippet_token = token if isinstance(token, str) else None | |
| proc = _run_bucket_uv_snippet( | |
| _BUCKET_UPLOAD_SNIPPET, [manifest_path, bucket_id], token=snippet_token | |
| ) | |
| if proc.returncode != 0: | |
| tail = (proc.stderr or proc.stdout or "").strip()[-2000:] | |
| raise RuntimeError( | |
| "Bucket upload via isolated uv environment failed " | |
| f"(exit {proc.returncode}).\n{tail}" | |
| ) | |
| finally: | |
| shutil.rmtree(tmpdir, ignore_errors=True) | |
| def download_bucket_file(path: str) -> str: | |
| """Download a single file from HF Bucket and return the local path. | |
| Uses the in-process bucket API when available; otherwise falls back to an isolated | |
| ``uv`` environment that has a hub version with bucket support (the qwen-asr stack | |
| pins huggingface-hub<1.0, which lacks the bucket API). | |
| """ | |
| local_dir = tempfile.mkdtemp() | |
| local_path = os.path.join(local_dir, os.path.basename(path)) | |
| if download_bucket_files is not None: | |
| download_bucket_files( | |
| HF_BUCKET_ID, | |
| files=[(path, local_path)], | |
| token=HF_TOKEN, | |
| ) | |
| else: | |
| _download_bucket_file_via_uv(path, local_path) | |
| return local_path | |