fix(setup): send the HF token when installing a gated model
Installing a gated model failed the fast download path with "401 Unauthorized" even with a valid token and the licence accepted, then fell back to snapshot_download and logged a 401 that reads like the token or the licence grant is at fault when it is neither (#2163). `token_resolver.resolve()` returns a ResolvedToken record, not the bearer string. Two call sites handed that record straight to consumers typed `token: str | None`, and both fail silently rather than loudly: - `_segmented_snapshot` passes it to HfApi, get_hf_file_metadata and our own segmented_download. huggingface_hub's build_hf_headers ignores a non-str token and falls back to its own ambient discovery, so a token held only in VoiceStudio's Settings produces NO Authorization header and every gated file 401s. segmented_download instead interpolates it into `f"Bearer {token}"`, sending a malformed header that also inlines the raw secret into the request. - `_step_fetch_weights` passes it to snapshot_download, so gated engine weights 401 the same way. Every other resolve() caller already unwraps `.token`; these two were the outliers. Both now unwrap once, at the seam. The existing weights tests all stubbed resolve() to return None, so no test ever exercised a resolved token — which is why this went unnoticed. The new tests drive a real ResolvedToken through both seams and assert a `str` reaches every consumer, plus an integration test that installs the pyannote diarisation pipeline end to end: a weightless config_only repo validates, both dependency repos are fetched, and every call carries the bearer string. Two catalogue invariants keep the rest of #2163 from returning by edit: dependency repos must be revision-pinned (revision_for raises otherwise, so an unpinned one ships an always-failing install), and a config_only entry must declare config_required_files (without them the completeness check can never pass and the error lists no files at all — the shape the report hit on 0.5.2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4e55180f70
commit
37b498c891
@@ -230,7 +230,16 @@ def _segmented_snapshot(repo_id: str, *, endpoint: "str | None", revision: str)
|
||||
from services.segmented_download import segmented_download
|
||||
from services.token_resolver import resolve as _resolve_token
|
||||
|
||||
token = _resolve_token()
|
||||
# `resolve()` returns a ResolvedToken record, not the bearer string, and
|
||||
# every consumer below is typed `token: str | None`. Handing over the
|
||||
# record fails silently rather than loudly (#2163): huggingface_hub's
|
||||
# build_hf_headers ignores a non-str token and falls back to its own
|
||||
# ambient discovery, so a token held only in VoiceStudio's settings sends
|
||||
# NO Authorization header at all and every gated file 401s; our own
|
||||
# segmented_download interpolates it into `f"Bearer {token}"` and sends a
|
||||
# malformed header carrying the raw secret. Unwrap once, here.
|
||||
_resolved = _resolve_token()
|
||||
token = _resolved.token if _resolved else None
|
||||
api = HfApi(endpoint=endpoint, token=token)
|
||||
info = api.repo_info(repo_id, repo_type="model", revision=revision)
|
||||
commit = info.sha
|
||||
|
||||
@@ -1543,10 +1543,15 @@ def _step_fetch_weights(spec: SidecarSpec, job: dict) -> None:
|
||||
# other model download in the app — see setup/download.py): the
|
||||
# source checkout is unpinned upstream `main` anyway, and hf_hub
|
||||
# checksum-verifies each artifact. Hence the B615 waiver below.
|
||||
# Unwrap to the bearer string: snapshot_download takes `token: str |
|
||||
# None`, and a ResolvedToken record is ignored in favour of ambient
|
||||
# discovery, so gated engine weights 401 for a user whose token lives
|
||||
# in VoiceStudio's settings rather than HF's own cache (#2163).
|
||||
_resolved = resolve_token()
|
||||
kwargs: dict = {
|
||||
"repo_id": spec.weights_repo_id,
|
||||
"local_dir": str(wdir),
|
||||
"token": resolve_token(),
|
||||
"token": _resolved.token if _resolved else None,
|
||||
}
|
||||
if spec.weights_revision:
|
||||
kwargs["revision"] = spec.weights_revision
|
||||
|
||||
Reference in New Issue
Block a user