let the model loader authenticate to the Hub, like the diarization worker always has

The diarization worker resolved an HF token from the environment or a protected
file in the config dir, and gated pyannote repos worked. The MODEL LOADING path
had no such resolution: the host runner forwards HF_TOKEN from the host
environment, which is only set if whoever launched the container exported it,
and on this install it was not -- so huggingface_hub went out unauthenticated
for every from_pretrained.

That is invisible until a repo is gated, and then it fails as 401 "must be
authenticated", which reads exactly like terms that have not been accepted even
when they have. On stabilityai/stable-audio-open-1.0 the account had access and
the token on disk could fetch the weights with curl, while the engine loading
them could not -- so the obvious reading was a licence problem, and it was a
credentials-plumbing problem.

The resolution moves into codai/hf_cache.py, which already owns how a process
talks to the Hub, and is applied from the same entry point as the cache
settings: both have to be in place before huggingface_hub is imported, and an
install that configured one has configured the other in the same directory. The
file/env list is kept identical to the worker's, and the worker now calls it,
because two answers to "where is the token" is how one path authenticates and
another does not. Its per-request override still wins. Never logged: it ends up
in request headers, and a test holds that.

Also torchsde, which Stable Audio Open's CosineDPMSolverMultistepScheduler is
built on. diffusers imports it lazily, so without it the model downloads its
3.8 GB in full and only then fails at request time, after the wait.
Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014T3rZozi1Ehk96NG1WesgC
parent 5b92f3fe
......@@ -131,20 +131,15 @@ def _resolve_hf_token(config: dict) -> str:
tok = (config or {}).get("hf_token")
if tok:
return tok
for var in ("HF_TOKEN", "HUGGINGFACE_TOKEN", "HUGGING_FACE_HUB_TOKEN"):
if os.environ.get(var):
return os.environ[var]
for path in (os.environ.get("CODERAI_HF_TOKEN_FILE"),
os.path.expanduser("~/.coderai/hf_token"),
"/config/coderai/hf_token"):
if path and os.path.isfile(path):
try:
t = open(path).read().strip()
if t:
return t
except OSError:
pass
return ""
# The env/file resolution this function used to carry now lives in
# codai.hf_cache, so the model-loading path resolves the token the same way
# instead of going out unauthenticated. Only the per-request override above
# is specific to diarization.
try:
from codai.hf_cache import configured_hf_token
return configured_hf_token() or ""
except Exception:
return ""
def ensure_service(model_name: str = None, config: dict = None,
......
......@@ -121,6 +121,80 @@ def child_env(env: Optional[dict] = None, hf_cache_dir: Optional[str] = None) ->
return out
#: Names huggingface_hub / transformers accept for the Hub token, in the order
#: the libraries themselves prefer.
_TOKEN_VAR_NAMES = ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN")
#: Every name a token may arrive under, and every file it may sit in. Kept the
#: same as codai.api.pyannote_worker._resolve_hf_token, which has worked for
#: gated pyannote repos for a long time -- two different answers to "where is
#: the token" is how one path authenticates and another does not.
_TOKEN_ENV_NAMES = ("HF_TOKEN", "HUGGINGFACE_TOKEN", "HUGGING_FACE_HUB_TOKEN")
def _token_files(config_dir: Optional[str] = None) -> list:
out = []
env_file = os.environ.get("CODERAI_HF_TOKEN_FILE")
if env_file:
out.append(env_file)
if config_dir:
out.append(os.path.join(config_dir, "hf_token"))
out.append(os.path.expanduser("~/.coderai/hf_token"))
out.append("/config/coderai/hf_token")
return out
def configured_hf_token(config_dir: Optional[str] = None) -> Optional[str]:
"""The operator's HuggingFace token, or None.
The environment first, then the protected file in the coderai config dir --
the same resolution the diarization worker has always used for gated
pyannote repos, so a token that works for one path works for all of them.
It exists here because the MODEL LOADING path had no such resolution. The
host runner forwards HF_TOKEN from the host environment, which is only set
if whoever launched the container exported it; on an install where it was
not, huggingface_hub went out unauthenticated for every from_pretrained.
That is invisible until a repo is gated, and then it fails as 401 "must be
authenticated" -- which reads exactly like terms that have not been
accepted, even when they have. Measured 2026-10-28 on
stabilityai/stable-audio-open-1.0: the account had access and the token on
disk could fetch the weights, while the engine loading them could not.
Never logged: it is a credential, and it ends up in request headers.
"""
for name in _TOKEN_ENV_NAMES:
tok = (os.environ.get(name) or "").strip()
if tok:
return tok
for path in _token_files(config_dir):
try:
if path and os.path.isfile(path):
with open(path) as fh:
tok = fh.read().strip()
if tok:
return tok
except Exception:
continue
return None
def apply_hf_token(config_dir: Optional[str] = None) -> bool:
"""Put the configured token in the environment. Idempotent.
Returns whether a token is now present. Both canonical names are set,
because library versions differ in which they read, and a token set only
in the one a given version ignores is the same as no token at all.
"""
tok = configured_hf_token(config_dir)
if not tok:
return False
for name in _TOKEN_VAR_NAMES:
os.environ[name] = tok
return True
def apply_hf_cache_env(hf_cache_dir: Optional[str] = None,
config_dir: Optional[str] = None) -> Optional[str]:
"""Point this process at the configured HuggingFace cache. Idempotent.
......@@ -134,6 +208,14 @@ def apply_hf_cache_env(hf_cache_dir: Optional[str] = None,
environment is then left exactly as it was: no directory is invented)."""
if os.environ.get(OPT_OUT):
return None
# The token travels with the cache settings: both answer "how does this
# process talk to the Hub", both are needed before huggingface_hub is
# imported, and an install that configured one has configured the other in
# the same directory.
try:
apply_hf_token(config_dir)
except Exception:
pass
d = hf_cache_dir or configured_hf_cache_dir(config_dir)
if not d:
return None
......
......@@ -96,6 +96,11 @@ timm>=0.9.0 # vision model backbones (depth/segment endpoints
# audiocraft is Meta's MusicGen/AudioGen library; install separately if desired:
# pip install audiocraft
# AudioLDM2 is available via diffusers (already listed above)
# Stable Audio Open's scheduler (CosineDPMSolverMultistepScheduler) is built on
# torchsde, and diffusers imports it lazily — so without this the model
# downloads its 3.8 GB in full and only then fails with "requires the torchsde
# library", at request time, after the wait.
torchsde>=0.2.6
# Voice cloning (F5-TTS zero-shot voice cloning)
f5-tts>=1.1.0
......
# CoderAI - OpenAI-compatible API server
# Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""One answer to "where is the HuggingFace token".
The diarization worker had resolved it from the environment or a protected
file in the config dir for a long time, and gated pyannote repos worked. The
MODEL LOADING path had no such resolution: the host runner forwards HF_TOKEN
from the host environment, which is only set if whoever launched the container
exported it, so on an install where it was not, huggingface_hub went out
unauthenticated for every from_pretrained.
That is invisible until a repo is gated, and then it fails as 401 "must be
authenticated" -- which reads exactly like terms that have not been accepted,
even when they have. Measured on stabilityai/stable-audio-open-1.0: the
account had access, a token on disk could fetch the weights with curl, and the
engine loading them could not.
"""
import os
import pytest
from codai import hf_cache
@pytest.fixture(autouse=True)
def _clean_env(monkeypatch):
for name in ("HF_TOKEN", "HUGGINGFACE_TOKEN", "HUGGING_FACE_HUB_TOKEN",
"CODERAI_HF_TOKEN_FILE"):
monkeypatch.delenv(name, raising=False)
# ------------------------------------------------------------ the resolution
def test_the_environment_wins(monkeypatch):
monkeypatch.setenv("HF_TOKEN", "from-env")
assert hf_cache.configured_hf_token() == "from-env"
@pytest.mark.parametrize("name", ["HF_TOKEN", "HUGGINGFACE_TOKEN",
"HUGGING_FACE_HUB_TOKEN"])
def test_every_name_the_libraries_use_is_accepted(monkeypatch, name):
"""A token under the one name a given library version ignores is the same
as no token at all, so all three are read."""
monkeypatch.setenv(name, "tok-" + name)
assert hf_cache.configured_hf_token() == "tok-" + name
def test_a_token_file_is_read_when_the_environment_is_empty(tmp_path, monkeypatch):
f = tmp_path / "hf_token"
f.write_text("from-file\n")
monkeypatch.setenv("CODERAI_HF_TOKEN_FILE", str(f))
assert hf_cache.configured_hf_token() == "from-file"
def test_the_config_dir_file_is_read(tmp_path):
(tmp_path / "hf_token").write_text(" from-config-dir \n")
assert hf_cache.configured_hf_token(str(tmp_path)) == "from-config-dir"
@pytest.fixture
def only_here(tmp_path, monkeypatch):
"""Search ONLY tmp_path, so "no token" can actually be tested.
A config_dir does not restrict the search -- the fallbacks
(~/.coderai/hf_token, /config/coderai/hf_token) are the point of the
resolver -- and on a host that has one, a test asserting "none found"
silently finds the real one.
"""
monkeypatch.setattr(hf_cache, "_token_files",
lambda config_dir=None: [str(tmp_path / "hf_token")])
return tmp_path
def test_an_empty_token_file_is_not_a_token(only_here):
(only_here / "hf_token").write_text(" \n")
assert hf_cache.configured_hf_token(str(only_here)) is None
def test_a_missing_file_is_not_an_error(monkeypatch):
monkeypatch.setenv("CODERAI_HF_TOKEN_FILE", "/nonexistent/hf_token")
hf_cache.configured_hf_token("/also/nonexistent") # must not raise
# ------------------------------------------------------------- applying it
def test_applying_sets_both_canonical_names(tmp_path, monkeypatch):
(tmp_path / "hf_token").write_text("abc123")
assert hf_cache.apply_hf_token(str(tmp_path)) is True
assert os.environ.get("HF_TOKEN") == "abc123"
assert os.environ.get("HUGGING_FACE_HUB_TOKEN") == "abc123"
def test_applying_without_a_token_changes_nothing(only_here):
assert hf_cache.apply_hf_token(str(only_here)) is False
assert "HF_TOKEN" not in os.environ
def test_applying_is_idempotent(tmp_path):
(tmp_path / "hf_token").write_text("abc123")
assert hf_cache.apply_hf_token(str(tmp_path)) is True
assert hf_cache.apply_hf_token(str(tmp_path)) is True
assert os.environ.get("HF_TOKEN") == "abc123"
def test_the_cache_entry_point_applies_the_token_too():
"""Both answer "how does this process talk to the Hub", and both have to
be set before huggingface_hub is imported."""
import inspect
src = inspect.getsource(hf_cache.apply_hf_cache_env)
assert "apply_hf_token" in src, (
"the token is no longer applied where the cache settings are, so a "
"process can be pointed at the right cache and still be anonymous")
# ------------------------------------------------- one implementation, shared
def test_the_diarization_worker_uses_the_shared_resolver():
"""Two answers to "where is the token" is how one path authenticates and
another does not."""
import inspect
from codai.api import pyannote_worker
src = inspect.getsource(pyannote_worker._resolve_hf_token)
assert "configured_hf_token" in src
# Its own per-request override must still win over anything configured.
assert pyannote_worker._resolve_hf_token({"hf_token": "per-request"}) == \
"per-request"
def test_a_per_request_token_still_beats_the_configured_one(tmp_path, monkeypatch):
monkeypatch.setenv("HF_TOKEN", "configured")
from codai.api import pyannote_worker
assert pyannote_worker._resolve_hf_token({"hf_token": "explicit"}) == "explicit"
assert pyannote_worker._resolve_hf_token({}) == "configured"
def test_the_token_is_never_logged():
"""It ends up in request headers; it must not end up in the log too."""
import inspect
src = inspect.getsource(hf_cache)
body = src[src.index("def configured_hf_token"):]
body = body[:body.index("def apply_hf_cache_env")]
for bad in ("print(", "_log.info", "logger.info", "logging.info"):
assert bad not in body, f"the token resolver calls {bad}"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment