Перейти к содержимому

Hugging Face Hub

NORA может работать как кеширующий прокси (pull-through) для публичных моделей Hugging Face Hub. Сведения о модели, ревизии и списки файлов NORA запрашивает у Hub при каждом запросе; файлы моделей кешируются по дайджесту, которым их называет Hub, и сверяются с ним при скачивании. Это реестр только для проксирования, и он включается явно: по умолчанию выключен и не входит в all. Доступен с NORA 1.4.0.

Окно терминала
NORA_HF_ENABLED=true nora serve

Если реестры выбираются через NORA_REGISTRIES_ENABLE (или [registries] enable), назовите его там: all оставляет прокси Hugging Face выключенным, all,hf включает его вместе со всеми остальными.

config.toml:

[hf]
enabled = true
proxy = "https://huggingface.co"
proxy_timeout = 30 # секунды
cdn_hosts = ["hf.co", "huggingface.co"] # куда может вести редирект файла

Переменные окружения:

ПеременнаяОписаниеПо умолчанию
NORA_HF_ENABLEDВключить прокси Hugging Facefalse
NORA_HF_PROXYURL upstream Hub; пустое значение отключает upstreamhttps://huggingface.co
NORA_HF_PROXY_TIMEOUTТаймаут upstream в секундах30
NORA_HF_CDN_HOSTSХосты, куда может вести редирект файла, через запятую: каждая запись и имена под нейhf.co,huggingface.co

Укажите NORA для huggingface_hub через HF_ENDPOINT:

Окно терминала
export HF_ENDPOINT=http://nora.example.com:4000/hf
hf download openai-community/gpt2 # весь снапшот
hf download openai-community/gpt2 config.json # один файл
hf download openai-community/gpt2 config.json --revision refs/pr/198

Python API читает ту же переменную:

from huggingface_hub import hf_hub_download, snapshot_download
hf_hub_download("openai-community/gpt2", "config.json")
snapshot_download("openai-community/gpt2")

Проверено с huggingface_hub 0.36.0, 1.0.0, 1.33.0, 2.0.0, 2.1.1 и 2.2.0, а с 2.1.1 — и против huggingface.co: обычный файл, LFS-файл репозитория на Xet, устаревший id репозитория (gpt2), ревизии refs/pr/N и коммит — все запросы через NORA. С установленным hf_xet клиент всё равно скачивает по обычному HTTP: NORA никогда не объявляет Xet.

  • Редиректы остаются внутри NORA. На запрос файла Hub отвечает редиректом — относительным 307 на свой кеш resolve или 302 на CDN для LFS-файла. NORA проходит их сама, поэтому до клиента не доходит ни один Location, и каждый байт идёт через NORA. Редирект проходится только на origin самого upstream или по https на хост из cdn_hosts — CDN Hub, us.aws.cdn.hf.co, лежит под hf.co. IP-адрес совпадает как адрес, а не как суффикс; по http на CDN NORA идёт, только если весь обход от http-upstream шёл по http, и никогда — после https. Запись открывает все порты своего хоста, поэтому localhost и внутренние имена в боевой список не пишут. Редирект на любой другой хост — 502, и к этому хосту NORA не обращается. Зеркалу, которое отдаёт файлы с другого хоста, этот хост нужно добавить в cdn_hosts.
  • Всегда свежо. Ответ Hub на resolve запрашивается при каждом скачивании: в нём названы коммит и дайджест файла. Поэтому main никогда не устаревает, а байты берутся из кеша, если этот дайджест у NORA уже есть.
  • Кеш по дайджесту. Файлы хранятся по репозиторию под дайджестом, которым их называет Hub (sha256 для LFS-файла, git blob sha1 для обычного): hf/models/{repo}/blobs/{digest}. Файл весов, который не меняется между коммитами, скачивается один раз.
  • Сверка при скачивании. Файл сверяется с этим дайджестом, а его последний кусок придерживается до совпадения: испорченный файл обрывается на кусок раньше и не сохраняется. Файл, прочитанный из кеша, так же сверяется со своим пином; не прошедшая сверку копия удаляется, и следующее скачивание берёт файл заново. Чтение части файла (range) из кеша не сверяется — часть нельзя перехешировать, как и в любом формате.
  • Проходят насквозь, не сохраняясь: range-запрос к ещё не закешированному файлу, HEAD и файл, etag которого не называет дайджест. Клиент, ушедший раньше времени, ничего после себя не оставляет.
  • Без Xet. xetHash убирается из метаданных на любой глубине, заголовки и ссылки Xet до клиента не доходят, а эндпоинты токенов Xet отвечают 404.
ЭндпоинтМетодСтатусПримечание
/hf/api/models/{repo}GETПолнаяСведения о модели, без xetHash
/hf/api/models/{repo}/revision/{rev}GETПолнаяРевизия в том виде, как её закодировал клиент (refs%2Fpr%2F1 остаётся одним сегментом)
/hf/api/models/{repo}/tree/{rev}[/{path}]GETПолнаяСписок по страницам; Link rel="next" указывает под /hf
/hf/{repo}/resolve/{rev}/{file}HEADПолнаяОтвет из ответа Hub на resolve (коммит, etag, размер)
/hf/{repo}/resolve/{rev}/{file}GETПолнаяКеш по дайджесту; Range поддерживается
/hf/api/models/{repo}/xet-read-token/…, xet-write-token/…GETОтказ (404)NORA никогда не отдаёт Xet
/hf/api/datasets/…, /hf/datasets/…*— (501)Только модели
Загрузка (POST, PUT, DELETE, PATCH)*— (405)Только прокси

Закешированные файлы учитываются в [gc] proxy_cache_max_bytes (NORA_GC_PROXY_CACHE_MAX_BYTES) вместе с проксированными пакетами RPM и Debian; сверх лимита GC вытесняет сначала самые старые файлы. Правила retention, где назван hf, их подрезают (older_than_days, а keep_last считается в файлах); правило * к этому формату, включаемому явно, не применяется.

Скачивание называется по id репозитория, а его версия — коммит. Правило на репозиторий отказывает до запроса к Hub; правило на коммит действует для main, когда main указывает на этот коммит, — и для уже закешированного файла. Псевдоним — устаревший id вроде gpt2, другой регистр, переименованный репозиторий — проверяется и под каноническим id (openai-community/gpt2), который NORA узнаёт у Hub; если узнать его не удалось, курация в режиме enforce отказывает (502), а в режиме audit записывает скачивание под запрошенным id и пропускает. Allowlist коммитов без коммита не пропускает ничего. curation.min_release_age при включённом прокси Hugging Face отвергается при старте: у коммита Hub нет даты публикации, которую NORA может прочитать. Подробнее — Курация.

  • Только модели: датасеты и Spaces не отдаются (501).
  • Только публичные репозитории: токен клиента никогда не уходит в upstream, поэтому закрытый (gated или private) репозиторий отвечает собственным отказом Hub (его статус, X-Error-Code и сообщение).
  • Общий лимит запросов: NORA обращается к Hub без токена, поэтому анонимные лимиты Hub делят все клиенты за NORA, а закешированный файл всё равно стоит одного запроса resolve. 429 передаётся клиенту с Retry-After, RateLimit и RateLimit-Policy, по которым huggingface_hub ждёт сброса окна.
  • Hub должен быть доступен для скачивания: ответ resolve запрашивается каждый раз, отдачи устаревшей копии пока нет.
  • HfFileSystem не проверялся; его вызов paths-info — это POST, он отвечает 405.
  • Псевдоним и канонический id кешируются отдельно (gpt2 и openai-community/gpt2).
  • Веб-интерфейс: формат виден на панели, но закешированные файлы пока не перечисляются.
  • nora backup включает кеш — каждый сохранённый файл весов. Исключить hf/ пока нельзя, поэтому перед резервной копией большого кеша держите его в пределах proxy_cache_max_bytes или правила retention.
  • S3: задайте для бакета правило жизненного цикла AbortIncompleteMultipartUpload. Заполнение кеша, прерванное во время загрузки (перезапуск), оставляет части multipart-загрузки, которые объектное хранилище держит, пока их не отменят.
  • Загрузка через NORA не поддерживается (405).