diff --git a/install.sh b/install.sh index 7948170043..1659b57212 100755 --- a/install.sh +++ b/install.sh @@ -1180,6 +1180,15 @@ _find_no_torch_runtime() { fi } +_overlay_local_repos() { + substep "overlaying local repo (editable)..." + run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps + substep "overlaying unsloth-zoo from git main..." + run_install_cmd "overlay unsloth-zoo (git main)" uv pip install --python "$_VENV_PY" \ + --no-deps --reinstall-package unsloth-zoo \ + "unsloth-zoo @ git+https://github.com/unslothai/unsloth-zoo" +} + # ── AMD ROCm GPU detection helper ── # Returns 0 (true) if an actual AMD GPU is present, 1 (false) otherwise. # Checks rocminfo for gfx[1-9]* (excludes gfx000 CPU agent) and @@ -1195,6 +1204,19 @@ _has_amd_rocm_gpu() { return 1 } +# ── Intel XPU GPU detection helper ── +# Returns 0 (true) if a Linux x86_64 host exposes an Intel GPU via DRM sysfs. +_has_intel_xpu_gpu() { + _drm_root="${UNSLOTH_DRM_ROOT:-/sys/class/drm}" + for _vendor in "$_drm_root"/card*/device/vendor; do + [ -r "$_vendor" ] || continue + if grep -qi '0x8086' "$_vendor" 2>/dev/null; then + return 0 + fi + done + return 1 +} + # ── NVIDIA usable-GPU helper ── # Returns 0 (true) only if nvidia-smi is present AND actually lists a GPU. # Prevents AMD-only hosts with a stale nvidia-smi on PATH from being routed @@ -1211,6 +1233,29 @@ _has_usable_nvidia_gpu() { "$_nvsmi" -L 2>/dev/null | awk '/^GPU[[:space:]]+[0-9]+:/{found=1} END{exit !found}' } +_install_intel_xpu_stack() { + _venv_py="$1" + _package_name="$2" + _local_install="$3" + + # Required for the current Intel extras path because one preview wheel in the + # dependency set uses a filename uv rejects unless this check is disabled. + substep "installing Intel XPU PyTorch + Unsloth extras..." + if [ "$_local_install" = true ]; then + run_install_cmd "install Intel XPU stack (local)" \ + env UV_SKIP_WHEEL_FILENAME_CHECK=1 \ + uv pip install --python "$_venv_py" \ + --upgrade-package "$_package_name" \ + "${_package_name}[intel-gpu-torch290]>=2026.4.8" + else + run_install_cmd "install Intel XPU stack" \ + env UV_SKIP_WHEEL_FILENAME_CHECK=1 \ + uv pip install --python "$_venv_py" \ + --upgrade-package "$_package_name" \ + "${_package_name}[intel-gpu-torch290]>=2026.4.8" + fi +} + # ── Detect GPU and choose PyTorch index URL ── # Mirrors Get-TorchIndexUrl in install.ps1. # On CPU-only machines this returns the cpu index, avoiding the solver @@ -1242,6 +1287,9 @@ get_torch_index_url() { *) echo "$_base/cpu"; return ;; esac if ! _has_amd_rocm_gpu; then + if _has_intel_xpu_gpu; then + echo "$_base/xpu"; return + fi echo "$_base/cpu"; return fi # AMD GPU confirmed -- detect ROCm version @@ -1461,6 +1509,11 @@ case "$TORCH_INDEX_URL" in echo "" fi ;; + */xpu) + echo "" + echo " Intel GPU detected -- installing Intel XPU-enabled PyTorch and Unsloth extras" + echo "" + ;; */rocm*) echo "" if [ "$_amd_gpu_radeon" = true ]; then @@ -1497,12 +1550,7 @@ if [ "$_MIGRATED" = true ]; then "unsloth>=2026.4.8" unsloth-zoo fi if [ "$STUDIO_LOCAL_INSTALL" = true ]; then - substep "overlaying local repo (editable)..." - run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps - substep "overlaying unsloth-zoo from git main..." - run_install_cmd "overlay unsloth-zoo (git main)" uv pip install --python "$_VENV_PY" \ - --no-deps --reinstall-package unsloth-zoo \ - "unsloth-zoo @ git+https://github.com/unslothai/unsloth-zoo" + _overlay_local_repos fi # AMD ROCm: install bitsandbytes even in migrated environments so # existing ROCm installs gain the AMD bitsandbytes build without a @@ -1524,9 +1572,13 @@ if [ "$_MIGRATED" = true ]; then esac fi elif [ -n "$TORCH_INDEX_URL" ]; then + _skip_unsloth_stage2=false # Fresh: Step 1 - install torch from explicit index (skip when --no-torch or Intel Mac) if [ "$SKIP_TORCH" = true ]; then substep "skipping PyTorch (--no-torch or Intel Mac x86_64)." "$C_WARN" + elif [ "${TORCH_INDEX_URL%/xpu}" != "$TORCH_INDEX_URL" ]; then + _install_intel_xpu_stack "$_VENV_PY" "$PACKAGE_NAME" "$STUDIO_LOCAL_INSTALL" + _skip_unsloth_stage2=true elif [ "$_amd_gpu_radeon" = true ]; then _radeon_url=$(get_radeon_wheel_url) if [ -n "$_radeon_url" ]; then @@ -1655,40 +1707,33 @@ elif [ -n "$TORCH_INDEX_URL" ]; then esac fi # Fresh: Step 2 - install unsloth, preserving pre-installed torch - tauri_log "STEP" "Installing Unsloth" - substep "installing unsloth (this may take a few minutes)..." - if [ "$SKIP_TORCH" = true ]; then - # No-torch: install unsloth + unsloth-zoo with --no-deps, then - # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. - run_install_cmd "install unsloth (no-torch)" uv pip install --python "$_VENV_PY" --no-deps \ - --upgrade-package unsloth --upgrade-package unsloth-zoo \ - "unsloth>=2026.4.8" unsloth-zoo - _NO_TORCH_RT="$(_find_no_torch_runtime)" - if [ -n "$_NO_TORCH_RT" ]; then - run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT" - fi - if [ "$STUDIO_LOCAL_INSTALL" = true ]; then - substep "overlaying local repo (editable)..." - run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps - substep "overlaying unsloth-zoo from git main..." - run_install_cmd "overlay unsloth-zoo (git main)" uv pip install --python "$_VENV_PY" \ - --no-deps --reinstall-package unsloth-zoo \ - "unsloth-zoo @ git+https://github.com/unslothai/unsloth-zoo" + if [ "$_skip_unsloth_stage2" = false ]; then + substep "installing unsloth (this may take a few minutes)..." + if [ "$SKIP_TORCH" = true ]; then + # No-torch: install unsloth + unsloth-zoo with --no-deps, then + # runtime deps (typer, safetensors, transformers, etc.) with --no-deps. + run_install_cmd "install unsloth (no torch)" uv pip install --python "$_VENV_PY" \ + --no-deps --upgrade-package unsloth --upgrade-package unsloth-zoo \ + "unsloth>=2026.4.8" unsloth-zoo + _NO_TORCH_RT="$(_find_no_torch_runtime)" + if [ -n "$_NO_TORCH_RT" ]; then + run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT" + fi + if [ "$STUDIO_LOCAL_INSTALL" = true ]; then + _overlay_local_repos + fi + elif [ "$STUDIO_LOCAL_INSTALL" = true ]; then + run_install_cmd "install unsloth (local)" uv pip install --python "$_VENV_PY" \ + --upgrade-package unsloth "unsloth>=2026.4.8" unsloth-zoo + _overlay_local_repos + else + run_install_cmd "install unsloth" uv pip install --python "$_VENV_PY" \ + --upgrade-package unsloth "$PACKAGE_NAME" fi elif [ "$STUDIO_LOCAL_INSTALL" = true ]; then - run_install_cmd "install unsloth (local)" uv pip install --python "$_VENV_PY" \ - --upgrade-package unsloth "unsloth>=2026.4.8" unsloth-zoo - substep "overlaying local repo (editable)..." - run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps - substep "overlaying unsloth-zoo from git main..." - run_install_cmd "overlay unsloth-zoo (git main)" uv pip install --python "$_VENV_PY" \ - --no-deps --reinstall-package unsloth-zoo \ - "unsloth-zoo @ git+https://github.com/unslothai/unsloth-zoo" - else - run_install_cmd "install unsloth" uv pip install --python "$_VENV_PY" \ - --upgrade-package unsloth -- "$PACKAGE_NAME" + _overlay_local_repos fi - # AMD ROCm: repair torch if the unsloth/unsloth-zoo install pulled in + # CUDA torch from PyPI, overwriting the ROCm wheels installed in Step 1. if [ "$SKIP_TORCH" = false ]; then case "$TORCH_INDEX_URL" in diff --git a/tests/sh/test_get_torch_index_url.sh b/tests/sh/test_get_torch_index_url.sh index 7235873f53..69127b1481 100755 --- a/tests/sh/test_get_torch_index_url.sh +++ b/tests/sh/test_get_torch_index_url.sh @@ -15,6 +15,8 @@ _FAKE_SMI_DIR=$(mktemp -d) { sed -n '/^_has_amd_rocm_gpu()/,/^}/p' "$INSTALL_SH" echo "" + sed -n '/^_has_intel_xpu_gpu()/,/^}/p' "$INSTALL_SH" + echo "" sed -n '/^_has_usable_nvidia_gpu()/,/^}/p' "$INSTALL_SH" echo "" sed -n '/^get_torch_index_url()/,/^}/p' "$INSTALL_SH" @@ -95,10 +97,10 @@ run_func() { _mock_dir="$1" if [ "$_mock_dir" = "none" ]; then # Minimal PATH with only basic tools, no nvidia-smi anywhere - PATH="$_TOOLS_DIR" bash -c ". '$_FUNC_FILE'; get_torch_index_url" 2>/dev/null + UNSLOTH_DRM_ROOT="$_FAKE_SMI_DIR/no-drm" PATH="$_TOOLS_DIR" bash -c ". '$_FUNC_FILE'; get_torch_index_url" 2>/dev/null else # Put mock nvidia-smi dir first, then basic tools - PATH="$_mock_dir:$_TOOLS_DIR" bash -c ". '$_FUNC_FILE'; get_torch_index_url" 2>/dev/null + UNSLOTH_DRM_ROOT="$_FAKE_SMI_DIR/no-drm" PATH="$_mock_dir:$_TOOLS_DIR" bash -c ". '$_FUNC_FILE'; get_torch_index_url" 2>/dev/null fi } @@ -190,6 +192,14 @@ rm -rf "$_cuda_dir" "$_amd_dir" "$_combined_dir" _result=$(run_func "none") assert_eq "no GPU -> cpu" "https://download.pytorch.org/whl/cpu" "$_result" +# 13b) Intel DRM vendor id -> xpu +_intel_root=$(mktemp -d) +mkdir -p "$_intel_root/card0/device" +printf '0x8086\n' > "$_intel_root/card0/device/vendor" +_result=$(UNSLOTH_DRM_ROOT="$_intel_root" PATH="$_TOOLS_DIR" bash -c ". '$_FUNC_FILE'; get_torch_index_url" 2>/dev/null) +assert_eq "Intel DRM vendor -> xpu" "https://download.pytorch.org/whl/xpu" "$_result" +rm -rf "$_intel_root" + # 14) ROCm 6.1 (no nvidia-smi) -> rocm6.1 _dir=$(make_mock_amd_smi "6.1") _result=$(run_func "$_dir") @@ -218,7 +228,14 @@ rm -rf "$_dir" _dir=$(mktemp -d) cat > "$_dir/amd-smi" <<'MOCK' #!/bin/sh -echo "AMDSMI Tool: 25.0.1 | AMDSMI Library version: 25.0.1.0 | ROCm version: " +case "$1" in + list) + printf 'GPU: 0\n BDF: 0000:03:00.0\n NAME: gfx1100\n' + ;; + *) + echo "AMDSMI Tool: 25.0.1 | AMDSMI Library version: 25.0.1.0 | ROCm version: " + ;; +esac MOCK chmod +x "$_dir/amd-smi" _result=$(run_func "$_dir") @@ -229,7 +246,14 @@ rm -rf "$_dir" _dir=$(mktemp -d) cat > "$_dir/amd-smi" <<'MOCK' #!/bin/sh -echo "AMDSMI Tool: 25.0.1 | AMDSMI Library version: 25.0.1.0 | ROCm version: N/A" +case "$1" in + list) + printf 'GPU: 0\n BDF: 0000:03:00.0\n NAME: gfx1100\n' + ;; + *) + echo "AMDSMI Tool: 25.0.1 | AMDSMI Library version: 25.0.1.0 | ROCm version: N/A" + ;; +esac MOCK chmod +x "$_dir/amd-smi" _result=$(run_func "$_dir")