The CUDA torch pin stays < 2.11 because flash-attn, causal-conv1d, and mamba-ssm do not publish torch 2.11 wheels yet; bumping now would drop those prebuilt accelerators and force slow source builds. This adds a version-compat canary that queries the GitHub releases of all three and fails only once all of them ship torch 2.11 Linux wheels, which is when it is safe to raise the pin. Green while any is pending, skips on a network error, and wired into version-compat-ci (PR paths + daily cron).
422 lines
17 KiB
YAML
422 lines
17 KiB
YAML
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
|
#
|
|
# Cross-version compat canary for the four upstream packages whose
|
|
# release cadence regularly breaks unsloth + unsloth-zoo:
|
|
#
|
|
# 1. vLLM (LoRA worker manager, BnB loader, cumem allocator)
|
|
# 2. TRL / GRPO (trainer source rewriters in unsloth.models.rl*)
|
|
# 3. PEFT (LoraConfig, get_peft_model, LoraLayer, bnb integration)
|
|
# 4. sentence-transformers (Transformer/Pooling/Normalize, Trainer)
|
|
# 5. bitsandbytes (Linear4bit, dequantize_4bit)
|
|
#
|
|
# Strategy: GitHub raw-fetch + symbol grep against every tracked
|
|
# version (no pip install, CPU-only). When upstream renames a symbol
|
|
# we depend on, the matching test fails BEFORE a user hits it. The
|
|
# `main` branch entries give us a few-day lead on PyPI releases.
|
|
#
|
|
# Cross-references:
|
|
# tests/vllm_compat/test_vllm_pinned_symbols.py (vLLM symbols)
|
|
# tests/version_compat/test_trl_grpo_pinned_symbols.py
|
|
# tests/version_compat/test_peft_pinned_symbols.py
|
|
# tests/version_compat/test_sentence_transformers_pinned_symbols.py
|
|
# tests/version_compat/test_bitsandbytes_pinned_symbols.py
|
|
|
|
name: Version Compat CI
|
|
|
|
on:
|
|
pull_request:
|
|
# Trigger on any unsloth source change, not just the three previously
|
|
# named files. The symbol-existence tests verify that EVERY pinned
|
|
# upstream reference in unsloth still resolves; a new
|
|
# `from peft.foo import Bar` added in unsloth/kernels/whatever.py
|
|
# is just as much a compat regression risk as one added in
|
|
# unsloth/models/rl.py.
|
|
paths:
|
|
- 'unsloth/**'
|
|
- 'tests/vllm_compat/**'
|
|
- 'tests/version_compat/**'
|
|
- 'pyproject.toml'
|
|
- '.github/workflows/version-compat-ci.yml'
|
|
schedule:
|
|
# Daily 06:43 UTC. Catches upstream PyPI releases roughly within
|
|
# 24 h. Off the :00 / :30 fleet-collision spots.
|
|
- cron: '43 6 * * *'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
vllm-pinned-symbols:
|
|
name: vLLM pinned-symbol matrix (≥ 0.9.0 + main)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 12
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
# The test fetches from raw.githubusercontent.com and greps
|
|
# source. No pip install of vllm / torch / transformers is
|
|
# needed — that's the whole point of this canary.
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run vllm-compat suite
|
|
env:
|
|
# Authenticated requests get a 5000-req/h quota on raw
|
|
# fetches; unauthenticated is 60/h and trips on the matrix.
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
python -m pytest tests/vllm_compat/test_vllm_pinned_symbols.py -v --tb=short
|
|
|
|
trl-grpo-pinned-symbols:
|
|
name: TRL / GRPO pinned-symbol matrix
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run trl-compat suite
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
# PYTHONPATH=. so `from tests.version_compat._fetch import …`
|
|
# works without an editable install of unsloth itself.
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_trl_grpo_pinned_symbols.py \
|
|
-v --tb=short
|
|
|
|
peft-pinned-symbols:
|
|
name: PEFT pinned-symbol matrix (pyproject window + main)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run peft-compat suite
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_peft_pinned_symbols.py \
|
|
tests/version_compat/test_unsloth_zoo_save_merged_pinned_symbols.py \
|
|
-v --tb=short
|
|
|
|
st-pinned-symbols:
|
|
name: sentence-transformers pinned-symbol matrix
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run sentence-transformers compat suite
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_sentence_transformers_pinned_symbols.py \
|
|
-v --tb=short
|
|
|
|
bitsandbytes-pinned-symbols:
|
|
name: bitsandbytes pinned-symbol matrix
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run bitsandbytes compat suite
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_bitsandbytes_pinned_symbols.py \
|
|
-v --tb=short
|
|
|
|
transformers-pinned-symbols:
|
|
name: transformers pinned-symbol matrix (4.57.6 + 5.x + main)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 12
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run transformers compat suite
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_transformers_pinned_symbols.py \
|
|
-v --tb=short
|
|
|
|
torch-211-readiness:
|
|
name: torch 2.11 prebuilt-wheel readiness canary
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 6
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest only
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install 'pytest>=8'
|
|
- name: Run torch 2.11 readiness canary
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_torch_211_prebuilt_readiness.py \
|
|
-v --tb=short
|
|
|
|
# Optional second layer: actually `pip install` ONE representative
|
|
# version of each package and verify unsloth + unsloth-zoo modules
|
|
# import on it under the existing CUDA spoof. CPU-only, runs on
|
|
# ubuntu-latest. Catches the small set of breakages that the static
|
|
# symbol check misses (e.g. import-time side effects).
|
|
zoo-imports-under-spoof:
|
|
name: unsloth_zoo vllm/grpo/peft/st modules import under CUDA spoof
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
path: unsloth
|
|
- name: Clone unsloth-zoo @ main
|
|
run: |
|
|
# github.com occasionally 500s on the git fetch; retry so a
|
|
# single upstream blip does not fail CI.
|
|
for attempt in 1 2 3; do
|
|
rm -rf "$RUNNER_TEMP/unsloth-zoo"
|
|
if git clone --depth=1 https://github.com/unslothai/unsloth-zoo \
|
|
"$RUNNER_TEMP/unsloth-zoo"; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "::error::git clone unsloth-zoo failed after 3 attempts"
|
|
exit 1
|
|
fi
|
|
delay=$((5 * attempt))
|
|
echo "::warning::clone failed (attempt $attempt/3), retrying in ${delay}s..."
|
|
sleep "$delay"
|
|
done
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install CPU torch + supported pkg pins
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
# CPU torch (vllm/peft/st all depend on it).
|
|
pip install --index-url https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple \
|
|
'torch>=2.4,<2.11' 'torchvision<0.26' 'torchcodec<0.10'
|
|
# torchcodec is a hard requirement on transformers 5.x:
|
|
# transformers/audio_utils.py:55 does
|
|
# `importlib.metadata.version("torchcodec")` UNCONDITIONALLY,
|
|
# which raises PackageNotFoundError on a CPU runner that
|
|
# otherwise has no audio path -- and that error trickles up
|
|
# through every `import unsloth_zoo.<module>` because
|
|
# unsloth-zoo's vision_utils transitively pulls
|
|
# transformers.processing_utils (-> audio_utils). The 0.10
|
|
# cap mirrors the torch 2.10 / torchvision 0.26 ABI window
|
|
# we already pin above.
|
|
# Ladder of supported floor versions per pyproject.toml.
|
|
pip install \
|
|
'transformers>=4.56,<5.6' 'trl>=0.22,<0.26' \
|
|
'peft>=0.18.0' 'sentence-transformers>=5.0' \
|
|
'accelerate>=1.0' 'datasets>=3.4,<5' \
|
|
'bitsandbytes>=0.45.5' \
|
|
sentencepiece protobuf safetensors numpy 'pytest>=8' \
|
|
'huggingface_hub>=0.34' tqdm packaging psutil triton Pillow
|
|
# Editable-install both repos so the test imports the
|
|
# checkouts (not whatever stale PyPI version pip resolved).
|
|
pip install --no-deps -e "$RUNNER_TEMP/unsloth-zoo"
|
|
pip install --no-deps -e ./unsloth
|
|
- name: Run vllm_compat zoo-imports tests under spoof
|
|
env:
|
|
UNSLOTH_IS_PRESENT: '1'
|
|
UNSLOTH_COMPILE_DISABLE: '1'
|
|
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python
|
|
run: |
|
|
cd unsloth
|
|
# tests/vllm_compat/test_unsloth_zoo_imports.py: narrow vllm/grpo
|
|
# import gates (5 tests).
|
|
# tests/vllm_compat/test_extended_module_imports.py: full sweep
|
|
# of unsloth_zoo + unsloth.models.* modules + RL dispatch
|
|
# table population + FastModel API surface under spoof
|
|
# (~30 tests). Catches transformers / peft / bnb symbol pin
|
|
# drift at module-top BEFORE any runtime call.
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/vllm_compat/test_unsloth_zoo_imports.py \
|
|
tests/vllm_compat/test_extended_module_imports.py \
|
|
-v --tb=short
|
|
|
|
# Fake-CUDA GRPO/SFT/DPO patch run against REAL TRL (latest + main). Unlike
|
|
# the static symbol/source greps above, this drives unsloth's actual
|
|
# source-transform patchers (models/rl.py + rl_replacements.py) on a CPU-only
|
|
# runner under the tests/conftest.py spoof harness -- no GPU, no training.
|
|
# Catches structural TRL drift the greps miss (e.g. TRL 1.7.0's 2->3-tuple
|
|
# per-token-logps return, restructured PEFT ref-adapter block) by asserting
|
|
# the generated Unsloth trainer still satisfies the transform contracts.
|
|
grpo-fake-run:
|
|
name: GRPO fake-run (latest + main TRL, CPU spoof)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 18
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
path: unsloth
|
|
- name: Clone unsloth-zoo @ main
|
|
run: |
|
|
for attempt in 1 2 3; do
|
|
rm -rf "$RUNNER_TEMP/unsloth-zoo"
|
|
if git clone --depth=1 https://github.com/unslothai/unsloth-zoo \
|
|
"$RUNNER_TEMP/unsloth-zoo"; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "::error::git clone unsloth-zoo failed after 3 attempts"
|
|
exit 1
|
|
fi
|
|
delay=$((5 * attempt))
|
|
echo "::warning::clone failed (attempt $attempt/3), retrying in ${delay}s..."
|
|
sleep "$delay"
|
|
done
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install CPU torch + ecosystem + TRL latest
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install --index-url https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple \
|
|
'torch>=2.4,<2.11' 'torchvision<0.26' 'torchcodec<0.10'
|
|
# Ecosystem floors unsloth needs; TRL itself is installed last so it
|
|
# can pull the transformers/peft it requires.
|
|
pip install \
|
|
'transformers>=4.57' 'peft>=0.18.0' 'accelerate>=1.0' 'datasets>=3.4,<5' \
|
|
'bitsandbytes>=0.45.5' sentencepiece protobuf safetensors numpy 'pytest>=8' \
|
|
'huggingface_hub>=0.34' tqdm packaging psutil triton Pillow
|
|
pip install --upgrade trl
|
|
pip install --no-deps -e "$RUNNER_TEMP/unsloth-zoo"
|
|
pip install --no-deps -e ./unsloth
|
|
- name: Fake-run vs TRL latest
|
|
env:
|
|
UNSLOTH_IS_PRESENT: '1'
|
|
UNSLOTH_COMPILE_DISABLE: '1'
|
|
# Disable dynamo/inductor at the process level, before conftest.py's early
|
|
# `import unsloth`, so the GRPO hot path never compiles on the GPU-less runner
|
|
# (defense in depth; the CPU fake-train also flips this at runtime).
|
|
TORCHDYNAMO_DISABLE: '1'
|
|
TORCH_COMPILE_DISABLE: '1'
|
|
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python
|
|
run: |
|
|
cd unsloth
|
|
python -c "import trl; print('Resolved TRL', trl.__version__)"
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_trl_grpo_fake_run.py \
|
|
tests/version_compat/test_trl_fake_train_cpu.py \
|
|
-v --tb=short
|
|
# `main` is scheduled/dispatch-only so PR jobs stay fast and a bleeding-edge
|
|
# TRL break does not red every PR. github.event_name is valid in a step if.
|
|
- name: Fake-run vs TRL main (scheduled / dispatch only)
|
|
if: ${{ github.event_name != 'pull_request' }}
|
|
env:
|
|
UNSLOTH_IS_PRESENT: '1'
|
|
UNSLOTH_COMPILE_DISABLE: '1'
|
|
TORCHDYNAMO_DISABLE: '1'
|
|
TORCH_COMPILE_DISABLE: '1'
|
|
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python
|
|
run: |
|
|
pip install --upgrade "git+https://github.com/huggingface/trl"
|
|
cd unsloth
|
|
python -c "import trl; print('Resolved TRL', trl.__version__)"
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/version_compat/test_trl_grpo_fake_run.py \
|
|
tests/version_compat/test_trl_fake_train_cpu.py \
|
|
-v --tb=short
|
|
|
|
# Daily-only: same suites but with --strict on importable upstream
|
|
# tags. Schedule-only so PR jobs stay fast; cron tolerates a flake.
|
|
daily-fresh-fetch:
|
|
name: daily fresh-fetch sweep (cron only)
|
|
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
- name: Install pytest
|
|
run: pip install 'pytest>=8'
|
|
- name: Run all version-compat suites in one process (no cache)
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/vllm_compat/test_vllm_pinned_symbols.py \
|
|
tests/version_compat/ \
|
|
-v --tb=short
|