diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2802f461b6..a9694036cf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,10 +6,10 @@ /unsloth/models/rl_replacements.py @Datta0 @pluesclues @danielhanchen /unsloth/trainer.py @danielhanchen /unsloth/models/sentence_transformer.py @Etherll @danielhanchen -/unsloth/save.py @rolandtannous @danielhanchen +/unsloth/save.py @danielhanchen /unsloth/tokenizer_utils.py @mmathew23 @danielhanchen -/unsloth/chat_templates.py @rolandtannous @danielhanchen -/unsloth/ollama_template_mappers.py @rolandtannous @danielhanchen +/unsloth/chat_templates.py @danielhanchen +/unsloth/ollama_template_mappers.py @danielhanchen /unsloth/kernels/moe/*.py @Datta0 /unsloth/import_fixes.py @danielhanchen /unsloth/device_type.py @danielhanchen @@ -45,14 +45,14 @@ /unsloth/utils/hf_hub.py @mmathew23 /unsloth/utils/packing.py @mmathew23 -/cli/ @rolandtannous @Manan17 -/studio/frontend/ @Shine1i @rolandtannous @Manan17 +/cli/ @Manan17 +/studio/frontend/ @Shine1i @Manan17 /studio/frontend/public/ @Shine1i -/studio/backend/ @rolandtannous -/studio/backend/core/data_recipe/ @rolandtannous -/studio/backend/tests/ @rolandtannous @danielhanchen -/tests/ @rolandtannous @danielhanchen -/scripts/ @rolandtannous @danielhanchen +/studio/backend/ +/studio/backend/core/data_recipe/ +/studio/backend/tests/ @danielhanchen +/tests/ @danielhanchen +/scripts/ @danielhanchen # Snapshot data for the notebook linter / Colab oracle. Drift in these # files changes the pin floor for every Unsloth notebook, so refreshes diff --git a/.github/workflows/consolidated-tests-ci.yml b/.github/workflows/consolidated-tests-ci.yml index c6c2e1fc37..f7c338d76b 100644 --- a/.github/workflows/consolidated-tests-ci.yml +++ b/.github/workflows/consolidated-tests-ci.yml @@ -333,6 +333,17 @@ jobs: run: | python -m pytest -v --tb=short tests/test_callback_signature_drift.py + - name: generation correctness guards (HARD GATE) + # Deterministic CPU guards, each validated to fail on its pre-fix code: + # leftpad = batched left-padded generation (#1066/#3699, fixed by + # #2216 + #4100; staging proof: unsloth-staging-2 PRs 170/172); + # rope_scaling_drift = config.rope_scaling dropped by replaced rotary + # classes (#2405). AST checks run first so import breakage cannot mask them. + run: | + python -m pytest -v --tb=short \ + tests/utils/test_prepare_inputs_leftpad.py \ + tests/utils/test_rope_scaling_drift.py + - name: unsloth Bucket-A — CPU tests not in Repo tests (CPU) # CPU tests across 6 files under tests/saving/, tests/utils/, tests/python/ # that Repo tests (CPU) --ignores. AST/protobuf/regex plus tiny CPU model diff --git a/.github/workflows/lint-ci.yml b/.github/workflows/lint-ci.yml index 8cb3535075..bd859a6e9e 100644 --- a/.github/workflows/lint-ci.yml +++ b/.github/workflows/lint-ci.yml @@ -105,29 +105,37 @@ jobs: # exactly where a hoist refactor lives, and it skips brand-new # files whose re-export imports would otherwise look "unused". # - # actions/checkout uses fetch-depth: 1, so the base branch is not - # present locally. Fetch the single base commit with an explicit - # refspec so origin/ is reliably created (a bare - # `git fetch origin ` only updates FETCH_HEAD in some - # configs). Two-dot diff avoids needing a merge-base on a shallow - # clone. + # Diff against the true merge-base, not the base tip. A two-dot + # diff against the tip re-lints every file the base branch + # changed after the PR branched, comparing newer base code + # (BEFORE) against the PR's older snapshot (AFTER) - a + # time-reversed comparison that flags the base branch's own + # refactors as blockers on PRs that never touched those files. + # The compare API returns the merge-base without needing local + # history, and fetching that single commit by SHA keeps the + # shallow (fetch-depth: 1) clone. if: github.event_name == 'pull_request' + env: + GH_TOKEN: ${{ github.token }} run: | - git fetch --no-tags --depth=1 origin \ - "${{ github.base_ref }}:refs/remotes/origin/${{ github.base_ref }}" + MERGE_BASE=$(gh api \ + "repos/${{ github.repository }}/compare/${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" \ + --jq .merge_base_commit.sha) + git fetch --no-tags --depth=1 origin "$MERGE_BASE" mapfile -t CHANGED < <( git diff --name-only --diff-filter=M \ - "origin/${{ github.base_ref }}" HEAD -- '*.py' \ + "$MERGE_BASE" HEAD -- '*.py' \ | grep -vE '(^|/)(unsloth_compiled_cache|node_modules|build|dist)/' || true ) if [ "${#CHANGED[@]}" -eq 0 ]; then echo "no in-place-modified Python files to check" exit 0 fi + printf 'merge base: %s\n' "$MERGE_BASE" printf 'checking %d file(s):\n' "${#CHANGED[@]}" printf ' %s\n' "${CHANGED[@]}" python scripts/verify_import_hoist.py \ - --before "origin/${{ github.base_ref }}" --after HEAD "${CHANGED[@]}" + --before "$MERGE_BASE" --after HEAD "${CHANGED[@]}" - name: No leftover debugger / pdb / breakpoint calls # Catches the "I'll just stick a breakpoint() here" mistake diff --git a/.github/workflows/mlx-ci.yml b/.github/workflows/mlx-ci.yml index 75940832a0..221e86f235 100644 --- a/.github/workflows/mlx-ci.yml +++ b/.github/workflows/mlx-ci.yml @@ -231,33 +231,14 @@ jobs: tests/studio/test_is_mlx_dispatch_gate.py \ tests/studio/test_mlx_training_worker_behaviors.py - # Studio prebuilt llama.cpp install + GGUF inference. Drives the - # exact path Studio's setup.sh takes on macOS: invokes - # studio/install_llama_prebuilt.py with --published-repo - # ggml-org/llama.cpp and --published-release-tag b9049 (the - # latest llama.cpp release at the time this step was added; bump - # via UNSLOTH_LLAMA_TAG / DEFAULT_LLAMA_TAG when refreshing). - # The installer downloads llama-b9049-bin-macos-arm64.tar.gz, - # which is the universal Apple Silicon (arm64) build -- the - # same artifact works on M1/M2/M3/M4 because llama.cpp compiles - # against the ARMv8.2 baseline. - # - # The b9049 release also publishes: - # - llama-b9049-bin-macos-arm64-kleidiai.tar.gz - # KleidiAI dispatches at runtime; on M1 it falls back where - # ISA features (e.g. I8MM) are missing, so this asset also - # runs on M1 -- Studio just doesn't choose it by default. - # - llama-b9049-bin-macos-x64.tar.gz - # Intel-only; would only run on M1 via Rosetta 2 emulation, - # which we explicitly avoid. - # - iOS XCFramework - # iOS-app build artifact, unrelated to a macOS desktop CI. - # - # After install, downloads a small published GGUF - # (unsloth/gemma-3-270m-it-GGUF, Q4_K_M) from HuggingFace and - # runs the prebuilt llama-cli on it. Asserts the prompt echo - # appears in stdout. If the install fails OR the binary exits - # non-zero, that's an Unsloth/Studio bug. + # Studio prebuilt llama.cpp install + GGUF inference. Mirrors the + # path Studio's setup.sh takes on macOS since #5963: plan against + # the unslothai/llama.cpp fork's latest release, which ships the + # bin-macos-arm64 bundle plus the llama-prebuilt-manifest.json the + # default policy reads. After install, downloads a small published + # GGUF (unsloth/gemma-3-270m-it-GGUF, Q4_K_M) and validates + # llama-server /completion end to end. An install failure or a + # non-zero binary exit is an Unsloth/Studio bug. - name: Studio prebuilt llama.cpp install + GGUF inference (Mac M1) env: HF_TOKEN: ${{ secrets.HF_TOKEN }} @@ -272,20 +253,12 @@ jobs: set -euo pipefail INSTALL_DIR="$HOME/.unsloth-studio-prebuilt-test/llama.cpp" rm -rf "$INSTALL_DIR" - # --simple-policy is required when --published-repo points - # at upstream ggml-org/llama.cpp; that repo doesn't ship the - # llama-prebuilt-manifest.json asset Studio's default policy - # expects, so the simple platform-specific policy maps - # Darwin+arm64 -> bin-macos-arm64 directly. studio/setup.sh - # passes both --published-repo ggml-org/llama.cpp AND - # --simple-policy automatically on macOS, so this CI step - # exercises the same code path users hit when they run - # `curl -fsSL https://unsloth.ai/install.sh | sh`. + # Mirror studio/setup.sh on macOS (the install.sh user path): + # it plans against the unslothai/llama.cpp fork's latest + # release with no policy or tag flags. python studio/install_llama_prebuilt.py \ --install-dir "$INSTALL_DIR" \ - --published-repo ggml-org/llama.cpp \ - --published-release-tag b9049 \ - --simple-policy + --published-repo unslothai/llama.cpp # Studio bundles only llama-server + llama-quantize from the # prebuilt (not llama-cli) -- inference goes through diff --git a/.github/workflows/studio-api-smoke.yml b/.github/workflows/studio-api-smoke.yml index 53514e2ce1..b196805cf7 100644 --- a/.github/workflows/studio-api-smoke.yml +++ b/.github/workflows/studio-api-smoke.yml @@ -77,7 +77,7 @@ jobs: path: hf-cache # Same key as studio-ui-smoke.yml so the two jobs share a # single GGUF download across CI. - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -88,17 +88,19 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-frontend-ci.yml b/.github/workflows/studio-frontend-ci.yml index 1270a57ef6..b42086f191 100644 --- a/.github/workflows/studio-frontend-ci.yml +++ b/.github/workflows/studio-frontend-ci.yml @@ -17,6 +17,8 @@ on: - 'studio/frontend/**' - 'scripts/check_frontend_dep_removal.py' - 'tests/studio/test_frontend_dep_removal.py' + - 'scripts/sync_allow_scripts_pins.py' + - 'tests/studio/test_sync_allow_scripts_pins.py' - '.github/workflows/studio-frontend-ci.yml' push: branches: [main, pip] @@ -60,6 +62,19 @@ jobs: with: node-version: '22' + # node 22 bundles npm 10.x, which predates allowScripts. Move to the + # 11.x line and fail loudly if the gate is still missing, so the + # strict flag below can never silently degrade into a warning. + - name: Upgrade npm to 11.x (allowScripts enforcement) + working-directory: ${{ github.workspace }} + run: | + npm install -g npm@^11 --no-fund --no-audit + V=$(npm -v) + case "$V" in + 11.1[6-9].*|11.[2-9][0-9].*|1[2-9].*) echo "npm $V has allowScripts" ;; + *) echo "::error::npm $V lacks allowScripts (need >=11.16)"; exit 1 ;; + esac + # Run the structural lockfile scan BEFORE npm ci. A compromised # tarball runs its `prepare` / `postinstall` during `npm ci`, # so any catch has to fire upstream of that. The scanner is @@ -68,14 +83,23 @@ jobs: working-directory: ${{ github.workspace }} run: python3 scripts/lockfile_supply_chain_audit.py + # Dependency bumps strand the version-pinned allowScripts entries. + # The paired pre-commit hook auto-fixes PRs; this is the backstop. + - name: allowScripts pins must match the lockfile + working-directory: ${{ github.workspace }} + run: | + python3 tests/studio/test_sync_allow_scripts_pins.py + python3 scripts/sync_allow_scripts_pins.py --check + - name: Lockfile must agree with package.json (npm ci is strict) - # Lifecycle scripts (esbuild native-binary postinstall, etc.) are - # required for `vite build`. The pre-install lockfile structural - # audit (lockfile_supply_chain_audit.py) is the practical defence - # against the npm postinstall-dropper class -- it fires BEFORE any - # tarball runs, on the injection pattern itself rather than an - # advisory-DB lookup. - run: npm ci --no-fund --no-audit + # The vite 8 chain (rolldown, lightningcss, tailwind oxide) ships napi + # binaries with no install scripts. The only script-bearing deps are + # covered by `allowScripts` in package.json (npm >=11.16, default in + # npm 12). The pre-install lockfile audit above stays the first line + # of defence -- it fires before any tarball can run code. + # --strict-allow-scripts: any unreviewed install script hard-fails + # the job; the sync hook keeps the pins fresh after bumps. + run: npm ci --strict-allow-scripts --no-fund --no-audit - name: npm ci must not have modified the working tree working-directory: ${{ github.workspace }} diff --git a/.github/workflows/studio-inference-smoke.yml b/.github/workflows/studio-inference-smoke.yml index 3a4c76a2bb..cffb33f71d 100644 --- a/.github/workflows/studio-inference-smoke.yml +++ b/.github/workflows/studio-inference-smoke.yml @@ -20,7 +20,7 @@ # enable_tools / enabled_tools, and enable_thinking on/off. # # 3. JSON, images -# Qwen3-VL-2B-Instruct UD-IQ2_XXS (~570 MiB) + mmproj-F16 (~780 MiB). +# Qwen3-VL-2B-Instruct UD-Q4_K_XL (~1.1 GiB) + mmproj-F16 (~780 MiB). # response_format JSON-schema decoding and OpenAI image_url # (data URI) plus Anthropic source/base64 image inputs. # @@ -91,7 +91,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -102,17 +102,19 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -375,6 +377,7 @@ jobs: - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -792,8 +795,14 @@ jobs: timeout-minutes: 30 env: GGUF_REPO: unsloth/Qwen3-VL-2B-Instruct-GGUF - GGUF_VARIANT: UD-IQ2_XXS - GGUF_FILE: Qwen3-VL-2B-Instruct-UD-IQ2_XXS.gguf + # UD-Q4_K_XL, not UD-IQ2_XXS: at 2-bit the temp-0 answer to the JSON + # step's capital-of-France probe flips with the host's SIMD kernels + # (GitHub runners deterministically answered France while other CPUs + # answer Paris; seeds do not rescue it, 1/5 Paris at temp 0.7). The + # Q4 quant answered Paris 13/13 across temps and seeds on the same + # runners, so the hard Paris assertion below stays reliable. + GGUF_VARIANT: UD-Q4_K_XL + GGUF_FILE: Qwen3-VL-2B-Instruct-UD-Q4_K_XL.gguf MMPROJ_FILE: mmproj-F16.gguf STUDIO_PORT: '18890' HF_HOME: ${{ github.workspace }}/hf-cache @@ -823,7 +832,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v2 - name: Prime HF_HOME with the GGUF + mmproj id: prime-hf @@ -835,17 +844,19 @@ jobs: mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$MMPROJ_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} (model + mmproj) if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-mac-api-smoke.yml b/.github/workflows/studio-mac-api-smoke.yml index 8839b559fd..412726538c 100644 --- a/.github/workflows/studio-mac-api-smoke.yml +++ b/.github/workflows/studio-mac-api-smoke.yml @@ -62,7 +62,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -73,17 +73,19 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-mac-inference-smoke.yml b/.github/workflows/studio-mac-inference-smoke.yml index e08ac6ca68..c794a34acd 100644 --- a/.github/workflows/studio-mac-inference-smoke.yml +++ b/.github/workflows/studio-mac-inference-smoke.yml @@ -85,7 +85,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -96,6 +96,7 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf # Save partial caches on cancel/timeout -- hf download resumes by # content hash. `outcome != skipped` keeps cache-hit a no-op. @@ -104,11 +105,12 @@ jobs: uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -361,6 +363,7 @@ jobs: - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -749,6 +752,7 @@ jobs: - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-mac-install-matrix.yml b/.github/workflows/studio-mac-install-matrix.yml index 4e2722d1cd..da944d4b5c 100644 --- a/.github/workflows/studio-mac-install-matrix.yml +++ b/.github/workflows/studio-mac-install-matrix.yml @@ -63,6 +63,7 @@ jobs: - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-mac-ui-smoke.yml b/.github/workflows/studio-mac-ui-smoke.yml index 0176b0a168..4f9f94b534 100644 --- a/.github/workflows/studio-mac-ui-smoke.yml +++ b/.github/workflows/studio-mac-ui-smoke.yml @@ -62,7 +62,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -73,17 +73,19 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-mac-update-smoke.yml b/.github/workflows/studio-mac-update-smoke.yml index 1eca227096..f554a16415 100644 --- a/.github/workflows/studio-mac-update-smoke.yml +++ b/.github/workflows/studio-mac-update-smoke.yml @@ -62,6 +62,7 @@ jobs: - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -73,6 +74,7 @@ jobs: - name: First update should be a no-op (prebuilt already validated) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update.log @@ -91,6 +93,7 @@ jobs: - name: Second update must also be a no-op env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update2.log diff --git a/.github/workflows/studio-ui-smoke.yml b/.github/workflows/studio-ui-smoke.yml index 455fe4b7e1..de106e201f 100644 --- a/.github/workflows/studio-ui-smoke.yml +++ b/.github/workflows/studio-ui-smoke.yml @@ -76,7 +76,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -87,17 +87,19 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Install Studio (--local, --no-torch) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail diff --git a/.github/workflows/studio-update-smoke.yml b/.github/workflows/studio-update-smoke.yml index 057aeacbd4..307bb51972 100644 --- a/.github/workflows/studio-update-smoke.yml +++ b/.github/workflows/studio-update-smoke.yml @@ -71,6 +71,7 @@ jobs: # prebuilt path falls back to source build. env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | mkdir -p logs set -o pipefail @@ -85,6 +86,7 @@ jobs: # idempotency regressed. env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update.log @@ -107,6 +109,7 @@ jobs: # the first one. env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update2.log diff --git a/.github/workflows/studio-windows-api-smoke.yml b/.github/workflows/studio-windows-api-smoke.yml index eee61516c7..78efe918ac 100644 --- a/.github/workflows/studio-windows-api-smoke.yml +++ b/.github/workflows/studio-windows-api-smoke.yml @@ -69,7 +69,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -80,13 +80,14 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Pre-install Windows tweaks (npm 11 + Defender exclusions) shell: pwsh @@ -123,6 +124,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 captures Write-Host (Information stream) output; diff --git a/.github/workflows/studio-windows-inference-smoke.yml b/.github/workflows/studio-windows-inference-smoke.yml index 6148856016..a772a6d102 100644 --- a/.github/workflows/studio-windows-inference-smoke.yml +++ b/.github/workflows/studio-windows-inference-smoke.yml @@ -101,7 +101,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -114,6 +114,7 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME cache for ${{ env.GGUF_REPO }} # Only write a fresh cache entry when we actually rebuilt the @@ -123,7 +124,7 @@ jobs: uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Pre-install Windows tweaks (npm 11 + Defender exclusions) shell: pwsh @@ -160,6 +161,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 captures Write-Host (Information stream) output; @@ -504,6 +506,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 captures Write-Host (Information stream) output; @@ -879,7 +882,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v2 - name: Prime HF_HOME with the GGUF + mmproj id: prime-hf @@ -891,13 +894,14 @@ jobs: mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$MMPROJ_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME cache for ${{ env.GGUF_REPO }} (model + mmproj) if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-${{ env.MMPROJ_FILE }}-v2 - name: Pre-install Windows tweaks (npm 11 + Defender exclusions) shell: pwsh @@ -934,6 +938,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 captures Write-Host (Information stream) output; diff --git a/.github/workflows/studio-windows-ui-smoke.yml b/.github/workflows/studio-windows-ui-smoke.yml index e3c8642122..40d8e530cd 100644 --- a/.github/workflows/studio-windows-ui-smoke.yml +++ b/.github/workflows/studio-windows-ui-smoke.yml @@ -85,7 +85,7 @@ jobs: continue-on-error: true with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Prime HF_HOME with the GGUF id: prime-hf @@ -96,13 +96,14 @@ jobs: python -m pip install --upgrade huggingface_hub mkdir -p hf-cache bash .github/scripts/hf-download-with-retry.sh "$GGUF_REPO" "$GGUF_FILE" + bash .github/scripts/hf-download-with-retry.sh ggml-org/models tinyllamas/stories260K.gguf - name: Save HF_HOME for ${{ env.GGUF_REPO }} if: always() && steps.prime-hf.outcome == 'success' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: hf-cache - key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v1 + key: ${{ runner.os }}-hf-${{ env.GGUF_REPO }}-${{ env.GGUF_VARIANT }}-v2 - name: Pre-install Windows tweaks (npm 11 + Defender exclusions) shell: pwsh @@ -143,6 +144,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 redirects ALL PowerShell streams (stdout, stderr, diff --git a/.github/workflows/studio-windows-update-smoke.yml b/.github/workflows/studio-windows-update-smoke.yml index aa3e35f052..4a4806cfb1 100644 --- a/.github/workflows/studio-windows-update-smoke.yml +++ b/.github/workflows/studio-windows-update-smoke.yml @@ -133,6 +133,7 @@ jobs: shell: pwsh env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | New-Item -ItemType Directory -Force -Path logs | Out-Null # *>&1 captures Write-Host (Information stream) output; @@ -179,6 +180,7 @@ jobs: - name: First update should be a no-op (prebuilt already validated) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update.log @@ -197,6 +199,7 @@ jobs: - name: Second update must also be a no-op env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | set -o pipefail unsloth studio update --local 2>&1 | tee logs/update2.log diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 56d32c9312..cffbf73cd5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,3 +19,15 @@ repos: exclude: '(chat_templates|ollama_template_mappers|_auto_install|mapper)\.py$' additional_dependencies: - ruff==0.6.9 + # Re-pins allowScripts entries after dependency bumps. pre-commit.ci + # pushes the fix to PR branches, Dependabot's included, so stale pins + # heal without a human in the loop. + - id: sync-allow-scripts-pins + name: Sync allowScripts pins with the frontend lockfile + # `python