Skip to content

Commit 7e189f8

Browse files
authored
fix fork PR rate limiting with HF model caching (#1817)
* fix fork PR rate limiting with HF model caching * trigger test * make tests offline to verify * revert * Add create model cache workflow * rename labels * fix * Update label names * restore cache for nightly and slow tests workflows * exclude model higher than 200mb
1 parent f48d93f commit 7e189f8

5 files changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Create Model Cache
2+
3+
on:
4+
pull_request:
5+
types: [labeled]
6+
workflow_dispatch:
7+
inputs:
8+
force_update:
9+
description: "Force the (re-)download, overriding the cached versions if exist"
10+
type: boolean
11+
default: false
12+
13+
permissions:
14+
contents: read
15+
actions: write # needed to delete the stale cache entry
16+
17+
env:
18+
HF_TOKEN: ${{ secrets.HF_HUB_READ_TOKEN }}
19+
UV_SYSTEM_PYTHON: true
20+
21+
jobs:
22+
create-cache:
23+
if: >-
24+
github.event_name == 'workflow_dispatch' ||
25+
github.event.label.name == 'update-hf-cache' ||
26+
github.event.label.name == 'update-hf-cache-force'
27+
runs-on: ubuntu-22.04
28+
29+
steps:
30+
- name: Checkout code
31+
uses: actions/checkout@v6.0.2
32+
33+
- name: Setup Python
34+
uses: actions/setup-python@v6
35+
with:
36+
python-version: "3.10"
37+
38+
- name: Check HF token is available
39+
run: |
40+
if [ -z "$HF_TOKEN" ]; then
41+
echo "HF_TOKEN secret not available — cannot run on fork PRs"
42+
exit 1
43+
fi
44+
45+
- name: Install dependencies
46+
run: |
47+
pip install --upgrade pip uv
48+
uv pip install .[tests]
49+
50+
- name: Restore existing cache
51+
uses: actions/cache/restore@v4
52+
with:
53+
path: ~/.cache/huggingface/hub
54+
key: hf-models
55+
56+
- name: Download models for cache
57+
run: python tests/scripts/download_cached_models.py
58+
env:
59+
FORCE_DOWNLOAD: ${{ inputs.force_update || github.event.label.name == 'update-hf-cache-force' }}
60+
61+
- name: Delete existing cache entry
62+
run: gh cache delete hf-models --repo ${{ github.repository }} || true
63+
env:
64+
GH_TOKEN: ${{ github.token }}
65+
66+
- name: Save HF models cache
67+
uses: actions/cache/save@v4
68+
with:
69+
path: ~/.cache/huggingface/hub
70+
key: hf-models

‎.github/workflows/test_openvino.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ jobs:
5757
pip install --upgrade pip uv
5858
uv pip install .[tests] librosa diffusers
5959
60+
- name: Restore HF models cache
61+
id: hf-cache-restore
62+
if: env.HF_TOKEN == ''
63+
uses: actions/cache/restore@v4
64+
with:
65+
path: ~/.cache/huggingface/hub
66+
key: hf-models
67+
6068
- if: ${{ matrix.test-pattern == '*modeling*' || matrix.test-pattern == '*quantization*' || matrix.test-pattern == '*transformation*' }}
6169
name: Install OpenVINO
6270
run: |

‎.github/workflows/test_openvino_nightly.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ jobs:
9999
pip install --upgrade pip uv
100100
uv pip install .[tests] librosa diffusers
101101
102+
- name: Restore HF models cache
103+
id: hf-cache-restore
104+
if: env.HF_TOKEN == ''
105+
uses: actions/cache/restore@v4
106+
with:
107+
path: ~/.cache/huggingface/hub
108+
key: hf-models
109+
102110
- if: ${{ matrix.openvino-version == 'openvino-nightly' }}
103111
name: Install OpenVINO Nightly
104112
run: |

‎.github/workflows/test_openvino_slow.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ jobs:
6161
python -m pip install --upgrade pip uv
6262
uv pip install .[tests] librosa diffusers
6363
64+
- name: Restore HF models cache
65+
id: hf-cache-restore
66+
if: env.HF_TOKEN == ''
67+
uses: actions/cache/restore@v4
68+
with:
69+
path: ~/.cache/huggingface/hub
70+
key: hf-models
71+
6472
- if: ${{ matrix.transformers-version != 'latest' && matrix.transformers-version != 'main' }}
6573
name: Install specific dependencies and versions required for older transformers
6674
run: |
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2026 The HuggingFace Team. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import os
15+
import sys
16+
from pathlib import Path
17+
18+
from huggingface_hub import HfApi, constants, scan_cache_dir, snapshot_download
19+
20+
21+
sys.path.insert(0, str(Path(__file__).parents[1] / "openvino"))
22+
23+
from utils_tests import MODEL_NAMES
24+
25+
26+
# github actions repo cache limit is 10 GB so using 9 GB
27+
CACHE_LIMIT = 9 * 10**9
28+
# exclude model larger than 100 MB
29+
MODEL_SIZE_THRESHOLD = 100 * 10**6
30+
31+
32+
def hub_size(api: HfApi, repo_id: str) -> int:
33+
try:
34+
info = api.model_info(repo_id, files_metadata=True)
35+
return sum(sib.size or 0 for sib in (info.siblings or []))
36+
except Exception as e:
37+
print(f"Size query failed for {repo_id}: {e}")
38+
return -1
39+
40+
41+
def main() -> None:
42+
api = HfApi()
43+
candidates = {}
44+
45+
for name, repo_id in MODEL_NAMES.items():
46+
if not isinstance(repo_id, str) or not repo_id or repo_id.startswith("/"):
47+
continue
48+
candidates[repo_id] = name
49+
50+
if os.path.exists(constants.HF_HUB_CACHE):
51+
cache_info = scan_cache_dir()
52+
cached_repos = {repo.repo_id for repo in cache_info.repos}
53+
sum_repo_size = cache_info.size_on_disk
54+
else:
55+
cached_repos = set()
56+
sum_repo_size = 0
57+
58+
downloaded = []
59+
skipped_too_large = []
60+
skipped_cache_full = []
61+
failed = []
62+
force_update = os.environ.get("FORCE_DOWNLOAD", "").lower() in ("1", "true", "yes")
63+
for repo_id, name in candidates.items():
64+
if not force_update and repo_id in cached_repos:
65+
continue
66+
67+
size = hub_size(api, repo_id)
68+
if size < 0:
69+
failed.append(name)
70+
continue
71+
72+
if size > MODEL_SIZE_THRESHOLD:
73+
skipped_too_large.append(name)
74+
continue
75+
76+
if sum_repo_size + size > CACHE_LIMIT:
77+
skipped_cache_full.append(name)
78+
continue
79+
80+
try:
81+
snapshot_download(repo_id)
82+
sum_repo_size += size
83+
downloaded.append(name)
84+
except Exception as e:
85+
print(f"[FAIL] {name}: {e}", file=sys.stderr)
86+
failed.append(repo_id)
87+
88+
print(f"Downloaded : {len(downloaded)} ({', '.join(downloaded)})")
89+
print(f"Skipped (too large) : {len(skipped_too_large)} ({', '.join(skipped_too_large)})")
90+
print(f"Skipped (cache full): {len(skipped_cache_full)} ({', '.join(skipped_cache_full)})")
91+
print(f"Loading failed : {len(failed)} ({', '.join(failed)})")
92+
93+
94+
if __name__ == "__main__":
95+
main()

0 commit comments

Comments
 (0)