Catches upstream API drift early — before a PyPI release breaks user
workloads. For each tracked package + version, fetch the relevant
source files from raw.githubusercontent.com and grep for the symbols
unsloth + unsloth-zoo monkey-patch, subclass, or eval-import. No pip
install required, CPU-only, runs PR-time + daily cron.
Files:
- tests/vllm_compat/test_vllm_pinned_symbols.py
extend VLLM_TAGS from {0.9.0..0.15.0} to include
{0.16.0, 0.17.1, 0.18.1, 0.19.1, 0.20.1, main}.
- tests/version_compat/_fetch.py
shared fetch + grep helpers (fetch_text / has_def / first_match).
- tests/version_compat/test_trl_grpo_pinned_symbols.py
12 TRL tags (0.18.2 -> v1.3.0 + main) covering the supported
window (pyproject pin trl>=0.18.2,!=0.19.0,<=0.24.0) plus
above-cap canaries. Asserts:
* top-level GRPOTrainer / GRPOConfig / SFTTrainer / SFTConfig
re-exports (used by `from trl import X`)
* trl.trainer.grpo_trainer.GRPOTrainer class
* trl.trainer.grpo_config.GRPOConfig (or grpo_trainer.py fallback)
* DataCollatorForPreference reachable from EITHER dpo_trainer or
utils (rl_replacements.py:318 string-emits the dpo_trainer path)
* trl.trainer.utils.pad (rl_replacements.py:326)
* unwrap_model_for_generation in any known submodule
(rl.py:152-155 try/except handles both)
* trl.experimental.openenv (gated; rl_replacements.py:1765-1770)
* trl.generation.vllm_generation (gated; rl_replacements.py:1846)
* trl.__version__ exported via literal / submodule / metadata
- tests/version_compat/test_peft_pinned_symbols.py
5 PEFT tags (0.18.0 -> 0.19.1 + main). Asserts:
* top-level LoraConfig / get_peft_model / PeftModel
* peft.tuners.lora.LoraConfig at canonical path
* get_peft_model in mapping.py / mapping_func.py
(peft 0.18 split this out)
* peft.tuners.lora.LoraLayer
* peft.tuners.lora.bnb (Linear4bit / Linear8bitLt)
- tests/version_compat/test_sentence_transformers_pinned_symbols.py
6 ST tags (5.0.0 -> 5.4.1 + main). Handles BOTH layouts:
legacy (< 5.4): sentence_transformers/models[.py|/__init__.py]
modular (>= 5.4): classes under
sentence_transformers/base/modules/*
sentence_transformers/sentence_transformer/modules/*
Plus verifies the deprecated-import shim
(`setup_deprecated_module_imports`) is wired in __init__.py
so `from sentence_transformers.models import Pooling` keeps
working for unsloth/models/sentence_transformer.py.
- tests/version_compat/test_bitsandbytes_pinned_symbols.py
4 bnb tags (0.45.5 -> 0.49.2 + main; skip the broken 0.46.0 /
0.48.0 listed in pyproject !=). Asserts:
* bnb.functional.{dequantize_4bit, quantize_4bit}
* bnb.nn.{Linear4bit, Params4bit}
- .github/workflows/version-compat-ci.yml
7 jobs:
* vllm-pinned-symbols (existing tests/vllm_compat/, now wired)
* trl-grpo-pinned-symbols
* peft-pinned-symbols
* st-pinned-symbols
* bitsandbytes-pinned-symbols
* zoo-imports-under-spoof (real pip install + CUDA spoof,
unsloth_zoo.{rl_replacements, empty_model, vllm_utils,
vllm_lora_*} import smoke)
* daily-fresh-fetch (cron-only superset)
Triggers: pull_request (paths), daily 06:43 UTC, workflow_dispatch.
Authenticated GitHub raw fetches (GITHUB_TOKEN) for the 5000 req/h
quota.
Smoke-tested locally: 226 pass, 15 skipped (gated optional features).
237 lines
9.1 KiB
YAML
237 lines
9.1 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:
|
|
paths:
|
|
- 'unsloth/models/rl.py'
|
|
- 'unsloth/models/rl_replacements.py'
|
|
- 'unsloth/models/sentence_transformer.py'
|
|
- '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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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 \
|
|
-v --tb=short
|
|
|
|
st-pinned-symbols:
|
|
name: sentence-transformers pinned-symbol matrix
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 8
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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
|
|
|
|
# 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
with: { path: unsloth }
|
|
- name: Clone unsloth-zoo @ main
|
|
run: |
|
|
git clone --depth=1 https://github.com/unslothai/unsloth-zoo \
|
|
"$RUNNER_TEMP/unsloth-zoo"
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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 \
|
|
'torch>=2.4,<2.11' 'torchvision<0.26'
|
|
# 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
|
|
PYTHONPATH=. python -m pytest \
|
|
tests/vllm_compat/test_unsloth_zoo_imports.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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.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
|