Merge branch 'main' into docker-blackwell-build
This commit is contained in:
commit
f34a4cd73d
992 changed files with 134130 additions and 30951 deletions
27
.github/workflows/consolidated-tests-ci.yml
vendored
27
.github/workflows/consolidated-tests-ci.yml
vendored
|
|
@ -269,7 +269,8 @@ jobs:
|
|||
tests/saving/test_patch_saving_none_tokenizer.py \
|
||||
tests/saving/test_fix_sentencepiece_gguf_robustness.py \
|
||||
tests/utils/test_attention_masks.py \
|
||||
tests/utils/test_trunc_normal_patch.py
|
||||
tests/utils/test_trunc_normal_patch.py \
|
||||
tests/python/test_fast_language_model_text_only.py
|
||||
python -m pytest --collect-only -q "$RUNNER_TEMP/unsloth-zoo/tests/"
|
||||
|
||||
- name: import_fixes drift detectors (18 tests, HARD GATE)
|
||||
|
|
@ -332,12 +333,21 @@ 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)
|
||||
# 16 tests across 5 files. They live inside tests/saving/ and
|
||||
# tests/utils/, both of which Repo tests (CPU) excludes via --ignore
|
||||
# because their sibling files need real GPUs / real HF weights.
|
||||
# The five files below are pure-Python + AST/protobuf/regex tests
|
||||
# that run cleanly on CPU. Env inherited from the job block.
|
||||
# CPU tests across 6 files under tests/saving/, tests/utils/, tests/python/
|
||||
# that Repo tests (CPU) --ignores. AST/protobuf/regex plus tiny CPU model
|
||||
# loads; run cleanly here (transformers/torch installed).
|
||||
run: |
|
||||
python -m pytest -q --tb=short \
|
||||
tests/saving/test_save_shell_injection.py \
|
||||
|
|
@ -345,11 +355,12 @@ jobs:
|
|||
tests/saving/test_fix_sentencepiece_gguf_robustness.py \
|
||||
tests/utils/test_attention_masks.py \
|
||||
tests/utils/test_trunc_normal_patch.py \
|
||||
tests/python/test_fast_language_model_text_only.py \
|
||||
--deselect 'tests/utils/test_attention_masks.py::test_run_attention_flash_varlen_receives_window_and_softcap'
|
||||
# The deselected test monkeypatches flash_attn_varlen_func, which is
|
||||
# only bound on the module when `flash_attn` is importable. flash_attn
|
||||
# requires CUDA + dev toolchain, which the CPU-only ubuntu-latest
|
||||
# runner does not have. The other 15 Bucket-A tests pass cleanly.
|
||||
# runner does not have. The other Bucket-A tests pass cleanly.
|
||||
|
||||
- name: unsloth_zoo @ ${{ env.UNSLOTH_ZOO_REF }} — full pytest (CPU)
|
||||
# 106 of 111 test_* in unsloth_zoo are CPU-only. The two CUDA-skip
|
||||
|
|
@ -990,8 +1001,10 @@ jobs:
|
|||
# First seen on transformers >=5,<6; each represents a slow
|
||||
# or recursive source-rewriter path the zoo can address.
|
||||
"beit": "TimeoutError: compile exceeds per-model budget",
|
||||
"deepseek_ocr2": "TimeoutError: compile exceeds per-model budget",
|
||||
"sam": "TimeoutError: compile exceeds per-model budget",
|
||||
"sam_hq": "TimeoutError: compile exceeds per-model budget",
|
||||
"deepseek_ocr2": "TimeoutError: compile exceeds per-model budget",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
58
.github/workflows/lint-ci.yml
vendored
58
.github/workflows/lint-ci.yml
vendored
|
|
@ -79,6 +79,64 @@ jobs:
|
|||
run: |
|
||||
ruff check unsloth unsloth_cli studio tests cli.py unsloth-cli.py
|
||||
|
||||
- name: Import-hoist verifier self-test
|
||||
# scripts/verify_import_hoist.py is a scope-aware (LEGB) AST
|
||||
# resolver that gates import-hoisting / alias-rename refactors
|
||||
# against two bugs ruff and pyflakes both miss:
|
||||
# 1. dangling alias -- `from a import b as _b` hoisted to
|
||||
# `from a import b` but a leftover `_b` reference now
|
||||
# resolves to nothing (or to some other module-level `_b`).
|
||||
# 2. rename clash -- `_b -> b` silently re-points at a
|
||||
# different object already named `b` in that scope.
|
||||
# This step runs the tool's 8 negative-control cases so a
|
||||
# regression in the verifier itself fails before we trust it on
|
||||
# a diff. Hermetic, stdlib-only, sub-second. Hard gate.
|
||||
run: |
|
||||
python scripts/verify_import_hoist.py --self-test
|
||||
|
||||
- name: Import-hoist / alias-rename safety (changed Python files)
|
||||
# Runs the verifier in compare mode on every in-place-modified
|
||||
# .py in the PR: parses each file BEFORE (base branch) and AFTER
|
||||
# (this diff), resolves every name load, and fails on a BLOCKER
|
||||
# (dangling alias / rename clash / re-pointed import). INFO
|
||||
# findings (a helper relocated to another file) do not fail.
|
||||
#
|
||||
# --diff-filter=M (in-place edits only) is deliberate: that is
|
||||
# exactly where a hoist refactor lives, and it skips brand-new
|
||||
# files whose re-export imports would otherwise look "unused".
|
||||
#
|
||||
# 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: |
|
||||
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 \
|
||||
"$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 "$MERGE_BASE" --after HEAD "${CHANGED[@]}"
|
||||
|
||||
- name: No leftover debugger / pdb / breakpoint calls
|
||||
# Catches the "I'll just stick a breakpoint() here" mistake
|
||||
# before it ships. AST-based so commented-out debugger
|
||||
|
|
|
|||
51
.github/workflows/mlx-ci.yml
vendored
51
.github/workflows/mlx-ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
10
.github/workflows/notebooks-ci.yml
vendored
10
.github/workflows/notebooks-ci.yml
vendored
|
|
@ -285,7 +285,15 @@ jobs:
|
|||
# The PR-time CI must validate the code in this PR; PyPI unsloth
|
||||
# may lag the in-repo CPU-torch fallback in unsloth/kernels/utils.py
|
||||
# (lines 162-170) that handles missing torch._C._cuda_getCurrentRawStream.
|
||||
pip install --no-deps unsloth_zoo
|
||||
# unsloth_zoo from git main mirrors every other CI (Core / MLX /
|
||||
# install.sh) so PR-time validation sees the same zoo HEAD.
|
||||
for attempt in 1 2 3; do
|
||||
if pip install --no-deps "unsloth_zoo @ git+https://github.com/unslothai/unsloth-zoo"; then
|
||||
break
|
||||
fi
|
||||
[ "$attempt" -eq 3 ] && { echo "::error::unsloth_zoo install failed after 3 attempts"; exit 1; }
|
||||
sleep $((5 * attempt))
|
||||
done
|
||||
pip install --no-deps -e ./unsloth
|
||||
|
||||
- name: Convert notebooks for AST scan
|
||||
|
|
|
|||
126
.github/workflows/security-audit.yml
vendored
126
.github/workflows/security-audit.yml
vendored
|
|
@ -72,6 +72,31 @@ concurrency:
|
|||
permissions:
|
||||
contents: read
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Network-resilience knobs, applied to every job/step. These add retries
|
||||
# and backoff ONLY; they do not relax a single integrity check. cargo
|
||||
# still resolves against Cargo.lock (--locked), pip still verifies the
|
||||
# wheels it downloads, npm still enforces package-lock integrity, the
|
||||
# harden-runner egress allowlists below are unchanged, and every action
|
||||
# stays SHA-pinned. The advisory-audit run on 2026-05-29 red-failed when
|
||||
# one crates.io tarball fetch hit "Recv failure: Connection reset by
|
||||
# peer" (curl 56); cargo's default of 3 retries over an HTTP/2-multiplexed
|
||||
# connection did not recover. The settings below make that class of
|
||||
# transient fault self-heal instead of failing the whole run.
|
||||
env:
|
||||
# pip: raise the built-in retry count and per-connection timeout.
|
||||
PIP_RETRIES: "10"
|
||||
PIP_DEFAULT_TIMEOUT: "60"
|
||||
# cargo: retry network ops and disable HTTP/2 multiplexing -- the
|
||||
# documented mitigation for the curl-56 connection resets above.
|
||||
CARGO_NET_RETRY: "10"
|
||||
CARGO_HTTP_MULTIPLEXING: "false"
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
|
||||
# npm: retry registry fetches with capped exponential backoff.
|
||||
NPM_CONFIG_FETCH_RETRIES: "5"
|
||||
NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "2000"
|
||||
NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "60000"
|
||||
|
||||
jobs:
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# Combined advisory-DB audit: pip-audit + npm audit + cargo audit
|
||||
|
|
@ -140,7 +165,7 @@ jobs:
|
|||
|
||||
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @ 2026-03-27
|
||||
|
||||
- uses: swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2.9.1
|
||||
- uses: swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
workspaces: studio/src-tauri -> target
|
||||
|
||||
|
|
@ -153,8 +178,23 @@ jobs:
|
|||
# crashes with a TOML parse error on that file.
|
||||
# npm audit is bundled with the node toolchain, no install.
|
||||
run: |
|
||||
python -m pip install --upgrade pip 'pip-audit>=2.7'
|
||||
cargo install --locked --version '^0.22' cargo-audit
|
||||
retry() { # retry <max-attempts> <command...> with exponential backoff
|
||||
local max="$1"; shift
|
||||
local n=1 delay=5
|
||||
until "$@"; do
|
||||
if [ "$n" -ge "$max" ]; then
|
||||
echo "::error::command failed after ${n} attempts: $*" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "attempt ${n}/${max} failed; retrying in ${delay}s: $*" >&2
|
||||
sleep "$delay"; n=$((n + 1)); delay=$((delay * 2))
|
||||
done
|
||||
}
|
||||
retry 5 python -m pip install --upgrade pip 'pip-audit>=2.7'
|
||||
# --locked keeps the resolved tree identical to Cargo.lock; the
|
||||
# CARGO_NET_* env above plus this outer loop survive transient
|
||||
# crates.io connection resets without weakening that guarantee.
|
||||
retry 5 cargo install --locked --version '^0.22' cargo-audit
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Python: pip-audit
|
||||
|
|
@ -330,32 +370,60 @@ jobs:
|
|||
# ─────────────────────────────────────────────────────────────
|
||||
# OSV-Scanner: cross-ecosystem advisory DB (PyPI + npm + cargo)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
- name: Download + verify OSV-Scanner
|
||||
# Split out from the scan below so binary integrity is a HARD gate:
|
||||
# a checksum mismatch (swapped release asset, the Trivy-style pivot
|
||||
# this workflow refuses) fails the job instead of being swallowed by
|
||||
# the scan step's continue-on-error. A download still failing after
|
||||
# retries is transient, so we skip the scan rather than red-fail.
|
||||
# SHA-256 verified BEFORE chmod +x / exec. Bump OSV_SHA256 in lockstep
|
||||
# with OSV_VERSION (value from the release's osv-scanner_SHA256SUMS).
|
||||
run: |
|
||||
set -euo pipefail
|
||||
OSV_VERSION="v2.0.2"
|
||||
OSV_SHA256="3abcfd7126c453a00421487e721b296e0cb68085bd431d6cef60872774170fc8"
|
||||
if ! curl --proto '=https' --tlsv1.2 -fsSL \
|
||||
--retry 5 --retry-delay 3 --retry-connrefused --retry-all-errors \
|
||||
-o /tmp/osv-scanner \
|
||||
"https://github.com/google/osv-scanner/releases/download/${OSV_VERSION}/osv-scanner_linux_amd64"; then
|
||||
echo "::warning::osv-scanner download failed after retries; skipping scan" >&2
|
||||
rm -f /tmp/osv-scanner
|
||||
exit 0 # transient availability: do not red-fail the job
|
||||
fi
|
||||
if ! echo "${OSV_SHA256} /tmp/osv-scanner" | sha256sum -c -; then
|
||||
echo "::error::osv-scanner checksum mismatch; refusing to execute" >&2
|
||||
rm -f /tmp/osv-scanner
|
||||
exit 1 # integrity failure: hard-fail
|
||||
fi
|
||||
chmod +x /tmp/osv-scanner
|
||||
/tmp/osv-scanner --version
|
||||
|
||||
- name: OSV-Scanner (PyPI + npm + cargo, cross-ecosystem advisories)
|
||||
# OSV's advisory feed is a superset of GitHub-Advisory + RustSec
|
||||
# + npm advisories; running it alongside the per-ecosystem audit
|
||||
# tools catches CVEs that haven't propagated to the per-ecosystem
|
||||
# DBs yet (e.g. langchain-core CVE-2025-68664 was on OSV before
|
||||
# GitHub Advisory). Single binary, one transitive resolver, all
|
||||
# three lockfile types in one pass. Non-blocking until baselines
|
||||
# close.
|
||||
# three lockfile types in one pass. Binary is checksum-verified in
|
||||
# the step above; only the advisory scan stays non-blocking until
|
||||
# baselines close.
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set +e
|
||||
# OSV-Scanner ships a raw binary (no tarball) in v2.x.
|
||||
curl -fsSL -o /tmp/osv-scanner \
|
||||
https://github.com/google/osv-scanner/releases/download/v2.0.2/osv-scanner_linux_amd64
|
||||
chmod +x /tmp/osv-scanner
|
||||
/tmp/osv-scanner --version
|
||||
/tmp/osv-scanner scan source \
|
||||
--lockfile=studio/frontend/package-lock.json \
|
||||
--lockfile=studio/src-tauri/Cargo.lock \
|
||||
--lockfile=requirements.txt:audit-reqs/unsloth-deps.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/studio.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/no-torch-runtime.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/overrides.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/extras.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/extras-no-deps.txt \
|
||||
--format=table 2>&1 | tee logs-osv-scanner.txt
|
||||
if [ ! -x /tmp/osv-scanner ]; then
|
||||
echo "osv-scanner unavailable this run; skipping scan" | tee logs-osv-scanner.txt
|
||||
else
|
||||
/tmp/osv-scanner scan source \
|
||||
--lockfile=studio/frontend/package-lock.json \
|
||||
--lockfile=studio/src-tauri/Cargo.lock \
|
||||
--lockfile=requirements.txt:audit-reqs/unsloth-deps.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/studio.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/no-torch-runtime.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/overrides.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/extras.txt \
|
||||
--lockfile=requirements.txt:audit-reqs/extras-no-deps.txt \
|
||||
--format=table 2>&1 | tee logs-osv-scanner.txt
|
||||
fi
|
||||
{
|
||||
echo "## OSV-Scanner (cross-ecosystem)"
|
||||
echo
|
||||
|
|
@ -1075,7 +1143,23 @@ jobs:
|
|||
# new-install-script gate below protects against, and we must
|
||||
# not run any third-party hook to set up the audit.
|
||||
working-directory: studio/frontend
|
||||
run: npm ci --ignore-scripts
|
||||
run: |
|
||||
retry() { # retry <max-attempts> <command...> with exponential backoff
|
||||
local max="$1"; shift
|
||||
local n=1 delay=5
|
||||
until "$@"; do
|
||||
if [ "$n" -ge "$max" ]; then
|
||||
echo "::error::command failed after ${n} attempts: $*" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "attempt ${n}/${max} failed; retrying in ${delay}s: $*" >&2
|
||||
sleep "$delay"; n=$((n + 1)); delay=$((delay * 2))
|
||||
done
|
||||
}
|
||||
# --ignore-scripts is mandatory here (no third-party hook runs);
|
||||
# the retry only re-attempts the registry fetch, it never relaxes
|
||||
# that flag or the package-lock integrity check npm ci enforces.
|
||||
retry 5 npm ci --ignore-scripts
|
||||
|
||||
- name: npm audit signatures (informational)
|
||||
# Surfaces unsigned / mis-signed packages from the npm
|
||||
|
|
|
|||
6
.github/workflows/studio-api-smoke.yml
vendored
6
.github/workflows/studio-api-smoke.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
17
.github/workflows/studio-backend-ci.yml
vendored
17
.github/workflows/studio-backend-ci.yml
vendored
|
|
@ -144,9 +144,19 @@ jobs:
|
|||
# versions ship a CPU build that imports cleanly on Linux.
|
||||
pip install 'bitsandbytes>=0.45'
|
||||
# unsloth.device_type imports unsloth_zoo.utils.Version at module
|
||||
# scope, so the conftest preload needs unsloth_zoo even though
|
||||
# it is an optional dep of unsloth.
|
||||
pip install 'unsloth_zoo>=2026.5.1'
|
||||
# scope, so the conftest preload needs unsloth_zoo. Pull from
|
||||
# git main so this job sees the same zoo HEAD as Core / MLX /
|
||||
# install.sh do (otherwise a fix on zoo main hides until release).
|
||||
# No --no-deps: matches prior `pip install 'unsloth_zoo>=2026.5.1'`
|
||||
# behaviour so triton etc. still come in for the Repo tests CPU
|
||||
# collection imports.
|
||||
for attempt in 1 2 3; do
|
||||
if pip install "unsloth_zoo @ git+https://github.com/unslothai/unsloth-zoo"; then
|
||||
break
|
||||
fi
|
||||
[ "$attempt" -eq 3 ] && { echo "::error::unsloth_zoo install failed after 3 attempts"; exit 1; }
|
||||
sleep $((5 * attempt))
|
||||
done
|
||||
pip install -e . --no-deps
|
||||
|
||||
- name: Repo tests (CPU, auto-discovered)
|
||||
|
|
@ -212,6 +222,7 @@ jobs:
|
|||
for s in \
|
||||
tests/sh/test_get_torch_index_url.sh \
|
||||
tests/sh/test_mac_intel_compat.sh \
|
||||
tests/sh/test_nvcc_meets_llama_minimum.sh \
|
||||
tests/sh/test_tauri_install_exit_order.sh \
|
||||
tests/sh/test_torch_constraint.sh; do
|
||||
echo "::group::$s"
|
||||
|
|
|
|||
38
.github/workflows/studio-frontend-ci.yml
vendored
38
.github/workflows/studio-frontend-ci.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
57
.github/workflows/studio-inference-smoke.yml
vendored
57
.github/workflows/studio-inference-smoke.yml
vendored
|
|
@ -20,7 +20,7 @@
|
|||
# enable_tools / enabled_tools, and enable_thinking on/off.
|
||||
#
|
||||
# 3. JSON, images
|
||||
# gemma-4-E2B-it UD-IQ3_XXS (~2.4 GiB) + mmproj-F16 (~986 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
|
||||
|
|
@ -296,6 +298,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: openai-anthropic-log
|
||||
|
|
@ -373,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
|
||||
|
|
@ -771,6 +776,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: tool-calling-log
|
||||
|
|
@ -787,9 +794,15 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
GGUF_REPO: unsloth/gemma-4-E2B-it-GGUF
|
||||
GGUF_VARIANT: UD-IQ3_XXS
|
||||
GGUF_FILE: gemma-4-E2B-it-UD-IQ3_XXS.gguf
|
||||
GGUF_REPO: unsloth/Qwen3-VL-2B-Instruct-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
|
||||
|
|
@ -819,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
|
||||
|
|
@ -831,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
|
||||
|
|
@ -884,13 +899,23 @@ jobs:
|
|||
-H 'content-type: application/json' \
|
||||
-d "{\"username\":\"unsloth\",\"password\":\"$NEW\"}" | jq -r .access_token)
|
||||
echo "API_KEY=$TOKEN" >> "$GITHUB_ENV"
|
||||
# Load the GGUF (mmproj is auto-detected via the HF repo
|
||||
# lookup, the cached file is pulled out of HF_HOME).
|
||||
curl -fs -X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 900 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}" \
|
||||
| jq '{status, display_name, is_vision}'
|
||||
# Retry: llama-server startup can race process teardown after a
|
||||
# failed attempt. Keep curl out of a pipe so HTTP failures are not
|
||||
# masked by jq.
|
||||
LOAD_OK=0
|
||||
for attempt in 1 2 3; do
|
||||
HTTP=$(curl -s -o /tmp/load.json -w '%{http_code}' \
|
||||
-X POST "http://127.0.0.1:${STUDIO_PORT}/api/inference/load" \
|
||||
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
|
||||
--max-time 900 \
|
||||
-d "{\"model_path\":\"$GGUF_REPO\",\"gguf_variant\":\"$GGUF_VARIANT\",\"is_lora\":false,\"max_seq_length\":2048}")
|
||||
if [ "$HTTP" = "200" ]; then LOAD_OK=1; break; fi
|
||||
echo "::warning::/api/inference/load attempt $attempt returned $HTTP; response:"
|
||||
cat /tmp/load.json || true
|
||||
sleep 10
|
||||
done
|
||||
[ "$LOAD_OK" = "1" ] || { echo "::error::/api/inference/load failed 3 attempts"; exit 22; }
|
||||
jq '{status, display_name, is_vision}' /tmp/load.json
|
||||
|
||||
- name: JSON schema decoding + image input
|
||||
env:
|
||||
|
|
@ -1043,6 +1068,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: json-images-log
|
||||
|
|
|
|||
15
.github/workflows/studio-mac-api-smoke.yml
vendored
15
.github/workflows/studio-mac-api-smoke.yml
vendored
|
|
@ -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,29 +73,26 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Install pyjwt for the JWT-expiry forge test
|
||||
run: pip install 'pyjwt>=2.6'
|
||||
|
|
|
|||
41
.github/workflows/studio-mac-inference-smoke.yml
vendored
41
.github/workflows/studio-mac-inference-smoke.yml
vendored
|
|
@ -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,23 +105,19 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Install OpenAI + Anthropic Python SDKs
|
||||
run: pip install 'openai>=1.50' 'anthropic>=0.40'
|
||||
|
|
@ -294,6 +291,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: openai-anthropic-log
|
||||
|
|
@ -364,18 +363,14 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Reset auth + boot Studio (API-only, default tool policy)
|
||||
# We deliberately use the API-only mode rather than
|
||||
|
|
@ -659,6 +654,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: tool-calling-log
|
||||
|
|
@ -755,18 +752,14 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Install OpenAI + Anthropic Python SDKs
|
||||
run: pip install 'openai>=1.50' 'anthropic>=0.40'
|
||||
|
|
@ -1040,6 +1033,8 @@ jobs:
|
|||
- name: Upload logs
|
||||
# Always upload so green runs are still reviewable.
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: json-images-log
|
||||
|
|
|
|||
81
.github/workflows/studio-mac-install-matrix.yml
vendored
Normal file
81
.github/workflows/studio-mac-install-matrix.yml
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
||||
|
||||
# Proves Studio's llama.cpp install loads on every supported macOS. The heavy
|
||||
# app smokes stay single-OS; this matrix covers the OS-version dimension cheaply
|
||||
# (install.sh + binary-load assert). Regression guard for the macOS-version
|
||||
# selection in studio/install_llama_prebuilt.py.
|
||||
|
||||
name: Mac Studio Install Matrix CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'studio/install_llama_prebuilt.py'
|
||||
- 'studio/setup.sh'
|
||||
- 'install.sh'
|
||||
- '.github/scripts/assert-llama-loads.sh'
|
||||
- '.github/workflows/studio-mac-install-matrix.yml'
|
||||
push:
|
||||
branches: [main, pip]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
install-load:
|
||||
name: Install + load (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 25
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-14 # Apple Silicon, macOS 14 Sonoma
|
||||
experimental: false
|
||||
- os: macos-15 # Apple Silicon, macOS 15 Sequoia
|
||||
experimental: false
|
||||
- os: macos-26 # Apple Silicon, macOS 26 Tahoe
|
||||
experimental: false
|
||||
- os: macos-15-intel # Intel x86_64, macOS 15 (informational)
|
||||
experimental: true
|
||||
- os: macos-26-intel # Intel x86_64, macOS 26 (last Intel macOS)
|
||||
experimental: true
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install Studio (--local, --no-torch)
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||
run: |
|
||||
mkdir -p logs
|
||||
set -o pipefail
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Upload install log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: mac-install-matrix-${{ matrix.os }}-log
|
||||
path: logs/install.log
|
||||
retention-days: 7
|
||||
15
.github/workflows/studio-mac-ui-smoke.yml
vendored
15
.github/workflows/studio-mac-ui-smoke.yml
vendored
|
|
@ -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,29 +73,26 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- name: Install Playwright + Chromium
|
||||
# No --with-deps on Mac: that flag installs Linux apt packages.
|
||||
|
|
|
|||
20
.github/workflows/studio-mac-update-smoke.yml
vendored
20
.github/workflows/studio-mac-update-smoke.yml
vendored
|
|
@ -62,30 +62,19 @@ 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
|
||||
bash install.sh --local --no-torch 2>&1 | tee logs/install.log
|
||||
|
||||
- name: Assert install.sh used the Mac llama.cpp prebuilt
|
||||
run: |
|
||||
# Mac install must take the prebuilt path. Source-build
|
||||
# fallback here is an Unsloth bug.
|
||||
if grep -q "falling back to source build" logs/install.log; then
|
||||
echo "::error::install.sh fell back to source-build llama.cpp on Mac. Studio must install the prebuilt llama-bNNNN-bin-macos-arm64 on Apple Silicon."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -qE "prebuilt installed and validated|prebuilt up to date and validated|bin-macos-arm64" logs/install.log; then
|
||||
echo "::error::no Mac prebuilt llama.cpp marker in install.log."
|
||||
grep -E "llama-prebuilt|llama.cpp" logs/install.log | tail -60
|
||||
exit 1
|
||||
fi
|
||||
echo "install.sh installed the Mac prebuilt llama.cpp"
|
||||
- name: Assert llama.cpp loads on this macOS
|
||||
run: bash .github/scripts/assert-llama-loads.sh
|
||||
|
||||
- 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
|
||||
|
|
@ -104,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
|
||||
|
|
|
|||
6
.github/workflows/studio-ui-smoke.yml
vendored
6
.github/workflows/studio-ui-smoke.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
3
.github/workflows/studio-update-smoke.yml
vendored
3
.github/workflows/studio-update-smoke.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# 2. Tool calling Tests
|
||||
# Qwen3.5-2B UD-Q4_K_XL (~890 MiB).
|
||||
# 3. JSON, images
|
||||
# gemma-4-E2B-it UD-Q4_K_XL + mmproj-F16 (~3.4 GiB total).
|
||||
# Qwen3-VL-2B-Instruct UD-IQ2_XXS + mmproj-F16 (~1.4 GiB total).
|
||||
# Within the 14 GB windows-latest SSD budget.
|
||||
|
||||
name: Windows Studio GGUF CI
|
||||
|
|
@ -65,6 +65,18 @@ jobs:
|
|||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Fast GPU-free gate: parse setup.ps1 and run the Resolve-CudaToolkit unit
|
||||
# test (deferred Windows CUDA Toolkit check) before the heavy GGUF smoke.
|
||||
- name: setup.ps1 unit test (Resolve-CudaToolkit)
|
||||
shell: pwsh
|
||||
run: |
|
||||
$errs = $null
|
||||
[void][System.Management.Automation.Language.Parser]::ParseFile(
|
||||
(Resolve-Path studio/setup.ps1).Path, [ref]$null, [ref]$errs)
|
||||
if ($errs) { $errs | ForEach-Object { $_.ToString() }; exit 1 }
|
||||
Write-Host "setup.ps1 parsed with no errors"
|
||||
pwsh -NoProfile -File tests/studio/test_resolve_cuda_toolkit.ps1
|
||||
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '22'
|
||||
|
|
@ -89,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
|
||||
|
|
@ -102,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
|
||||
|
|
@ -111,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
|
||||
|
|
@ -148,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;
|
||||
|
|
@ -360,6 +374,9 @@ jobs:
|
|||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
# A transient Windows DLL-init crash (0xC0000142) in this diagnostic
|
||||
# copy must not fail an otherwise-green job.
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
|
|
@ -373,6 +390,8 @@ jobs:
|
|||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: windows-openai-anthropic-log
|
||||
|
|
@ -487,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;
|
||||
|
|
@ -788,6 +808,9 @@ jobs:
|
|||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
# A transient Windows DLL-init crash (0xC0000142) in this diagnostic
|
||||
# copy must not fail an otherwise-green job.
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
|
|
@ -801,6 +824,8 @@ jobs:
|
|||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: windows-tool-calling-log
|
||||
|
|
@ -821,9 +846,9 @@ jobs:
|
|||
run:
|
||||
shell: bash
|
||||
env:
|
||||
GGUF_REPO: unsloth/gemma-4-E2B-it-GGUF
|
||||
GGUF_VARIANT: UD-Q4_K_XL
|
||||
GGUF_FILE: gemma-4-E2B-it-UD-Q4_K_XL.gguf
|
||||
GGUF_REPO: unsloth/Qwen3-VL-2B-Instruct-GGUF
|
||||
GGUF_VARIANT: UD-IQ2_XXS
|
||||
GGUF_FILE: Qwen3-VL-2B-Instruct-UD-IQ2_XXS.gguf
|
||||
MMPROJ_FILE: mmproj-F16.gguf
|
||||
STUDIO_PORT: '18899'
|
||||
HF_HOME: ${{ github.workspace }}/hf-cache
|
||||
|
|
@ -857,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
|
||||
|
|
@ -869,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
|
||||
|
|
@ -912,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;
|
||||
|
|
@ -1101,7 +1128,7 @@ jobs:
|
|||
)
|
||||
data_uri = f"data:image/png;base64,{PNG_64X64_RED_B64}"
|
||||
|
||||
# On Windows + the gemma-4-E2B mmproj, llama.cpp's vision
|
||||
# On Windows + the Qwen3-VL mmproj, llama.cpp's vision
|
||||
# path runs on CPU (no Metal involvement). The wrapper is
|
||||
# kept for resilience but the vision path is expected to
|
||||
# work on Windows; an exception here is a real regression.
|
||||
|
|
@ -1186,6 +1213,9 @@ jobs:
|
|||
|
||||
- name: Collect llama-server logs
|
||||
if: always()
|
||||
# A transient Windows DLL-init crash (0xC0000142) in this diagnostic
|
||||
# copy must not fail an otherwise-green job.
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
# Copy llama-server's own stdout/stderr (teed by Studio under
|
||||
# ~/.unsloth/studio/logs/llama-server/) into the workspace so
|
||||
|
|
@ -1199,6 +1229,8 @@ jobs:
|
|||
|
||||
- name: Upload logs
|
||||
if: always()
|
||||
# Diagnostic only: a transient artifact-service drop must not fail a green job.
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: windows-json-images-log
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue