diff --git a/.github/workflows/bench.yml.disabled b/.github/workflows/bench.yml.disabled deleted file mode 100644 index f2d7e16e981..00000000000 --- a/.github/workflows/bench.yml.disabled +++ /dev/null @@ -1,304 +0,0 @@ -# TODO: there have been some issues with the workflow, so disabling for now -# https://github.com/ggml-org/llama.cpp/issues/7893 -# -# Benchmark -name: Benchmark - -on: - workflow_dispatch: - inputs: - gpu-series: - description: 'Azure GPU series to run with' - required: true - type: choice - options: - - Standard_NC4as_T4_v3 - - Standard_NC24ads_A100_v4 - - Standard_NC80adis_H100_v5 - sha: - description: 'Commit SHA1 to build' - required: false - type: string - duration: - description: 'Duration of the bench' - type: string - default: 10m - - push: - branches: - - master - paths: ['llama.cpp', 'ggml.c', 'ggml-backend.cpp', 'ggml-quants.c', '**/*.cu', 'tools/server/*.h*', 'tools/server/*.cpp'] - pull_request_target: - types: [opened, synchronize, reopened] - paths: ['llama.cpp', 'ggml.c', 'ggml-backend.cpp', 'ggml-quants.c', '**/*.cu', 'tools/server/*.h*', 'tools/server/*.cpp'] - schedule: - - cron: '04 2 * * *' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.run_id }}-${{ github.event.inputs.sha }} - cancel-in-progress: true - -jobs: - bench-server-baseline: - runs-on: Standard_NC4as_T4_v3 - env: - RUNNER_LABEL: Standard_NC4as_T4_v3 # FIXME Do not find a way to not duplicate it - N_USERS: 8 - DURATION: 10m - - strategy: - matrix: - model: [phi-2] - ftype: [q4_0, q8_0, f16] - include: - - model: phi-2 - ftype: q4_0 - pr_comment_enabled: "true" - - if: | - inputs.gpu-series == 'Standard_NC4as_T4_v3' - || github.event_name == 'pull_request_target' - steps: - - name: Clone - id: checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha || github.head_ref || github.ref_name }} - - - name: Install python env - id: pipenv - run: | - cd tools/server/bench - python3 -m venv venv - source venv/bin/activate - pip install -r requirements.txt - - - name: Prometheus - id: install_prometheus - run: | - wget --quiet https://github.com/prometheus/prometheus/releases/download/v2.51.0/prometheus-2.51.0.linux-amd64.tar.gz - tar xzf prometheus*.tar.gz --strip-components=1 - ./prometheus --config.file=tools/server/bench/prometheus.yml & - while ! nc -z localhost 9090; do - sleep 0.1 - done - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.21' - - - name: Install k6 and xk6-sse - id: k6_installation - run: | - cd tools/server/bench - go install go.k6.io/xk6/cmd/xk6@latest - xk6 build master \ - --with github.com/phymbert/xk6-sse - - - name: Build - id: cmake_build - run: | - set -eux - cmake -B build \ - -DGGML_NATIVE=OFF \ - -DLLAMA_BUILD_SERVER=ON \ - -DLLAMA_CUBLAS=ON \ - -DCUDAToolkit_ROOT=/usr/local/cuda \ - -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc \ - -DCMAKE_CUDA_ARCHITECTURES=75 \ - -DLLAMA_FATAL_WARNINGS=OFF \ - -DLLAMA_ALL_WARNINGS=OFF \ - -DCMAKE_BUILD_TYPE=Release; - cmake --build build --config Release -j $(nproc) --target llama-server - - - name: Download the dataset - id: download_dataset - run: | - cd tools/server/bench - wget --quiet https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json - - - name: Server bench - id: server_bench - env: - HEAD_REF: ${{ github.head_ref || github.ref_name }} - run: | - set -eux - - cd tools/server/bench - source venv/bin/activate - python bench.py \ - --runner-label ${{ env.RUNNER_LABEL }} \ - --name ${{ github.job }} \ - --branch $HEAD_REF \ - --commit ${{ github.event.inputs.sha || github.event.pull_request.head.sha || github.sha }} \ - --scenario script.js \ - --duration ${{ github.event.inputs.duration || env.DURATION }} \ - --hf-repo ggml-org/models \ - --hf-file ${{ matrix.model }}/ggml-model-${{ matrix.ftype }}.gguf \ - --model-path-prefix /models \ - --parallel ${{ env.N_USERS }} \ - -ngl 33 \ - --batch-size 2048 \ - --ubatch-size 256 \ - --ctx-size 16384 \ - --n-prompts 1000 \ - --max-prompt-tokens 1024 \ - --max-tokens 2048 - - cat results.github.env >> $GITHUB_ENV - - # Remove dataset as we do not want it in the artefact - rm ShareGPT_V3_unfiltered_cleaned_split.json - - - uses: actions/upload-artifact@v4 - with: - name: bench-server-${{ github.job }}-${{ env.RUNNER_LABEL }}-${{ matrix.model }}-${{ matrix.ftype }} - compression-level: 9 - path: | - tools/server/bench/*.jpg - tools/server/bench/*.json - tools/server/bench/*.log - - - name: Commit status - uses: Sibz/github-status-action@v1 - with: - authToken: ${{secrets.GITHUB_TOKEN}} - sha: ${{ inputs.sha || github.event.pull_request.head.sha || github.sha }} - context: bench-server-${{ github.job }}-${{ env.RUNNER_LABEL }}-${{ matrix.model }}-${{ matrix.ftype }} - description: | - ${{ env.BENCH_RESULTS }} - state: 'success' - - - name: Upload benchmark images - uses: devicons/public-upload-to-imgur@v2.2.2 - continue-on-error: true # Important as it looks unstable: 503 - id: imgur_step - with: - client_id: ${{secrets.IMGUR_CLIENT_ID}} - path: | - tools/server/bench/prompt_tokens_seconds.jpg - tools/server/bench/predicted_tokens_seconds.jpg - tools/server/bench/kv_cache_usage_ratio.jpg - tools/server/bench/requests_processing.jpg - - - name: Extract mermaid - id: set_mermaid - run: | - set -eux - - cd tools/server/bench - PROMPT_TOKENS_SECONDS=$(cat prompt_tokens_seconds.mermaid) - echo "PROMPT_TOKENS_SECONDS<> $GITHUB_ENV - echo "$PROMPT_TOKENS_SECONDS" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - PREDICTED_TOKENS_SECONDS=$(cat predicted_tokens_seconds.mermaid) - echo "PREDICTED_TOKENS_SECONDS<> $GITHUB_ENV - echo "$PREDICTED_TOKENS_SECONDS" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - KV_CACHE_USAGE_RATIO=$(cat kv_cache_usage_ratio.mermaid) - echo "KV_CACHE_USAGE_RATIO<> $GITHUB_ENV - echo "$KV_CACHE_USAGE_RATIO" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - REQUESTS_PROCESSING=$(cat requests_processing.mermaid) - echo "REQUESTS_PROCESSING<> $GITHUB_ENV - echo "$REQUESTS_PROCESSING" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - - name: Extract image url - id: extract_image_url - continue-on-error: true - run: | - set -eux - - echo "IMAGE_O=${{ fromJSON(steps.imgur_step.outputs.imgur_urls)[0] }}" >> $GITHUB_ENV - echo "IMAGE_1=${{ fromJSON(steps.imgur_step.outputs.imgur_urls)[1] }}" >> $GITHUB_ENV - echo "IMAGE_2=${{ fromJSON(steps.imgur_step.outputs.imgur_urls)[2] }}" >> $GITHUB_ENV - echo "IMAGE_3=${{ fromJSON(steps.imgur_step.outputs.imgur_urls)[3] }}" >> $GITHUB_ENV - - - name: Comment PR - uses: mshick/add-pr-comment@v2 - id: comment_pr - if: ${{ github.event.pull_request != '' && matrix.pr_comment_enabled == 'true' }} - with: - message-id: bench-server-${{ github.job }}-${{ env.RUNNER_LABEL }}-${{ matrix.model }}-${{ matrix.ftype }} - message: | -

- - 📈 **llama.cpp server** for _${{ github.job }}_ on _${{ env.RUNNER_LABEL }}_ for `${{ matrix.model }}`-`${{ matrix.ftype }}`: **${{ env.BENCH_ITERATIONS}} iterations** 🚀 - -

- -
- - Expand details for performance related PR only - - - Concurrent users: ${{ env.N_USERS }}, duration: ${{ github.event.inputs.duration || env.DURATION }} - - HTTP request : avg=${{ env.HTTP_REQ_DURATION_AVG }}ms p(95)=${{ env.HTTP_REQ_DURATION_P_95_ }}ms fails=${{ env.HTTP_REQ_FAILED_PASSES }}, finish reason: stop=${{ env.LLAMACPP_COMPLETIONS_STOP_RATE_PASSES }} truncated=${{ env.LLAMACPP_COMPLETIONS_TRUNCATED_RATE_PASSES }} - - Prompt processing (pp): avg=${{ env.LLAMACPP_PROMPT_PROCESSING_SECOND_AVG }}tk/s p(95)=${{ env.LLAMACPP_PROMPT_PROCESSING_SECOND_P_95_ }}tk/s - - Token generation (tg): avg=${{ env.LLAMACPP_TOKENS_SECOND_AVG }}tk/s p(95)=${{ env.LLAMACPP_TOKENS_SECOND_P_95_ }}tk/s - - ${{ env.BENCH_GRAPH_XLABEL }} - - -

- - prompt_tokens_seconds - -

- - More - - ```mermaid - ${{ env.PROMPT_TOKENS_SECONDS }} - ``` - -
- - predicted_tokens_seconds - -
- More - - ```mermaid - ${{ env.PREDICTED_TOKENS_SECONDS }} - ``` - -
- -

- -
- - Details - -

- - kv_cache_usage_ratio - -

- More - - ```mermaid - ${{ env.KV_CACHE_USAGE_RATIO }} - ``` - -
- - requests_processing - -
- More - - ```mermaid - ${{ env.REQUESTS_PROCESSING }} - ``` - -
- -

-
-
diff --git a/.github/workflows/unsloth-pr-set-lint.yml b/.github/workflows/unsloth-pr-set-lint.yml deleted file mode 100644 index 37a4a68d7d3..00000000000 --- a/.github/workflows/unsloth-pr-set-lint.yml +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth: lint pr-set.json" - -# Tripwire for edits to the mix-build PR set: validates the file the moment a -# commit touching it is pushed (the team edits it by pushing to master, so -# there is no PR gate to hook). A bad entry can never build -- the nightly's -# resolve job re-runs the same structural checks -- but this surfaces the -# mistake on the commit within seconds instead of failing the 3 AM build, and -# adds the one check resolve skips: that each pinned commit actually belongs -# to the PR it is listed under (a wrong paste would otherwise merge unreviewed -# code while the manifest blames the named PR). - -on: - push: - paths: - - scripts/unsloth/pr-set.json - pull_request: - paths: - - scripts/unsloth/pr-set.json - -permissions: - contents: read - -jobs: - lint: - name: Validate pr-set.json - runs-on: ubuntu-22.04 - env: - GH_TOKEN: ${{ github.token }} - steps: - - uses: actions/checkout@v6 - - run: | - set -euo pipefail - FILE=scripts/unsloth/pr-set.json - jq -e '.prs | type == "array" and all(.[]; type == "string")' "$FILE" >/dev/null \ - || { echo "::error file=$FILE::.prs must be an array of PR url strings" >&2; exit 1; } - URL_RE='^https://github\.com/ggml-org/llama\.cpp/pull/([0-9]+)/commits/([0-9a-f]{40})/?$' - fail=0 - for url in $(jq -r '.prs[]' "$FILE"); do - if ! [[ "$url" =~ $URL_RE ]]; then - echo "::error file=$FILE::malformed entry '$url' (expected https://github.com/ggml-org/llama.cpp/pull//commits/<40-hex-sha>)" - fail=1 - continue - fi - NUM="${BASH_REMATCH[1]}"; PIN="${BASH_REMATCH[2]}" - PR_JSON="$(gh api "repos/ggml-org/llama.cpp/pulls/${NUM}" --jq '{state: .state, commits: .commits, head: .head.sha}')" - STATE="$(jq -r .state <<<"$PR_JSON")" - COMMITS="$(jq -r .commits <<<"$PR_JSON")" - HEAD="$(jq -r .head <<<"$PR_JSON")" - [ "$STATE" = "open" ] || echo "::warning file=$FILE::PR #${NUM} is ${STATE}; the nightly will skip this entry" - # The commits listing is capped at 250 by the API; past that the - # membership check cannot be trusted, so skip it rather than - # false-fail a legitimate giant PR. - if [ "$COMMITS" -gt 250 ]; then - echo "::notice::PR #${NUM} has ${COMMITS} commits (over the API listing cap); skipping pin membership check" - elif ! gh api "repos/ggml-org/llama.cpp/pulls/${NUM}/commits" --paginate --jq '.[].sha' | grep -qx "$PIN"; then - echo "::error file=$FILE::pinned commit ${PIN} is not a commit of PR #${NUM}" - fail=1 - continue - fi - [ "$PIN" = "$HEAD" ] || echo "::notice::PR #${NUM} pin ${PIN} is behind its head ${HEAD}" - echo "OK: PR #${NUM} @ ${PIN} (${STATE})" - done - exit "$fail" diff --git a/.github/workflows/unsloth-prebuilt-cpu.yml b/.github/workflows/unsloth-prebuilt-cpu.yml deleted file mode 100644 index 88432a5d503..00000000000 --- a/.github/workflows/unsloth-prebuilt-cpu.yml +++ /dev/null @@ -1,278 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: CPU" - -# Reusable child of unsloth-prebuilt.yml. Builds the CPU-only bundles for -# Linux + Windows, each on x64 and arm64 (one matrix entry per arch). Every -# matrix entry uploads a single app-*.{tar.gz|zip} artifact for the parent's -# assemble step to pick up. -# -# The Linux build mirrors oobabooga/llama-cpp-binaries' build-wheels-cpu.yml -# (GGML_BACKEND_DL + GGML_CPU_ALL_VARIANTS + GGML_RPC, no GPU backend). The -# Windows build instead matches ggml-org/llama.cpp release.yml's windows-cpu -# job (clang/LLVM toolchain file + "Ninja Multi-Config" + OpenMP + BoringSSL, -# arm64 cross-compiled from x64) -- clang is upstream's proven CPU path, so we -# follow it rather than llama-cpp-binaries' MSVC recipe; MSVC stays only where -# it is mandatory (CUDA/nvcc). Adapted to this repo's conventions: -# app----cpu archives packaged straight from build/bin like -# the ROCm/macOS children (no embedded UNSLOTH_PREBUILT_INFO.json -- -# assemble_metadata.py derives the manifest entry from the filename), $ORIGIN -# RPATH on Linux. arm64 extends llama-cpp-binaries (x64-only) so the release -# covers the arm64 CPU hosts that previously fell back to ggml-org upstream. - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - -permissions: - contents: read - -jobs: - build-linux: - name: linux/${{ matrix.arch }} - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - - { arch: x64, runner: ubuntu-22.04 } - - { arch: arm64, runner: ubuntu-24.04-arm } - steps: - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: Install build dependencies - run: | - set -eux - sudo apt-get update - sudo apt-get install -y build-essential libssl-dev ninja-build - - # ggml-org's release.yml applies this GCC-14 workaround on every - # ubuntu-24.04 leg (our arm64 runner); the default toolchain there - # miscompiles ggml. Matches the gcc-14 step in our CUDA arm64 child. - - name: Toolchain workaround (GCC 14 on arm64) - if: matrix.arch == 'arm64' - run: | - set -eux - sudo apt-get install -y gcc-14 g++-14 - { - echo "CC=gcc-14" - echo "CXX=g++-14" - } >> "$GITHUB_ENV" - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: cpu-linux-${{ matrix.arch }} - variant: ccache - evict-old-files: 14d - - - name: Configure - working-directory: src - run: | - set -eux - # Build recipe mirrors llama-cpp-binaries' CPU wheel (backend-DL + - # all CPU variants + RPC, no GPU backend). RPATH=$ORIGIN so the - # bundle's sibling .so files resolve from the binary's own directory. - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DGGML_NATIVE=OFF \ - -DGGML_BACKEND_DL=ON \ - -DGGML_CPU_ALL_VARIANTS=ON \ - -DGGML_RPC=ON \ - -DLLAMA_BUILD_TESTS=OFF \ - -DLLAMA_BUILD_EXAMPLES=OFF \ - -DCMAKE_INSTALL_RPATH='$ORIGIN' \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - - - name: Build - working-directory: src - run: | - set -eux - # Backend modules (CPU variants, RPC) build as ggml dependencies, so - # the server/quantize targets pull them in. - cmake --build build --config Release -j "$(nproc)" \ - --target llama-server llama-quantize - strip build/bin/llama-server build/bin/llama-quantize || true - - # DiffusionGemma binaries (example targets present only in #24423 mix - # builds): best-effort, never fail the job. See the CUDA child for the - # rationale. The bundle tars all of build/bin, so anything produced here - # is shipped automatically. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - set -u - if [ ! -d examples/diffusion-gemma-server ]; then - echo "no DiffusionGemma sources in this tree; skipping" - exit 0 - fi - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON \ - || { echo "reconfigure for examples failed; skipping DiffusionGemma binaries"; exit 0; } - if cmake --build build --config Release -j "$(nproc)" \ - --target llama-diffusion-gemma-visual-server llama-diffusion-cli; then - strip build/bin/llama-diffusion-gemma-visual-server build/bin/llama-diffusion-cli || true - echo "built DiffusionGemma binaries" - else - echo "warning: DiffusionGemma binaries failed to build; bundle will omit them" - fi - exit 0 - - - name: Package bundle (tar.gz) - run: | - set -eux - ASSET="app-${{ inputs.tag }}-linux-${{ matrix.arch }}-cpu.tar.gz" - cp src/LICENSE src/build/bin/ - mkdir -p dist - (cd src/build/bin && tar -czf "${GITHUB_WORKSPACE}/dist/${ASSET}" .) - ls -la dist - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-linux-${{ matrix.arch }}-cpu - path: dist/app-${{ inputs.tag }}-linux-${{ matrix.arch }}-cpu.tar.gz - if-no-files-found: error - - build-windows: - name: windows/${{ matrix.arch }} - # Single x64 runner for both arches: arm64 is cross-compiled with the clang - # toolchain (vcvars amd64_arm64), exactly like ggml-org's release.yml - # windows-cpu job. CUDA stays on MSVC (mandatory for nvcc on Windows), but - # the CPU build uses clang/LLVM to match upstream's proven CPU recipe. - runs-on: windows-2025-vs2026 - strategy: - fail-fast: false - matrix: - include: - - { arch: x64, vcvars: x64, omp_arch: x86_64, cpu_variants: 'ON' } - - { arch: arm64, vcvars: amd64_arm64, omp_arch: aarch64, cpu_variants: 'OFF' } - defaults: - run: - shell: pwsh - steps: - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: cpu-windows-${{ matrix.arch }} - variant: ccache - evict-old-files: 14d - - - name: Install Ninja - run: choco install ninja --no-progress - - # Build recipe copied from ggml-org/llama.cpp release.yml (windows-cpu): - # clang via the per-arch LLVM toolchain file, "Ninja Multi-Config", OpenMP, - # BoringSSL, and GGML_CPU_ALL_VARIANTS only on x64 (it is an x86 microarch - # fan-out). vcvarsall sets the env (incl. the amd64_arm64 cross toolchain), - # so this runs in cmd. CMAKE_ARGS mirrors upstream's env.CMAKE_ARGS; we - # only narrow `--target` to the binaries we ship. - - name: Build - working-directory: src - shell: cmd - run: | - call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.vcvars }} - cmake -S . -B build -G "Ninja Multi-Config" ^ - -D CMAKE_TOOLCHAIN_FILE=cmake/${{ matrix.arch }}-windows-llvm.cmake ^ - -DLLAMA_BUILD_BORINGSSL=ON ^ - -DGGML_NATIVE=OFF ^ - -DGGML_BACKEND_DL=ON ^ - -DGGML_CPU_ALL_VARIANTS=${{ matrix.cpu_variants }} ^ - -DGGML_OPENMP=ON ^ - -DGGML_RPC=ON ^ - -DLLAMA_BUILD_TESTS=OFF ^ - -DLLAMA_BUILD_EXAMPLES=OFF ^ - -DLLAMA_BUILD_TOOLS=ON ^ - -DLLAMA_BUILD_SERVER=ON ^ - -DCMAKE_C_COMPILER_LAUNCHER=ccache ^ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - if errorlevel 1 exit /b 1 - cmake --build build --config Release --target llama-server llama-quantize - if errorlevel 1 exit /b 1 - - # DiffusionGemma binaries (#24423 mix builds only): best-effort, never fail - # the job (trailing `exit /b 0`). vcvarsall is re-called -- step env does - # not persist -- and the reconfigure reuses the cached clang toolchain. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - shell: cmd - run: | - if not exist examples\diffusion-gemma-server ( - echo no DiffusionGemma sources in this tree; skipping - exit /b 0 - ) - call "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.vcvars }} - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON - cmake --build build --config Release --target llama-diffusion-gemma-visual-server llama-diffusion-cli - exit /b 0 - - # Ninja Multi-Config emits to build/bin/Release. Ship the OpenMP runtime - # (GGML_OPENMP=ON) from the VS LLVM redist -- exactly as upstream does -- - # globbing the MSVC version dir so an image bump does not break the path. - - name: Package bundle (zip) - run: | - $rel = "src/build/bin/Release" - Copy-Item src/LICENSE $rel/ - $omp = Get-ChildItem "C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Redist\MSVC\*\debug_nonredist\${{ matrix.arch }}\Microsoft.VC*.OpenMP.LLVM\libomp140.${{ matrix.omp_arch }}.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 - if (-not $omp) { Write-Error "libomp140.${{ matrix.omp_arch }}.dll not found in the VS LLVM redist"; exit 1 } - Copy-Item $omp.FullName $rel/ - New-Item -ItemType Directory -Force -Path dist | Out-Null - $asset = "app-${{ inputs.tag }}-windows-${{ matrix.arch }}-cpu.zip" - Push-Location $rel - 7z a -tzip "$env:GITHUB_WORKSPACE/dist/$asset" . - Pop-Location - Get-ChildItem dist - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-windows-${{ matrix.arch }}-cpu - path: dist/app-${{ inputs.tag }}-windows-${{ matrix.arch }}-cpu.zip - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt-cuda-windows.yml b/.github/workflows/unsloth-prebuilt-cuda-windows.yml deleted file mode 100644 index e963bb8f89e..00000000000 --- a/.github/workflows/unsloth-prebuilt-cuda-windows.yml +++ /dev/null @@ -1,227 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: CUDA Windows" - -# Reusable child of unsloth-prebuilt.yml. Builds the per-profile CUDA Windows -# bundles the parent's `resolve` job filtered into `matrix`. Each entry uploads -# a single app-*.zip artifact for the parent's assemble step to pick up. -# -# Mirrors unsloth-prebuilt-cuda.yml (Linux), adapted for Windows: MSVC + Ninja -# toolchain, BoringSSL instead of system OpenSSL, no RPATH (Windows resolves -# DLLs from the executable's directory), and a .zip archive. CUDA compiles -# entirely in software here -- no GPU is present on the runner. - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - commit: - description: 'Upstream commit SHA for that tag, resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - matrix: - description: 'Matrix JSON {include:[...]} produced by the parent resolve job' - required: true - type: string - -permissions: - contents: read - -jobs: - build: - name: x64/${{ matrix.profile }} - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: ${{ fromJSON(inputs.matrix) }} - defaults: - run: - shell: pwsh - steps: - - name: Checkout build tooling (this repo) - uses: actions/checkout@v6 - with: - path: tooling - - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: cuda-${{ matrix.cuda }}-windows-${{ matrix.profile }} - variant: ccache - evict-old-files: 14d - - - uses: actions/setup-python@v6 - with: - python-version: "3.11" - - - name: Install Ninja - run: choco install ninja --no-progress - - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - # Jimver's action has no mapping for CUDA 13.3 yet, so for that version we - # fall back to llama.cpp's own install method: curl the individual NVIDIA - # redist component archives and assemble the toolkit by hand. Block copied - # verbatim from ggml-org/llama.cpp .github/actions/windows-setup-cuda - # (the cuda_version == '13.3' case). Any other version uses Jimver. - - name: Install CUDA toolkit 13.3 (NVIDIA redist) - if: matrix.cuda == '13.3' - run: | - mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" - choco install unzip -y - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_crt/windows-x86_64/cuda_crt-windows-x86_64-13.3.33-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-13.3.29-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-13.3.33-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-13.3.33-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-x86_64/libcublas-windows-x86_64-13.5.1.27-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libnvvm/windows-x86_64/libnvvm-windows-x86_64-13.3.33-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-13.3.29-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_profiler_api/windows-x86_64/cuda_profiler_api-windows-x86_64-13.3.27-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-13.3.27-archive.zip" - curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cccl/windows-x86_64/cccl-windows-x86_64-13.3.3.3.1-archive.zip" - unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_crt-windows-x86_64-13.3.33-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_cudart-windows-x86_64-13.3.29-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_nvcc-windows-x86_64-13.3.33-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_nvrtc-windows-x86_64-13.3.33-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\libcublas-windows-x86_64-13.5.1.27-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\libnvvm-windows-x86_64-13.3.33-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_nvtx-windows-x86_64-13.3.29-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cuda_profiler_api-windows-x86_64-13.3.27-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\visual_studio_integration-windows-x86_64-13.3.27-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\cccl-windows-x86_64-13.3.3.3.1-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" /E /I /H /Y - echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 - echo "CUDA_PATH_V13_3=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.3" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 - - - name: Install CUDA toolkit ${{ matrix.cuda }} (Jimver) - if: matrix.cuda != '13.3' - uses: Jimver/cuda-toolkit@v0.2.35 - id: cuda-toolkit - with: - cuda: ${{ matrix.cuda }} - method: 'network' - - - name: Set up CUDA environment (Jimver) - if: matrix.cuda != '13.3' - run: | - echo "CUDA_PATH=$env:CUDA_PATH" >> $env:GITHUB_ENV - echo "CUDA_HOME=$env:CUDA_PATH" >> $env:GITHUB_ENV - - - name: Verify CUDA - run: nvcc --version - - - name: Configure - working-directory: src - run: | - # CMAKE_CUDA_ARCHITECTURES is the explicit per-profile arch list (the - # whole point of the matrix). ccache launchers cache nvcc + cl.exe; - # no RPATH knobs -- Windows loads sibling DLLs from the binary's dir. - $archs = "${{ matrix.archs }}".Replace(' ', ';') - cmake -S . -B build -G Ninja ` - -DCMAKE_BUILD_TYPE=Release ` - -DGGML_NATIVE=OFF ` - -DGGML_BACKEND_DL=ON ` - -DGGML_CPU_ALL_VARIANTS=ON ` - -DGGML_RPC=ON ` - -DGGML_CUDA=ON ` - -DGGML_CUDA_CUB_3DOT2=ON ` - -DLLAMA_BUILD_TESTS=OFF ` - -DLLAMA_BUILD_EXAMPLES=OFF ` - -DLLAMA_BUILD_BORINGSSL=ON ` - -DCMAKE_CUDA_ARCHITECTURES="$archs" ` - -DCMAKE_C_COMPILER_LAUNCHER=ccache ` - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ` - -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache - - - name: Build - working-directory: src - run: | - # -j 3: this multi-arch nvcc build peaks at ~3 GB host RSS on the - # 4 vCPU / 16 GB GitHub-hosted runners (measured over a 1s sampler), - # so 3 parallel TUs leave ~13 GB free. -j 4 adds no speed (the runner - # is ~2 physical cores + HT), so 3 is the sweet spot. - cmake --build build --config Release -j 3 ` - --target llama-server llama-quantize - - # DiffusionGemma binaries (example targets present only in #24423 mix - # builds): best-effort, never fail the job. See the Linux CUDA child for - # the rationale. package_bundle.py ships the .exe only if it was produced. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - if (-not (Test-Path "examples/diffusion-gemma-server")) { - Write-Host "no DiffusionGemma sources in this tree; skipping" - exit 0 - } - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON - if ($LASTEXITCODE -ne 0) { - Write-Host "reconfigure for examples failed; skipping DiffusionGemma binaries" - exit 0 - } - cmake --build build --config Release -j 3 ` - --target llama-diffusion-gemma-visual-server llama-diffusion-cli - if ($LASTEXITCODE -ne 0) { - Write-Host "warning: DiffusionGemma binaries failed to build; bundle will omit them" - } else { - Write-Host "built DiffusionGemma binaries" - } - exit 0 - - - name: Package bundle - env: - PLATFORM: windows - ARCH: x64 - BIN_DIR: ${{ github.workspace }}/src/build/bin - SRC_DIR: ${{ github.workspace }}/src - OUT_DIR: ${{ github.workspace }}/dist - TAG: ${{ inputs.tag }} - SOURCE_COMMIT: ${{ inputs.commit }} - SOURCE_REPO: ${{ inputs.repo }} - SOURCE_REF_KIND: ${{ inputs.repo == 'ggml-org/llama.cpp' && 'tag' || 'mix' }} - PROFILE: ${{ matrix.profile }} - LINE: ${{ matrix.line }} - KLASS: ${{ matrix.klass }} - RANK: ${{ matrix.rank }} - TOOLKIT_LINE: ${{ matrix.toolkit_line }} - DOCKER_IMAGE: github-hosted ${{ matrix.runner }}, CUDA ${{ matrix.cuda }} ${{ matrix.cuda == '13.3' && '(NVIDIA redist)' || '(Jimver)' }} - ARCHS: ${{ matrix.archs }} - run: python tooling/scripts/unsloth/package_bundle.py - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-windows-x64-${{ matrix.profile }} - path: dist/app-${{ inputs.tag }}-windows-x64-${{ matrix.profile }}.zip - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt-cuda.yml b/.github/workflows/unsloth-prebuilt-cuda.yml deleted file mode 100644 index 54ee4c333a7..00000000000 --- a/.github/workflows/unsloth-prebuilt-cuda.yml +++ /dev/null @@ -1,250 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: CUDA" - -# Reusable child of unsloth-prebuilt.yml. Builds the per-profile CUDA bundles -# the parent's `resolve` job filtered into `matrix`. Each entry uploads a -# single app-*.tar.gz artifact for the parent's assemble step to pick up. - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - commit: - description: 'Upstream commit SHA for that tag, resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - matrix: - description: 'Matrix JSON {include:[...]} produced by the parent resolve job' - required: true - type: string - -permissions: - contents: read - -jobs: - build: - name: ${{ matrix.arch }}/${{ matrix.profile }} - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: ${{ fromJSON(inputs.matrix) }} - steps: - - name: Free disk space - uses: jlumbroso/free-disk-space@v1.3.1 - with: - tool-cache: true - android: true - dotnet: true - haskell: true - large-packages: false - swap-storage: true - - - name: Checkout build tooling (this repo) - uses: actions/checkout@v6 - with: - path: tooling - - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: Install build dependencies - run: | - set -eux - sudo apt-get update - sudo apt-get install -y build-essential libssl-dev - - - name: Install ARM64 host compiler (gcc-14) - if: matrix.arch == 'arm64' - run: | - set -eux - sudo apt-get install -y gcc-14 g++-14 - { - echo "CC=gcc-14" - echo "CXX=g++-14" - echo "CUDAHOSTCXX=g++-14" - } >> "$GITHUB_ENV" - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: cuda-${{ matrix.cuda }}-${{ matrix.arch }}-${{ matrix.profile }} - variant: ccache - evict-old-files: 14d - - # Jimver has no mapping for CUDA 13.3 yet, so for that version we pull the - # toolkit components straight from NVIDIA's redist CDN (same source Jimver - # and ggml-org use) and assemble a prefix by hand. Component versions are - # from NVIDIA's redistrib_13.3.0.json. Runs on the same ubuntu runner, so - # the glibc floor is unchanged. Any other version uses Jimver. - - name: Install CUDA toolkit 13.3 (NVIDIA redist) - if: matrix.cuda == '13.3' - run: | - set -eux - case "${{ matrix.arch }}" in - x64) PLAT=linux-x86_64 ;; - arm64) PLAT=linux-sbsa ;; - *) echo "unsupported arch ${{ matrix.arch }}" >&2; exit 1 ;; - esac - PREFIX="$HOME/cuda-13.3" - mkdir -p "$PREFIX" - BASE="https://developer.download.nvidia.com/compute/cuda/redist" - for cv in \ - cuda_crt:13.3.33 cuda_cudart:13.3.29 cuda_nvcc:13.3.33 \ - cuda_nvrtc:13.3.33 libcublas:13.5.1.27 libnvvm:13.3.33 \ - cuda_nvtx:13.3.29 cuda_profiler_api:13.3.27 cccl:13.3.3.3.1; do - comp="${cv%:*}"; ver="${cv#*:}" - name="${comp}-${PLAT}-${ver}-archive" - curl -fsSL -o comp.tar.xz "$BASE/${comp}/${PLAT}/${name}.tar.xz" - tar -xf comp.tar.xz - cp -a "${name}/." "$PREFIX"/ - rm -rf comp.tar.xz "${name}" - done - # Redist ships libs under lib/; some CMake CUDA discovery expects lib64. - ln -sfn lib "$PREFIX/lib64" - { - echo "CUDA_PATH=$PREFIX" - echo "CUDA_HOME=$PREFIX" - echo "LD_LIBRARY_PATH=$PREFIX/lib:${LD_LIBRARY_PATH:-}" - } >> "$GITHUB_ENV" - echo "$PREFIX/bin" >> "$GITHUB_PATH" - - - name: Install CUDA toolkit ${{ matrix.cuda }} (Jimver) - if: matrix.cuda != '13.3' - uses: Jimver/cuda-toolkit@v0.2.35 - id: cuda-toolkit - with: - cuda: ${{ matrix.cuda }} - method: 'network' - - - name: Set up CUDA environment (Jimver) - if: matrix.cuda != '13.3' - run: | - echo "CUDA_PATH=${{ steps.cuda-toolkit.outputs.CUDA_PATH }}" >> "$GITHUB_ENV" - echo "CUDA_HOME=${{ steps.cuda-toolkit.outputs.CUDA_PATH }}" >> "$GITHUB_ENV" - echo "LD_LIBRARY_PATH=${{ steps.cuda-toolkit.outputs.CUDA_PATH }}/lib64:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV" - - - name: Verify CUDA - run: nvcc --version - - - name: Configure - working-directory: src - run: | - set -eux - # Three non-obvious flags: - # - CMAKE_INSTALL_RPATH=$ORIGIN + RPATH knobs: the tar.gz layout - # ships sibling .so files in the same directory as the binaries. - # - CMAKE_CUDA_ARCHITECTURES: explicit per-profile arch list, the - # whole point of the matrix. - # - CMAKE_*_COMPILER_LAUNCHER=ccache: Jimver installs nvcc outside - # /usr/local/bin, so PATH-symlinked ccache misses CUDA TUs -- - # setting the launcher explicitly caches nvcc too. - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DGGML_NATIVE=OFF \ - -DGGML_BACKEND_DL=ON \ - -DGGML_CPU_ALL_VARIANTS=ON \ - -DGGML_RPC=ON \ - -DGGML_CUDA=ON \ - -DGGML_CUDA_CUB_3DOT2=ON \ - -DLLAMA_BUILD_TESTS=OFF \ - -DLLAMA_BUILD_EXAMPLES=OFF \ - -DCMAKE_CUDA_ARCHITECTURES="$(echo '${{ matrix.archs }}' | tr ' ' ';')" \ - -DCMAKE_INSTALL_RPATH='$ORIGIN' \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache - - - name: Build - working-directory: src - run: | - set -eux - # Backend modules (CPU variants, CUDA, RPC) build as ggml dependencies, - # so the server/quantize targets pull them in. - # -j 3: this multi-arch nvcc build peaks at ~3 GB host RSS on the - # 4 vCPU / 16 GB GitHub-hosted runners (x64 and arm64 alike, measured - # over a 1s sampler), so 3 parallel TUs leave ~13 GB free. -j 4 adds - # no speed (the runner is ~2 physical cores + HT), so 3 is the sweet spot. - cmake --build build --config Release -j 3 \ - --target llama-server llama-quantize - strip build/bin/llama-server build/bin/llama-quantize || true - - # The DiffusionGemma visual server + cli are example targets that exist - # only when the source tree carries ggml-org/llama.cpp#24423 (a mix build). - # Build them best-effort and NEVER fail the job: the required server + - # quantize bundle above must publish even if these do not compile on a - # given toolchain. package_bundle.py ships them only if they were produced. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - set -u - if [ ! -d examples/diffusion-gemma-server ]; then - echo "no DiffusionGemma sources in this tree; skipping" - exit 0 - fi - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON \ - || { echo "reconfigure for examples failed; skipping DiffusionGemma binaries"; exit 0; } - if cmake --build build --config Release -j 3 \ - --target llama-diffusion-gemma-visual-server llama-diffusion-cli; then - strip build/bin/llama-diffusion-gemma-visual-server build/bin/llama-diffusion-cli || true - echo "built DiffusionGemma binaries" - else - echo "warning: DiffusionGemma binaries failed to build; bundle will omit them" - fi - exit 0 - - - name: Package bundle - env: - PLATFORM: linux - ARCH: ${{ matrix.arch }} - BIN_DIR: ${{ github.workspace }}/src/build/bin - SRC_DIR: ${{ github.workspace }}/src - OUT_DIR: ${{ github.workspace }}/dist - TAG: ${{ inputs.tag }} - SOURCE_COMMIT: ${{ inputs.commit }} - SOURCE_REPO: ${{ inputs.repo }} - SOURCE_REF_KIND: ${{ inputs.repo == 'ggml-org/llama.cpp' && 'tag' || 'mix' }} - PROFILE: ${{ matrix.profile }} - LINE: ${{ matrix.line }} - KLASS: ${{ matrix.klass }} - RANK: ${{ matrix.rank }} - TOOLKIT_LINE: ${{ matrix.toolkit_line }} - DOCKER_IMAGE: github-hosted ${{ matrix.runner }}, CUDA ${{ matrix.cuda }} ${{ matrix.cuda == '13.3' && '(NVIDIA redist)' || '(Jimver)' }} - ARCHS: ${{ matrix.archs }} - run: python3 tooling/scripts/unsloth/package_bundle.py - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-linux-${{ matrix.arch }}-${{ matrix.profile }} - path: dist/app-${{ inputs.tag }}-linux-${{ matrix.arch }}-${{ matrix.profile }}.tar.gz - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt-macos.yml b/.github/workflows/unsloth-prebuilt-macos.yml deleted file mode 100644 index 05e1ac73685..00000000000 --- a/.github/workflows/unsloth-prebuilt-macos.yml +++ /dev/null @@ -1,129 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: macOS" - -# Reusable child of unsloth-prebuilt.yml. Builds the macOS slices (arm64 Metal + -# x64 CPU) the parent's `resolve` job placed in `matrix`. Each entry uploads a -# single app-*.tar.gz artifact for the parent's assemble step to pick up. -# -# The only change over ggml-org's own macos build is an explicit -# -DCMAKE_OSX_DEPLOYMENT_TARGET per slice, so the binaries declare their load -# floor instead of inheriting the runner OS. Upstream stopped pinning this on -# arm64 (their macos-26 runner stamps minos=26, which fails to dyld-load on -# macOS < 26); owning the build is how we keep a loadable floor. - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - matrix: - description: 'Matrix JSON {include:[...]} produced by the parent resolve job' - required: true - type: string - -permissions: - contents: read - -jobs: - build: - name: ${{ matrix.build }} - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: ${{ fromJSON(inputs.matrix) }} - steps: - - name: Checkout build tooling (this repo) - uses: actions/checkout@v6 - with: - path: tooling - persist-credentials: false - - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: Build (deployment target ${{ matrix.deploy_target }}) - working-directory: src - run: | - set -euo pipefail - cmake -B build \ - ${{ matrix.defines }} \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.deploy_target }} \ - -DCMAKE_INSTALL_RPATH='@loader_path' \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DLLAMA_FATAL_WARNINGS=ON \ - -DLLAMA_BUILD_BORINGSSL=ON \ - -DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_TOOLS=ON \ - -DLLAMA_BUILD_SERVER=ON -DGGML_RPC=ON - cmake --build build --config Release -j "$(sysctl -n hw.logicalcpu)" - - - name: Load gate (minos <= ${{ matrix.deploy_target }}, arch, launch) - run: bash tooling/scripts/unsloth/assert_macho_minos.sh src/build/bin "${{ matrix.expect_arch }}" "${{ matrix.deploy_target }}" - - # DiffusionGemma binaries (example targets present only in #24423 mix - # builds): best-effort, never fail the job. Built after the load gate so - # they do not affect the required-binary minos check; the same cached - # deployment target applies. The bundle tars all of build/bin, so anything - # produced here is shipped automatically. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - set -u - if [ ! -d examples/diffusion-gemma-server ]; then - echo "no DiffusionGemma sources in this tree; skipping" - exit 0 - fi - cmake -B build -DLLAMA_BUILD_EXAMPLES=ON \ - || { echo "reconfigure for examples failed; skipping DiffusionGemma binaries"; exit 0; } - if cmake --build build --config Release -j "$(sysctl -n hw.logicalcpu)" \ - --target llama-diffusion-gemma-visual-server llama-diffusion-cli; then - echo "built DiffusionGemma binaries" - else - echo "warning: DiffusionGemma binaries failed to build; bundle will omit them" - fi - exit 0 - - - name: Package bundle - working-directory: src - run: | - set -euo pipefail - TAG="${{ inputs.tag }}" - cp LICENSE build/bin/ - mkdir -p "$GITHUB_WORKSPACE/dist" - # BSD tar (-s) rewrites the leading ./ to llama-/ so the archive - # unpacks into a named dir, matching upstream's own macos tarball layout. - tar -czf "$GITHUB_WORKSPACE/dist/llama-${TAG}-bin-macos-${{ matrix.build }}.tar.gz" \ - -s ",^\.,llama-${TAG}," -C build/bin . - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-macos-${{ matrix.build }} - path: dist/llama-${{ inputs.tag }}-bin-macos-${{ matrix.build }}.tar.gz - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt-rocm.yml b/.github/workflows/unsloth-prebuilt-rocm.yml deleted file mode 100644 index 7d94b977936..00000000000 --- a/.github/workflows/unsloth-prebuilt-rocm.yml +++ /dev/null @@ -1,729 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: ROCm" - -# Reusable child of unsloth-prebuilt.yml. Per-gfx-target ROCm bundles on -# Windows + Ubuntu. -# -# Huge thanks to the lemonade-sdk team -- this is adapted from their workflow: -# https://github.com/lemonade-sdk/llamacpp-rocm/blob/main/.github/workflows/build-llamacpp-rocm.yml -# -# Build mechanics taken verbatim from that file: TheRock S3 nightly download -# + version auto-detect, gfx target name mapping, HIP/clang cmake invocation, -# the full hardcoded ROCm runtime lib copy list, patchelf RPATH. -# -# Local adaptations: -# - tag is passed in (the parent resolves it against ggml-org upstream) -# instead of lemonade's auto-incrementing b1000+ scheme. -# - artifacts pre-packaged into app---x64-rocm-.{tar.gz|zip} -# so the parent assemble step picks them up via the same merge-multiple -# download pattern it uses for CUDA bundles. -# - test jobs (stx-halo, stx) dropped -- those need self-hosted AMD runners. -# - build-summary + post-build cleanup steps dropped (clutter). - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - matrix: - description: 'gfx_target matrix JSON ({"gfx_target":[...]}), built by parent' - required: true - type: string - operating_systems: - description: 'OSes to build for (comma-separated: windows,ubuntu)' - required: false - default: 'windows,ubuntu' - type: string - rocm_version: - description: 'TheRock ROCm version (e.g., 7.13.0a20260318) or "latest"' - required: false - default: 'latest' - type: string - -permissions: - contents: read - -jobs: - build-windows: - name: windows/${{ matrix.gfx_target }} - runs-on: windows-latest - if: contains(inputs.operating_systems, 'windows') - strategy: - matrix: ${{ fromJson(inputs.matrix) }} - fail-fast: false - - steps: - - name: Clean up existing directories (safety precaution) - run: | - # Remove existing llama.cpp directory if it exists - if (Test-Path "llama.cpp") { - Write-Host "Removing existing llama.cpp directory..." - Remove-Item -Recurse -Force "llama.cpp" - } - - # Remove existing C:\opt\rocm directory if it exists - if (Test-Path "C:\opt\rocm") { - Write-Host "Removing existing C:\opt\rocm directory..." - Remove-Item -Recurse -Force "C:\opt\rocm" - } - - # Remove any existing ROCm tarball - if (Test-Path "rocm.tar.gz") { - Write-Host "Removing existing rocm.tar.gz..." - Remove-Item -Force "rocm.tar.gz" - } - - Write-Host "Cleanup completed successfully" - - - name: Install Visual Studio Build Tools - run: | - # Download and install Visual Studio Build Tools - $vsInstallerUrl = "https://aka.ms/vs/17/release/vs_buildtools.exe" - $vsInstallerPath = "$env:TEMP\vs_buildtools.exe" - - Write-Host "Downloading Visual Studio Build Tools..." - Invoke-WebRequest -Uri $vsInstallerUrl -OutFile $vsInstallerPath - - Write-Host "Installing Visual Studio Build Tools..." - Start-Process -FilePath $vsInstallerPath -ArgumentList "--quiet", "--wait", "--norestart", "--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "--add", "Microsoft.VisualStudio.Component.VC.CMake.Project", "--add", "Microsoft.VisualStudio.Component.VC.ATL", "--add", "Microsoft.VisualStudio.Component.Windows11SDK.22621" -Wait - - # Clean up installer - Remove-Item $vsInstallerPath -Force - - - name: Install build dependencies - run: | - Write-Host "Installing build dependencies using manual methods..." - - # Install Ninja - Write-Host "Installing Ninja..." - $ninjaUrl = "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip" - $ninjaPath = "$env:TEMP\ninja-win.zip" - $ninjaDir = "C:\ninja" - New-Item -ItemType Directory -Force -Path $ninjaDir - Invoke-WebRequest -Uri $ninjaUrl -OutFile $ninjaPath - Expand-Archive -Path $ninjaPath -DestinationPath $ninjaDir -Force - - # Install Strawberry Perl - Write-Host "Installing Strawberry Perl..." - $perlUrl = "http://strawberryperl.com/download/5.32.1.1/strawberry-perl-5.32.1.1-64bit.msi" - $perlPath = "$env:TEMP\strawberry-perl-5.32.1.1-64bit.msi" - Invoke-WebRequest -Uri $perlUrl -OutFile $perlPath - Start-Process msiexec.exe -ArgumentList "/i $perlPath /quiet /norestart" -Wait - - # Verify installations - $env:PATH = "C:\ninja;C:\Strawberry\perl\bin;$env:PATH" - Write-Host "Verifying installations..." - ninja --version - perl --version - - Write-Host "Manual installation of build dependencies completed" - - - name: Download ROCm nightly tarball - run: | - # Determine ROCm version to use - $rocmVersion = "${{ inputs.rocm_version }}" - $currentTarget = "${{ matrix.gfx_target }}" - - # Add appropriate suffixes for different GPU targets - $s3Target = $currentTarget - if ($currentTarget -eq "gfx103X") { - $s3Target = "$currentTarget-dgpu" - Write-Host "Using S3 target with -dgpu suffix: $s3Target" - } elseif ($currentTarget -eq "gfx110X" -or $currentTarget -eq "gfx120X") { - $s3Target = "$currentTarget-all" - Write-Host "Using S3 target with -all suffix: $s3Target" - } - - if ($rocmVersion -eq "latest") { - Write-Host "Auto-detecting latest ROCm version for target: $currentTarget" - $s3Response = (Invoke-WebRequest "https://therock-nightly-tarball.s3.amazonaws.com/?prefix=therock-dist-windows-$s3Target-7").Content - $files = $s3Response -split '' | Where-Object {$_ -match ''} | ForEach-Object { ($_ -split '')[0] } - - # Extract versions and sort them properly using semantic versioning - $versionFiles = @() - foreach ($file in $files) { - if ($file -match "therock-dist-windows-$s3Target-.*?(\d+\.\d+\.\d+(?:a|rc)\d+)\.tar\.gz") { - $version = $matches[1] - $versionFiles += [PSCustomObject]@{ - File = $file - Version = $version - Major = [int]($version -split '\.')[0] - Minor = [int]($version -split '\.')[1] - Patch = [int](($version -split '\.')[2] -replace '(?:a|rc).*', '') - RC = [int]($version -replace '.*(?:a|rc)', '') - IsAlpha = $version -match 'a' - } - } - } - - # Sort by semantic version (major.minor.patch.rc, with alpha versions being newer than rc) - $latestFile = ($versionFiles | Sort-Object Major, Minor, Patch, @{Expression={if($_.IsAlpha){1}else{0}}}, RC | Select-Object -Last 1).File - - Write-Host "Found latest file: $latestFile" - - # Extract version from the filename for environment variable - if ($latestFile -match "therock-dist-windows-$s3Target-.*?(\d+\.\d+\.\d+(?:a|rc)\d+)\.tar\.gz") { - $rocmVersion = $matches[1] - Write-Host "Detected latest ROCm version: $rocmVersion" - } else { - Write-Error "Failed to extract ROCm version from latest file: $latestFile" - Write-Error "Expected pattern: therock-dist-windows-$s3Target-*.tar.gz" - exit 1 - } - - # Use the exact filename from S3 instead of reconstructing - $rocmUrl = "https://therock-nightly-tarball.s3.amazonaws.com/$latestFile" - } else { - # For specific versions, construct the URL as before - $rocmUrl = "https://therock-nightly-tarball.s3.amazonaws.com/therock-dist-windows-$s3Target-$rocmVersion.tar.gz" - } - - # Store the version for use in other steps - echo "DETECTED_ROCM_VERSION=$rocmVersion" >> $env:GITHUB_ENV - - Write-Host "Downloading ROCm from: $rocmUrl" - Invoke-WebRequest -Uri $rocmUrl -OutFile "rocm.tar.gz" - - - name: Extract ROCm to C:\opt\rocm - run: | - # Create directory if it doesn't exist - New-Item -ItemType Directory -Force -Path "C:\opt\rocm" - - # Extract the tarball - tar -xzf rocm.tar.gz -C C:\opt\rocm --strip-components=1 - - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p llama.cpp - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C llama.cpp --strip-components=1 - - - name: Build Llama.cpp + ROCm - shell: cmd - run: | - - REM Map GPU targets - set "current_target=${{ matrix.gfx_target }}" - echo Input target: %current_target% - - if "%current_target%"=="gfx110X" ( - set "mapped_target=gfx1100;gfx1101;gfx1102;gfx1103" - ) else if "%current_target%"=="gfx103X" ( - set "mapped_target=gfx1030;gfx1031;gfx1032;gfx1034" - ) else if "%current_target%"=="gfx1151" ( - set "mapped_target=gfx1151" - ) else if "%current_target%"=="gfx1150" ( - set "mapped_target=gfx1150" - ) else if "%current_target%"=="gfx120X" ( - set "mapped_target=gfx1200;gfx1201" - ) else ( - set "mapped_target=%current_target%" - ) - echo Mapped target: %mapped_target% - - REM Set up environment variables and PATH - set HIP_PATH=C:\opt\rocm - set HIP_PLATFORM=amd - set PATH=%HIP_PATH%\lib\llvm\bin;%HIP_PATH%\bin;%PATH% - - REM Set up x64 Native Tools Command Prompt environment. - REM Pin to the VS 2022 line (-version "[17.0,18.0)") so vswhere -latest does not - REM pick up the runner image's VS 2026 (MSVC 14.51), whose STL adds constexpr to - REM math builtins like isgreater; that constexpr (implicitly __host__ __device__) - REM collides with clang's HIP __device__ forward declares and breaks the ggml-cuda - REM .cu compile (ggml-org/llama.cpp#22570). VS 2022 (MSVC 14.44) is unaffected. - call "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -version "[17.0,18.0)" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath > vs_path.txt - set /p VS_PATH=)[^<]*' | grep "therock-dist-linux-${s3_target}-") - - # Extract versions and sort them properly using semantic versioning - latest_file="" - latest_major=0 - latest_minor=0 - latest_patch=0 - latest_rc=0 - latest_is_alpha=false - - while IFS= read -r file; do - if [[ "$file" =~ therock-dist-linux-${s3_target}-.*?([0-9]+\.[0-9]+\.[0-9]+(a|rc)[0-9]+)\.tar\.gz ]]; then - version="${BASH_REMATCH[1]}" - major=$(echo "$version" | cut -d. -f1) - minor=$(echo "$version" | cut -d. -f2) - patch=$(echo "$version" | cut -d. -f3 | sed 's/\(a\|rc\).*//') - rc=$(echo "$version" | sed 's/.*\(a\|rc\)//') - is_alpha=false - if [[ "$version" =~ a ]]; then - is_alpha=true - fi - - # Compare versions (alpha versions are newer than rc versions) - is_newer=false - if [ "$major" -gt "$latest_major" ]; then - is_newer=true - elif [ "$major" -eq "$latest_major" ] && [ "$minor" -gt "$latest_minor" ]; then - is_newer=true - elif [ "$major" -eq "$latest_major" ] && [ "$minor" -eq "$latest_minor" ] && [ "$patch" -gt "$latest_patch" ]; then - is_newer=true - elif [ "$major" -eq "$latest_major" ] && [ "$minor" -eq "$latest_minor" ] && [ "$patch" -eq "$latest_patch" ]; then - # Same major.minor.patch, compare alpha vs rc - if [ "$is_alpha" = true ] && [ "$latest_is_alpha" = false ]; then - is_newer=true - elif [ "$is_alpha" = "$latest_is_alpha" ] && [ "$rc" -gt "$latest_rc" ]; then - is_newer=true - fi - fi - - if [ "$is_newer" = true ]; then - latest_file="$file" - latest_major="$major" - latest_minor="$minor" - latest_patch="$patch" - latest_rc="$rc" - latest_is_alpha="$is_alpha" - fi - fi - done <<< "$files" - - echo "Found latest file: $latest_file" - - # Extract version from the filename for environment variable - if [[ "$latest_file" =~ therock-dist-linux-${s3_target}-.*?([0-9]+\.[0-9]+\.[0-9]+(a|rc)[0-9]+)\.tar\.gz ]]; then - rocm_version="${BASH_REMATCH[1]}" - echo "Detected latest ROCm version: $rocm_version" - else - echo "Failed to extract ROCm version from latest file: $latest_file" - echo "Expected pattern: therock-dist-linux-${s3_target}-*.tar.gz" - exit 1 - fi - - # Use the exact filename from S3 instead of reconstructing - rocm_url="https://therock-nightly-tarball.s3.amazonaws.com/$latest_file" - else - # For specific versions, construct the URL as before - rocm_url="https://therock-nightly-tarball.s3.amazonaws.com/therock-dist-linux-${s3_target}-${rocm_version}.tar.gz" - fi - - # Store the version for use in other steps - echo "DETECTED_ROCM_VERSION=$rocm_version" >> $GITHUB_ENV - - echo "Streaming ROCm from: $rocm_url directly to extraction" - - # Create directory if it doesn't exist - sudo mkdir -p /opt/rocm - - # Stream download directly into tar extraction (no intermediate file) - curl -sL "$rocm_url" | sudo tar --use-compress-program=gzip -xf - -C /opt/rocm --strip-components=1 - - - name: Set ROCm environment variables - run: | - echo "Setting ROCm environment variables..." - - # Set environment variables for this step and subsequent steps - echo "HIP_PATH=/opt/rocm" >> $GITHUB_ENV - echo "ROCM_PATH=/opt/rocm" >> $GITHUB_ENV - echo "HIP_PLATFORM=amd" >> $GITHUB_ENV - echo "HIP_CLANG_PATH=/opt/rocm/llvm/bin" >> $GITHUB_ENV - echo "HIP_INCLUDE_PATH=/opt/rocm/include" >> $GITHUB_ENV - echo "HIP_LIB_PATH=/opt/rocm/lib" >> $GITHUB_ENV - echo "HIP_DEVICE_LIB_PATH=/opt/rocm/lib/llvm/amdgcn/bitcode" >> $GITHUB_ENV - - # Update PATH - echo "/opt/rocm/bin:/opt/rocm/llvm/bin:$PATH" >> $GITHUB_PATH - - # Set library paths - echo "LD_LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/lib64:/opt/rocm/llvm/lib:${LD_LIBRARY_PATH:-}" >> $GITHUB_ENV - echo "LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/lib64:${LIBRARY_PATH:-}" >> $GITHUB_ENV - echo "CPATH=/opt/rocm/include:${CPATH:-}" >> $GITHUB_ENV - echo "PKG_CONFIG_PATH=/opt/rocm/lib/pkgconfig:${PKG_CONFIG_PATH:-}" >> $GITHUB_ENV - - echo "ROCm environment variables set successfully" - - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p llama.cpp - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C llama.cpp --strip-components=1 - - - name: Build Llama.cpp + ROCm - run: | - # Map GPU targets - current_target="${{ matrix.gfx_target }}" - echo "Input target: $current_target" - - if [ "$current_target" = "gfx110X" ]; then - mapped_target="gfx1100;gfx1101;gfx1102;gfx1103" - elif [ "$current_target" = "gfx103X" ]; then - mapped_target="gfx1030;gfx1031;gfx1032;gfx1034" - elif [ "$current_target" = "gfx1151" ]; then - mapped_target="gfx1151" - elif [ "$current_target" = "gfx1150" ]; then - mapped_target="gfx1150" - elif [ "$current_target" = "gfx120X" ]; then - mapped_target="gfx1200;gfx1201" - else - mapped_target="$current_target" - fi - echo "Mapped target: $mapped_target" - - # Create build directory - cd llama.cpp - mkdir build - cd build - - # Configure the project - cmake .. -G Ninja \ - -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang \ - -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ \ - -DCMAKE_CXX_FLAGS="-I/opt/rocm/include" \ - -DCMAKE_CROSSCOMPILING=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DGPU_TARGETS="$mapped_target" \ - -DBUILD_SHARED_LIBS=ON \ - -DLLAMA_BUILD_TESTS=OFF \ - -DGGML_HIP=ON \ - -DGGML_OPENMP=OFF \ - -DGGML_CUDA_FORCE_CUBLAS=OFF \ - -DGGML_RPC=ON \ - -DGGML_HIP_ROCWMMA_FATTN=OFF \ - -DLLAMA_BUILD_BORINGSSL=ON \ - -DGGML_NATIVE=OFF \ - -DGGML_STATIC=OFF \ - -DCMAKE_SYSTEM_NAME=Linux - - # Build the project - cmake --build . -j $(nproc) - - - name: Copy ROCm core libs to build directory - run: | - build_bin_path="llama.cpp/build/bin" - rocm_bin_path="/opt/rocm/bin" - - # Copy the rocblas/library folder and all its contents - rocblas_lib_path="/opt/rocm/lib/rocblas/library" - if [ -d "$rocblas_lib_path" ]; then - echo "Copying rocblas/library folder and all contents..." - dest_rocblas_path="$build_bin_path/rocblas/library" - mkdir -p "$(dirname "$dest_rocblas_path")" - cp -r "$rocblas_lib_path" "$(dirname "$dest_rocblas_path")/" - echo "Copied: rocblas/library folder with all contents" - - # List the contents of the copied rocblas/library folder - echo "Contents of rocblas/library:" - find "$dest_rocblas_path" -type f -exec ls -la {} \; | head -20 - else - echo "Warning: rocblas/library folder not found at: $rocblas_lib_path" - fi - - # Copy the hipblaslt/library folder and all its contents - hipblaslt_lib_path="/opt/rocm/lib/hipblaslt/library" - if [ -d "$hipblaslt_lib_path" ]; then - echo "Copying hipblaslt/library folder and all contents..." - dest_hipblaslt_path="$build_bin_path/hipblaslt/library" - mkdir -p "$(dirname "$dest_hipblaslt_path")" - cp -r "$hipblaslt_lib_path" "$(dirname "$dest_hipblaslt_path")/" - echo "Copied: hipblaslt/library folder with all contents" - - # List the contents of the copied hipblaslt/library folder - echo "Contents of hipblaslt/library:" - find "$dest_hipblaslt_path" -type f -exec ls -la {} \; | head -20 - else - echo "Warning: hipblaslt/library folder not found at: $hipblaslt_lib_path" - fi - - # Copy required ROCm libraries to build directory - # If artifacts from ROCm or Llama.cpp change, you may need to update this list - # To get a new list of all libraries, run: - # gather_required_libs.py --rocm-dir /opt/rocm --dest-dir llama.cpp/build/bin - echo "Copying required ROCm libraries to build directory..." - cp -v /opt/rocm/lib/libhipblas.so* "$build_bin_path/" 2>/dev/null || echo "libhipblas.so* not found" - cp -v /opt/rocm/lib/librocblas.so* "$build_bin_path/" 2>/dev/null || echo "librocblas.so* not found" - cp -v /opt/rocm/lib/libamdhip64.so* "$build_bin_path/" 2>/dev/null || echo "libamdhip64.so* not found" - cp -v /opt/rocm/lib/librocsolver.so* "$build_bin_path/" 2>/dev/null || echo "librocsolver.so* not found" - cp -v /opt/rocm/lib/libroctx64.so* "$build_bin_path/" 2>/dev/null || echo "libroctx64.so* not found" - cp -v /opt/rocm/lib/libhipblaslt.so* "$build_bin_path/" 2>/dev/null || echo "libhipblaslt.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_liblzma.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_liblzma.so* not found" - cp -v /opt/rocm/lib/librocprofiler-register.so* "$build_bin_path/" 2>/dev/null || echo "librocprofiler-register.so* not found" - cp -v /opt/rocm/lib/libamd_comgr.so* "$build_bin_path/" 2>/dev/null || echo "libamd_comgr.so* not found" - cp -v /opt/rocm/lib/libamd_comgr_loader.so* "$build_bin_path/" 2>/dev/null || echo "libamd_comgr_loader.so* not found" - cp -v /opt/rocm/lib/libhsa-runtime64.so* "$build_bin_path/" 2>/dev/null || echo "libhsa-runtime64.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_numa.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_numa.so* not found" - cp -v /opt/rocm/lib/librocroller.so* "$build_bin_path/" 2>/dev/null || echo "librocroller.so* not found" - cp -v /opt/rocm/lib/librocm_kpack.so* "$build_bin_path/" 2>/dev/null || echo "librocm_kpack.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_z.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_z.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_zstd.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_zstd.so* not found" - cp -v /opt/rocm/lib/llvm/lib/libLLVM.so* "$build_bin_path/" 2>/dev/null || echo "libLLVM.so* not found" - cp -v /opt/rocm/lib/llvm/lib/libclang-cpp.so* "$build_bin_path/" 2>/dev/null || echo "libclang-cpp.so* not found" - - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_elf.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_elf.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_drm.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_drm.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_drm_amdgpu.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_drm_amdgpu.so* not found" - cp -v /opt/rocm/lib/rocm_sysdeps/lib/librocm_sysdeps_bz2.so* "$build_bin_path/" 2>/dev/null || echo "librocm_sysdeps_bz2.so* not found" - - # libatomic.so.1 is a transitive dependency of librocm_sysdeps_numa - # (libhsa-runtime64 -> numa -> libatomic) but it is a system GCC runtime - # library, not shipped under /opt/rocm. Bundle it so the runtime stays - # self-contained on hosts that lack it. lemonade-sdk/lemonade#1349 hit - # exactly this ("libatomic.so.1: cannot open shared object file"). - sudo apt-get install -y libatomic1 >/dev/null 2>&1 || true - libatomic_path="$(ldconfig -p | awk -F'=> ' '/libatomic\.so\.1/{print $2; exit}')" - cp -v "$libatomic_path" "$build_bin_path/" 2>/dev/null || echo "libatomic.so.1 not found" - - echo "Finished copying required ROCm libraries" - - - name: Set RPATH for portable distribution - run: | - sudo apt-get install -y patchelf - cd llama.cpp/build/bin - # Set RPATH to $ORIGIN so all libraries (including the comgr stub loader) find deps locally - for file in *.so* llama-*; do - [ -f "$file" ] && [ ! -L "$file" ] && patchelf --set-rpath '$ORIGIN' "$file" 2>/dev/null || true - done - - - name: List build artifacts (including ROCm files) - run: | - cd llama.cpp/build/bin - echo "Final build artifacts (including ROCm library files):" - ls -la - - - name: Package bundle (tar.gz) - run: | - set -eux - ASSET="app-${{ inputs.tag }}-linux-x64-rocm-${{ matrix.gfx_target }}.tar.gz" - mkdir -p dist - (cd llama.cpp/build/bin && tar -czf "${GITHUB_WORKSPACE}/dist/${ASSET}" .) - ls -la dist - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-linux-x64-rocm-${{ matrix.gfx_target }} - path: dist/app-${{ inputs.tag }}-linux-x64-rocm-${{ matrix.gfx_target }}.tar.gz - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt-vulkan.yml b/.github/workflows/unsloth-prebuilt-vulkan.yml deleted file mode 100644 index 97f7b9a04b9..00000000000 --- a/.github/workflows/unsloth-prebuilt-vulkan.yml +++ /dev/null @@ -1,268 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: "Unsloth prebuilt: Vulkan" - -# Reusable child of unsloth-prebuilt.yml. Builds the Vulkan bundles for -# Linux x64 + Windows x64. Each job uploads a single app-*.{tar.gz|zip} -# artifact for the parent's assemble step to pick up. -# -# Mirrors oobabooga/llama-cpp-binaries' build-wheels-vulkan.yml build recipe -# (the CPU recipe plus GGML_VULKAN=ON and a Vulkan SDK install), adapted to -# this repo's conventions: app---x64-vulkan archives packaged -# straight from build/bin like the ROCm/macOS children (no embedded -# UNSLOTH_PREBUILT_INFO.json -- assemble_metadata.py derives the manifest entry -# from the filename), MSVC + BoringSSL on Windows, $ORIGIN RPATH on Linux. -# -# The Vulkan loader (libvulkan.so.1 / vulkan-1.dll) is a system/driver library -# resolved by the OS at runtime, so -- like llama-cpp-binaries -- it is not -# bundled; only ggml's own libggml-vulkan backend module ships in the archive. - -on: - workflow_call: - inputs: - tag: - description: 'Upstream llama.cpp release tag (b####), resolved by parent' - required: true - type: string - repo: - description: 'Source repo (owner/name): ggml-org/llama.cpp for plain builds, or this repo for mix tags' - required: false - default: 'ggml-org/llama.cpp' - type: string - source_artifact: - description: 'Workflow artifact (app-source-*) holding the stamped source tree; set by resolve for every build' - required: false - default: '' - type: string - -permissions: - contents: read - -env: - # LunarG SDK version, matching llama-cpp-binaries' build-wheels-vulkan.yml. - VULKAN_VERSION: 1.4.313.2 - -jobs: - build-linux: - name: linux/x64 - runs-on: ubuntu-22.04 - steps: - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: Install Vulkan SDK + build dependencies - run: | - set -eux - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list - sudo apt-get update -y - sudo apt-get install -y build-essential mesa-vulkan-drivers vulkan-sdk libssl-dev ninja-build - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: vulkan-linux-x64 - variant: ccache - evict-old-files: 14d - - - name: Configure - working-directory: src - run: | - set -eux - # Build recipe mirrors llama-cpp-binaries' Vulkan wheel: the CPU recipe - # (backend-DL + all CPU variants + RPC) plus GGML_VULKAN. RPATH=$ORIGIN - # so the bundle's sibling .so files resolve from the binary's own dir. - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DGGML_NATIVE=OFF \ - -DGGML_BACKEND_DL=ON \ - -DGGML_CPU_ALL_VARIANTS=ON \ - -DGGML_RPC=ON \ - -DGGML_VULKAN=ON \ - -DLLAMA_BUILD_TESTS=OFF \ - -DLLAMA_BUILD_EXAMPLES=OFF \ - -DCMAKE_INSTALL_RPATH='$ORIGIN' \ - -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ - -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=OFF \ - -DCMAKE_C_COMPILER_LAUNCHER=ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - - - name: Build - working-directory: src - run: | - set -eux - # Backend modules (CPU variants, Vulkan, RPC) build as ggml - # dependencies, so the server/quantize targets pull them in. - cmake --build build --config Release -j "$(nproc)" \ - --target llama-server llama-quantize - strip build/bin/llama-server build/bin/llama-quantize || true - - # DiffusionGemma binaries (example targets present only in #24423 mix - # builds): best-effort, never fail the job. See the CUDA child for the - # rationale. The bundle tars all of build/bin, so anything produced here - # is shipped automatically. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - set -u - if [ ! -d examples/diffusion-gemma-server ]; then - echo "no DiffusionGemma sources in this tree; skipping" - exit 0 - fi - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON \ - || { echo "reconfigure for examples failed; skipping DiffusionGemma binaries"; exit 0; } - if cmake --build build --config Release -j "$(nproc)" \ - --target llama-diffusion-gemma-visual-server llama-diffusion-cli; then - strip build/bin/llama-diffusion-gemma-visual-server build/bin/llama-diffusion-cli || true - echo "built DiffusionGemma binaries" - else - echo "warning: DiffusionGemma binaries failed to build; bundle will omit them" - fi - exit 0 - - - name: Package bundle (tar.gz) - run: | - set -eux - ASSET="app-${{ inputs.tag }}-linux-x64-vulkan.tar.gz" - cp src/LICENSE src/build/bin/ - mkdir -p dist - (cd src/build/bin && tar -czf "${GITHUB_WORKSPACE}/dist/${ASSET}" .) - ls -la dist - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-linux-x64-vulkan - path: dist/app-${{ inputs.tag }}-linux-x64-vulkan.tar.gz - if-no-files-found: error - - build-windows: - name: windows/x64 - runs-on: windows-2022 - defaults: - run: - shell: pwsh - steps: - # The parent's resolve job built the source tree (upstream base + any mix - # PRs, with the build number/commit and Unsloth fingerprint baked - # into cmake/build-info.cmake) and uploaded it as an artifact; extract it - # instead of cloning -- no .git needed, the build number is already baked. - - name: Download source @ ${{ inputs.tag }} - uses: actions/download-artifact@v6 - with: - name: ${{ inputs.source_artifact }} - path: srcpkg - - name: Extract source - shell: bash - run: | - set -eux - mkdir -p src - tar -xzf "srcpkg/llama.cpp-source-${{ inputs.tag }}.tar.gz" -C src --strip-components=1 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2.20 - with: - key: vulkan-windows-x64 - variant: ccache - evict-old-files: 14d - - - name: Install Ninja - run: choco install ninja --no-progress - - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - - name: Install Vulkan SDK - run: | - curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/vulkansdk-windows-X64-${env:VULKAN_VERSION}.exe" - & "$env:RUNNER_TEMP\VulkanSDK-Installer.exe" --accept-licenses --default-answer --confirm-command install - Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\${env:VULKAN_VERSION}" - Add-Content $env:GITHUB_PATH "C:\VulkanSDK\${env:VULKAN_VERSION}\bin" - - - name: Configure - working-directory: src - run: | - # Same recipe as Linux plus BoringSSL (statically linked, no system - # OpenSSL on the runner). No RPATH -- Windows loads sibling DLLs from - # the binary's directory. CMAKE_PREFIX_PATH points at the Vulkan SDK - # so CMake finds its SPIRV-Headers config (set via env, not -D, so the - # backslashes survive); mirrors llama-cpp-binaries' Vulkan wheel. - # GGML_OPENMP=OFF keeps this MSVC bundle self-contained: a default-ON - # MSVC build would link vcomp140.dll (not shipped). Upstream sidesteps - # this by building its Windows Vulkan artifact with GGML_CPU=OFF (no - # OpenMP at all); we ship a full bundle, so we disable OpenMP instead - # (the CPU backend is a GPU-offload fallback and uses ggml's threadpool). - $env:CMAKE_PREFIX_PATH = $env:VULKAN_SDK - cmake -S . -B build -G Ninja ` - -DCMAKE_BUILD_TYPE=Release ` - -DGGML_NATIVE=OFF ` - -DGGML_BACKEND_DL=ON ` - -DGGML_CPU_ALL_VARIANTS=ON ` - -DGGML_OPENMP=OFF ` - -DGGML_RPC=ON ` - -DGGML_VULKAN=ON ` - -DLLAMA_BUILD_TESTS=OFF ` - -DLLAMA_BUILD_EXAMPLES=OFF ` - -DLLAMA_BUILD_BORINGSSL=ON ` - -DCMAKE_C_COMPILER_LAUNCHER=ccache ` - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - - - name: Build - working-directory: src - run: | - cmake --build build --config Release -j 3 ` - --target llama-server llama-quantize - - # DiffusionGemma binaries (#24423 mix builds only): best-effort, never - # fail the job. See the CUDA child for the rationale. - - name: Build DiffusionGemma binaries (best-effort; mix builds only) - working-directory: src - run: | - if (-not (Test-Path "examples/diffusion-gemma-server")) { - Write-Host "no DiffusionGemma sources in this tree; skipping" - exit 0 - } - cmake -S . -B build -DLLAMA_BUILD_EXAMPLES=ON - if ($LASTEXITCODE -ne 0) { - Write-Host "reconfigure for examples failed; skipping DiffusionGemma binaries" - exit 0 - } - cmake --build build --config Release -j 3 ` - --target llama-diffusion-gemma-visual-server llama-diffusion-cli - if ($LASTEXITCODE -ne 0) { - Write-Host "warning: DiffusionGemma binaries failed to build; bundle will omit them" - } else { - Write-Host "built DiffusionGemma binaries" - } - exit 0 - - - name: Package bundle (zip) - shell: bash - run: | - set -eux - ASSET="app-${{ inputs.tag }}-windows-x64-vulkan.zip" - cp src/LICENSE src/build/bin/ - mkdir -p dist - (cd src/build/bin && 7z a -tzip "${GITHUB_WORKSPACE}/dist/${ASSET}" .) - ls -la dist - - - name: Upload bundle artifact - uses: actions/upload-artifact@v6 - with: - name: app-${{ inputs.tag }}-windows-x64-vulkan - path: dist/app-${{ inputs.tag }}-windows-x64-vulkan.zip - if-no-files-found: error diff --git a/.github/workflows/unsloth-prebuilt.yml b/.github/workflows/unsloth-prebuilt.yml deleted file mode 100644 index 13ff8d0626e..00000000000 --- a/.github/workflows/unsloth-prebuilt.yml +++ /dev/null @@ -1,620 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-only -# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. - -name: Unsloth prebuilt (full release) - -# Atomic daily build for unslothai/llama.cpp. One cron, one workflow run, one -# release per upstream b#### tag. Splits the heavy build work into six reusable -# children: -# unsloth-prebuilt-cuda.yml -- Linux CUDA bundles (x64 + arm64, matrix profiles) -# unsloth-prebuilt-cuda-windows.yml -- CUDA Windows bundles (x64, matrix profiles) -# unsloth-prebuilt-rocm.yml -- ROCm bundles (Windows + Ubuntu, per gfx target) -# unsloth-prebuilt-macos.yml -- macOS bundles (arm64 Metal + x64 CPU) -# unsloth-prebuilt-cpu.yml -- CPU-only bundles (Linux + Windows, x64 + arm64) -# unsloth-prebuilt-vulkan.yml -- Vulkan bundles (Linux + Windows, x64) -# -# Atomicity: the `assemble` job depends on all children. GitHub's default -# `needs` semantics require all needs to succeed -- if any matrix entry in -# any child fails, `assemble` skips and no release is published. The -# installer needs the full bundle set at the same tag or it'll dispatch to -# something that isn't there. -# -# Mix builds: scripts/unsloth/pr-set.json can list upstream PRs (each pinned -# to an exact commit) to merge into the build. `resolve` merges the open ones -# onto the base tag and uploads the merged tree as a workflow artifact that -# the children extract instead of cloning; the release is tagged -# b####-mix-. Empty list = vanilla upstream build. - -on: - schedule: - - cron: '13 20 * * *' # ~1PM San Francisco PDT / ~12PM PST (UTC; not DST adjusted) - workflow_dispatch: - inputs: - tag: - description: 'ggml-org tag (b#### or "latest")' - default: 'latest' - required: true - type: string - min_age_hours: - description: 'For "latest": only build a release public for at least this many hours (blank = default 6)' - default: '' - required: false - type: string - only_profile: - description: 'CUDA profile to build' - default: 'all' - required: false - type: choice - options: [all, cuda12-older, cuda12-newer, cuda12-portable, cuda13-older, cuda13-newer, cuda13-portable] - operating_systems: - description: 'OSes for ROCm builds' - default: 'windows,ubuntu' - required: false - type: string - gfx_target: - description: 'GPU targets for ROCm builds' - default: 'gfx1151,gfx1150,gfx120X,gfx110X,gfx103X,gfx90a,gfx908' - required: false - type: string - rocm_version: - description: 'ROCm version (or "latest")' - default: 'latest' - required: false - type: string - publish: - description: 'Publish to GitHub Releases' - default: false - required: false - type: boolean - -permissions: - contents: write - -env: - # Supply-chain aging: when resolving "latest", only build an upstream release - # that has been public for at least this many hours, so a malicious or broken - # release has time to be caught and yanked before we compile and ship it. An - # explicit b#### tag (manual run) skips it. Per-run override via min_age_hours. - UNSLOTH_LLAMA_MIN_RELEASE_AGE_HOURS: "6" - -concurrency: - group: ${{ github.workflow }}-${{ github.event.inputs.tag || 'scheduled' }} - cancel-in-progress: false - -jobs: - resolve: - name: Resolve tag - runs-on: ubuntu-22.04 - # Read-only: resolve merges third-party PR content but pushes nothing; - # only assemble needs the workflow-level contents:write (publish). - permissions: - contents: read - outputs: - tag: ${{ steps.r.outputs.tag }} - repo: ${{ steps.r.outputs.repo }} - base: ${{ steps.r.outputs.base }} - prs: ${{ steps.r.outputs.prs }} - source_artifact: ${{ steps.r.outputs.source_artifact }} - commit: ${{ steps.r.outputs.commit }} - exists: ${{ steps.r.outputs.exists }} - cuda_matrix: ${{ steps.r.outputs.cuda_matrix }} - win_cuda_matrix: ${{ steps.r.outputs.win_cuda_matrix }} - rocm_matrix: ${{ steps.r.outputs.rocm_matrix }} - macos_matrix: ${{ steps.r.outputs.macos_matrix }} - env: - GH_TOKEN: ${{ github.token }} - steps: - # Shallow by default (only pr-set.json is needed); a mix build unshallows - # in-place to merge the PRs. - - name: Checkout (pr-set.json + mix merge workspace) - uses: actions/checkout@v6 - - id: r - run: | - set -euo pipefail - REQ='${{ github.event.inputs.tag || 'latest' }}' - ONLY='${{ github.event.inputs.only_profile || 'all' }}' - GFX_DEFAULT='gfx1151,gfx1150,gfx120X,gfx110X,gfx103X,gfx90a,gfx908' - GFX='${{ github.event.inputs.gfx_target }}'; GFX="${GFX:-$GFX_DEFAULT}" - OS_LIST='${{ github.event.inputs.operating_systems || 'windows,ubuntu' }}' - AGE_H='${{ github.event.inputs.min_age_hours }}' - - # publish does delete-then-create on the whole release, and the installer - # treats the published manifest as the authoritative bundle set: a partial - # build drops bundles and silently downgrades uncovered hosts to slow - # source builds. Refuse to publish unless the full default set is built; - # publish=false test runs may use subsets. - if [ "${{ github.event_name }}" = "schedule" ] || [ "${{ inputs.publish }}" = "true" ]; then - [ "$ONLY" = "all" ] || { echo "refusing to publish only_profile=$ONLY: a partial CUDA set clobbers manifest coverage. Use only_profile=all to publish, or publish=false for an artifact-only test." >&2; exit 1; } - [ "$GFX" = "$GFX_DEFAULT" ] || { echo "refusing to publish gfx_target='$GFX': publish requires the full default set ($GFX_DEFAULT); use publish=false for a subset test." >&2; exit 1; } - case "$OS_LIST" in - *windows*ubuntu*|*ubuntu*windows*) : ;; - *) echo "refusing to publish operating_systems='$OS_LIST': both windows and ubuntu ROCm bundles are required. Use publish=false for a subset test." >&2; exit 1 ;; - esac - fi - [ -n "$AGE_H" ] || AGE_H="${UNSLOTH_LLAMA_MIN_RELEASE_AGE_HOURS:-6}" - if [ "$REQ" = "latest" ]; then - # Newest published (non-draft, non-prerelease) release that has been - # public for >= AGE_H hours -- the supply-chain aging window. GitHub - # does not guarantee the list order, so pick the max by published_at - # explicitly rather than trusting `first`. - CUTOFF="$(date -u -d "-${AGE_H} hours" +%s)" - BASE="$(gh api 'repos/ggml-org/llama.cpp/releases?per_page=100' \ - --jq "[.[] | select(.draft==false and .prerelease==false) | select((.published_at|fromdateiso8601) <= ${CUTOFF})] | max_by(.published_at|fromdateiso8601) | .tag_name")" - if [ -z "$BASE" ] || [ "$BASE" = "null" ]; then - echo "refusing: no ggml-org release older than ${AGE_H}h in the last 100 releases" >&2 - exit 1 - fi - echo "selected $BASE (aged >= ${AGE_H}h)" - else - BASE="$REQ" # explicit b#### override skips the aging filter - fi - printf '%s' "$BASE" | grep -qE '^b[0-9]+$' || { echo "refusing non-release tag '$BASE'" >&2; exit 1; } - - # Resolve the PR mix set (scripts/unsloth/pr-set.json): upstream PR - # commit urls. Pins are mandatory -- only an exact, reviewed commit - # is ever built, so a PR author pushing more commits cannot change - # what the nightly ships. Non-open PRs are skipped, so merged or - # abandoned entries rot away harmlessly instead of breaking the - # nightly. unsloth-pr-set-lint.yml runs the same checks on every - # push that edits the file, but that is only a tripwire -- a red - # lint does not stop the schedule, so the gate must live here. - jq -e '.prs | type == "array" and all(.[]; type == "string")' scripts/unsloth/pr-set.json >/dev/null \ - || { echo "scripts/unsloth/pr-set.json: .prs must be an array of PR url strings" >&2; exit 1; } - PRS='[]' - URL_RE='^https://github\.com/ggml-org/llama\.cpp/pull/([0-9]+)/commits/([0-9a-f]{40})/?$' - for url in $(jq -r '.prs[]' scripts/unsloth/pr-set.json); do - [[ "$url" =~ $URL_RE ]] || { echo "refusing malformed PR url '$url' (expected https://github.com/ggml-org/llama.cpp/pull//commits/<40-hex-sha>)" >&2; exit 1; } - NUM="${BASH_REMATCH[1]}"; SHA="${BASH_REMATCH[2]}" - # plain assignment first so a gh failure aborts the run (set -e) - # instead of being swallowed by a later read's exit status. Title can - # contain spaces, so it can't ride a space-delimited read -- pull each - # field out on its own. - PR_JSON="$(gh api "repos/ggml-org/llama.cpp/pulls/${NUM}")" - STATE="$(jq -r '.state' <<<"$PR_JSON")" - HEAD="$(jq -r '.head.sha' <<<"$PR_JSON")" - N_COMMITS="$(jq -r '.commits' <<<"$PR_JSON")" - TITLE="$(jq -r '.title' <<<"$PR_JSON")" - if [ "$STATE" != "open" ]; then - echo "skipping PR #${NUM} (${STATE}): $url" - continue - fi - # A pin pasted from the wrong PR would build arbitrary upstream - # code while the manifest blames PR #; require the pinned - # commit to be a commit of that PR. The commits listing is capped - # at 250 by the API; past that, skip rather than false-fail. - if [ "$N_COMMITS" -gt 250 ]; then - echo "note: PR #${NUM} has ${N_COMMITS} commits (over the API listing cap); skipping pin membership check" - elif ! gh api "repos/ggml-org/llama.cpp/pulls/${NUM}/commits" --paginate --jq '.[].sha' | grep -qx "$SHA"; then - echo "refusing PR #${NUM}: pinned commit ${SHA} is not a commit of that PR (wrong paste, or force-pushed away); fix the pin in scripts/unsloth/pr-set.json" >&2 - exit 1 - fi - [ "$SHA" = "$HEAD" ] || echo "note: PR #${NUM} is pinned to ${SHA} but its head has moved to ${HEAD}" - echo "including PR #${NUM} @ ${SHA}" - PRS="$(jq -c --arg n "$NUM" --arg s "$SHA" --arg u "$url" --arg t "$TITLE" '. + [{number: ($n|tonumber), sha: $s, url: $u, title: $t}]' <<<"$PRS")" - done - - # Decide the tag and source repo first; the source tree is built - # further down, only if this release isn't already published. - if [ "$(jq length <<<"$PRS")" = 0 ]; then - # Plain build: pristine upstream base, no merge. REPO stays the real - # upstream repo even though we ship our own stamped tarball. - TAG="$BASE" - REPO='ggml-org/llama.cpp' - else - # The synthetic tag embeds a hash of the pinned SHAs in listed order - # (merge order can matter for conflicts), so a pin update or a reorder - # yields a new tag and build, while the "exists" check below still - # skips rebuilding an already-published set. The merged tree exists - # only as this repo's release assets, never upstream. - SETHASH="$(jq -r 'map("\(.number):\(.sha)") | join("\n")' <<<"$PRS" | sha256sum | cut -c1-7)" - TAG="${BASE}-mix-${SETHASH}" - REPO="$GITHUB_REPOSITORY" - fi - SRC_ARTIFACT="app-source-${TAG}" - COMMIT="" - - # Only PUBLISHED releases count as "exists"; a leftover draft (from - # a failed prior publish) should not block today's rebuild. - EXISTS=false - if [ "$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft --jq .isDraft 2>/dev/null || true)" = "false" ]; then - EXISTS=true - fi - - # Build the source tree every child compiles, ONCE, here -- but only - # when something will actually be built. On a scheduled no-op (the aged - # "latest" was already published) skip the whole checkout/merge/tar + - # upload; a manual dispatch always rebuilds. Check out the upstream base, - # merge any pinned PRs (mix builds), then bake the build number/commit - # and the Unsloth fingerprint into cmake/build-info.cmake. The - # result is uploaded as the app-source-* artifact every child extracts -- - # one identical tree everywhere, no child clones, fingerprint patch in one - # place. Nothing is pushed (GITHUB_TOKEN may never push commits touching - # .github/workflows, which upstream history routinely does). - if [ "$EXISTS" != "true" ] || [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - git remote add upstream https://github.com/ggml-org/llama.cpp.git - if [ "$(jq length <<<"$PRS")" != 0 ]; then - # Merges need a merge-base, so unshallow first. - if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then - git fetch -q --unshallow --no-tags origin - fi - git fetch -q --no-tags upstream "refs/tags/${BASE}:refs/tags/${BASE}" - git checkout -q --detach "refs/tags/${BASE}" - for pair in $(jq -r '.[] | "\(.number):\(.sha)"' <<<"$PRS"); do - NUM="${pair%%:*}"; SHA="${pair##*:}" - git fetch -q --no-tags upstream "$SHA" \ - || { echo "could not fetch commit ${SHA} for PR #${NUM}; it may have been pruned after a force-push -- update or remove the pin in scripts/unsloth/pr-set.json" >&2; exit 1; } - git -c user.name='github-actions[bot]' -c user.email='41898282+github-actions[bot]@users.noreply.github.com' \ - merge --no-ff --no-edit -m "Merge ggml-org/llama.cpp#${NUM} @ ${SHA}" "$SHA" \ - || { echo "PR #${NUM} (${SHA}) does not merge cleanly onto ${BASE} + the PRs listed before it; reorder or drop it in scripts/unsloth/pr-set.json" >&2; exit 1; } - done - else - # Plain build: only the base tree is needed. Shallow is fine -- the - # build number is baked below, so no git history is needed at build time. - git fetch -q --depth 1 --no-tags upstream "refs/tags/${BASE}:refs/tags/${BASE}" - git checkout -q --detach "refs/tags/${BASE}" - fi - COMMIT="$(git rev-parse HEAD)" - - # The tarball ships without .git, where cmake/build-info.cmake falls - # back to its defaults; bake real values into those defaults so - # llama-server --version doesn't report b0 (unknown). The build number - # stays the BASE's b#### number (the upstream tag's commit count), so - # anything comparing versions against upstream releases keeps working; - # BUILD_COMMIT identifies the (possibly merged) head. - COUNT="${BASE#b}" - SHORT="$(git rev-parse --short HEAD)" - sed -i "s/^set(BUILD_NUMBER 0)$/set(BUILD_NUMBER ${COUNT})/" cmake/build-info.cmake - sed -i "s/^set(BUILD_COMMIT \"unknown\")$/set(BUILD_COMMIT \"${SHORT}\")/" cmake/build-info.cmake - grep -q "set(BUILD_NUMBER ${COUNT})" cmake/build-info.cmake \ - && grep -q "set(BUILD_COMMIT \"${SHORT}\")" cmake/build-info.cmake \ - || { echo "cmake/build-info.cmake no longer has the expected fallback lines; cannot bake the build number into the source artifact" >&2; exit 1; } - - # Unsloth fingerprint: fold "Compiled by the Unsloth team" - # into BUILD_TARGET, which cmake bakes into LLAMA_BUILD_TARGET, so it - # prints on llama-server --version ("built with ... for ...") and shows - # up in `strings` on every binary that links common. Append so it wraps - # whatever BUILD_TARGET the build computes. Keep this string byte-identical - # to MARK in the assemble job's verify gate, which re-checks it landed. - grep -q 'set(BUILD_TARGET' cmake/build-info.cmake \ - || { echo "cmake/build-info.cmake has no BUILD_TARGET line to stamp" >&2; exit 1; } - printf '\n# Unsloth fingerprint: shows in --version and strings.\nset(BUILD_TARGET "${BUILD_TARGET} (Compiled by the Unsloth team)")\n' >> cmake/build-info.cmake - grep -q 'Compiled by the Unsloth team' cmake/build-info.cmake \ - || { echo "failed to stamp the Unsloth fingerprint into cmake/build-info.cmake" >&2; exit 1; } - - tar -czf "${RUNNER_TEMP}/llama.cpp-source-${TAG}.tar.gz" --exclude-vcs --transform "s,^\.,llama.cpp-${TAG}," . - echo "prepared ${TAG} (${COMMIT}, build ${COUNT})" - else - echo "release ${TAG} already published; skipping source prep (nothing to build)" - fi - - # ubuntu-22.04 is x64 (glibc 2.35). arm64 only has ubuntu-24.04-arm - # available on GitHub-hosted runners (glibc 2.39). arm64 is cuda13-only - # (no cuda12 SBSA) and ships the single "portable" coverage class. - # cuda12 installs via Jimver; cuda13 is pinned to 13.3 (matching - # upstream) which Jimver lacks, so those install via NVIDIA redist in - # the build children -- on the same runners, so the glibc floor holds. - ALL='[ - {"profile":"cuda12-older", "arch":"x64", "runner":"ubuntu-22.04", "line":"cuda12","klass":"older", "rank":10,"cuda":"12.8.0","toolkit_line":"12.8","archs":"70 75 80 86 89"}, - {"profile":"cuda12-newer", "arch":"x64", "runner":"ubuntu-22.04", "line":"cuda12","klass":"newer", "rank":20,"cuda":"12.8.0","toolkit_line":"12.8","archs":"86 89 90 100 120"}, - {"profile":"cuda12-portable","arch":"x64", "runner":"ubuntu-22.04", "line":"cuda12","klass":"portable","rank":30,"cuda":"12.8.0","toolkit_line":"12.8","archs":"70 75 80 86 89 90 100 120"}, - {"profile":"cuda13-older", "arch":"x64", "runner":"ubuntu-22.04", "line":"cuda13","klass":"older", "rank":40,"cuda":"13.3","toolkit_line":"13.3","archs":"75 80 86 89"}, - {"profile":"cuda13-newer", "arch":"x64", "runner":"ubuntu-22.04", "line":"cuda13","klass":"newer", "rank":50,"cuda":"13.3","toolkit_line":"13.3","archs":"86 89 90 100 120"}, - {"profile":"cuda13-portable","arch":"x64", "runner":"ubuntu-22.04", "line":"cuda13","klass":"portable","rank":60,"cuda":"13.3","toolkit_line":"13.3","archs":"75 80 86 89 90 100 120"}, - {"profile":"cuda13-portable","arch":"arm64","runner":"ubuntu-24.04-arm","line":"cuda13","klass":"portable","rank":60,"cuda":"13.3","toolkit_line":"13.3","archs":"90 100 120 121"} - ]' - if [ "$ONLY" = "all" ]; then - CUDA_INCLUDE="$(echo "$ALL" | jq -c .)" - else - CUDA_INCLUDE="$(echo "$ALL" | jq -c --arg p "$ONLY" '[.[] | select(.profile==$p)]')" - fi - # CUDA Windows reuses the x64 profiles (same arch lists / CUDA - # versions), just on a Windows runner. arm64 has no CUDA Windows target. - WIN_CUDA_INCLUDE="$(echo "$CUDA_INCLUDE" | jq -c '[.[] | select(.arch=="x64") | .runner="windows-2022"]')" - [ -n "$GFX" ] || { echo "refusing empty gfx_target (would publish a CUDA-only release labeled CUDA + ROCm)" >&2; exit 1; } - ROCM_MATRIX="$(jq -cn --arg g "$GFX" '{gfx_target: ($g | split(",") | map(gsub("^\\s+|\\s+$"; "")))}')" - - # macOS slices are static: two fixed runners with per-slice deployment - # targets. arm64 builds on macos-26 (newest Metal SDK; avoids the - # M5/A19 "error compiling source" the macos-14 SDK emits) yet pins - # minos 14.0 via deploy_target, so it still loads on macOS 14+. x64 - # pins 13.3 (matches upstream's Intel leg, covers 2017 Intel Macs - # capped at Ventura). - MACOS_INCLUDE='[ - {"build":"arm64","runner":"macos-26", "expect_arch":"arm64", "deploy_target":"14.0","defines":"-DGGML_METAL_USE_BF16=ON -DGGML_METAL_EMBED_LIBRARY=ON"}, - {"build":"x64", "runner":"macos-15-intel","expect_arch":"x86_64","deploy_target":"13.3","defines":"-DGGML_METAL=OFF"} - ]' - MACOS_INCLUDE="$(echo "$MACOS_INCLUDE" | jq -c .)" - - { - echo "tag=$TAG" - echo "repo=$REPO" - echo "base=$BASE" - echo "prs=$PRS" - echo "source_artifact=$SRC_ARTIFACT" - echo "commit=$COMMIT" - echo "exists=$EXISTS" - echo "cuda_matrix={\"include\":$CUDA_INCLUDE}" - echo "win_cuda_matrix={\"include\":$WIN_CUDA_INCLUDE}" - echo "rocm_matrix=$ROCM_MATRIX" - echo "macos_matrix={\"include\":$MACOS_INCLUDE}" - } >> "$GITHUB_OUTPUT" - echo "Resolved $REQ -> $TAG ($COMMIT); prs=$PRS; source_artifact=${SRC_ARTIFACT:-none}; release exists=$EXISTS; only=$ONLY; gfx=$GFX" - - # The stamped source tree (every build): every build child extracts this - # instead of cloning, and assemble ships it as the release's source-tarball - # asset, so a source build reproduces the same fingerprinted binary. Only - # produced when we build, so guard on the same condition as the build jobs - # (resolve skips source prep on a scheduled no-op, leaving no tarball). - - name: Upload source artifact - if: ${{ steps.r.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: actions/upload-artifact@v6 - with: - name: ${{ steps.r.outputs.source_artifact }} - path: ${{ runner.temp }}/llama.cpp-source-${{ steps.r.outputs.tag }}.tar.gz - if-no-files-found: error - retention-days: 7 - - build-cuda: - name: CUDA - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-cuda.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - commit: ${{ needs.resolve.outputs.commit }} - matrix: ${{ needs.resolve.outputs.cuda_matrix }} - - build-windows-cuda: - name: CUDA Windows - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-cuda-windows.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - commit: ${{ needs.resolve.outputs.commit }} - matrix: ${{ needs.resolve.outputs.win_cuda_matrix }} - - build-rocm: - name: ROCm - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-rocm.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - matrix: ${{ needs.resolve.outputs.rocm_matrix }} - operating_systems: ${{ github.event.inputs.operating_systems || 'windows,ubuntu' }} - rocm_version: ${{ github.event.inputs.rocm_version || 'latest' }} - - build-macos: - name: macOS - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-macos.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - matrix: ${{ needs.resolve.outputs.macos_matrix }} - - build-cpu: - name: CPU - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-cpu.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - - build-vulkan: - name: Vulkan - needs: resolve - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/unsloth-prebuilt-vulkan.yml - with: - tag: ${{ needs.resolve.outputs.tag }} - repo: ${{ needs.resolve.outputs.repo }} - source_artifact: ${{ needs.resolve.outputs.source_artifact }} - - assemble: - name: Assemble metadata + publish - needs: [resolve, build-cuda, build-windows-cuda, build-rocm, build-macos, build-cpu, build-vulkan] - # No `if: always()`. Atomicity: if any matrix entry in any child - # workflow failed, the corresponding `build-*` job reports failure, the - # default `needs` success check skips this assemble job, and nothing is - # published. The installer needs the full bundle set or it'll fail. - if: ${{ needs.resolve.outputs.exists != 'true' || github.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-22.04 - env: - GH_TOKEN: ${{ github.token }} - steps: - - name: Checkout build tooling (this repo) - uses: actions/checkout@v6 - with: - path: tooling - - # The first download attempt occasionally hits a transient ECONNRESET - # on GitHub's ListArtifacts API. Retrying once is cheaper than - # re-running the whole hour-long build matrix. - - name: Download built bundles - id: download - uses: actions/download-artifact@v6 - continue-on-error: true - with: - path: dist - pattern: app-* - merge-multiple: true - - - name: Download built bundles (retry) - if: steps.download.outcome == 'failure' - uses: actions/download-artifact@v6 - with: - path: dist - pattern: app-* - merge-multiple: true - - # Fingerprint gate: every bundle must carry the fingerprint the build children - # fold into BUILD_TARGET (-> LLAMA_BUILD_TARGET, printed by `--version`). That - # string is compiled into common (build-info.cpp); on Linux/macOS it lands in - # llama-server's own .rodata, but on a Windows shared build it can live in the - # sibling llama-common DLL instead -- so scan every file in the bundle, not - # just the exe. Nothing but a compiled binary carries this string (no metadata - # file does), so a hit means the brand shipped. Scan bytes rather than running - # `--version`: cross-compiled (ROCm) and Windows binaries can't run on this - # Linux runner. MARK must stay byte-identical to the resolve job's stamp string. - # Refuse to publish if any bundle is unbranded. - - name: Verify Unsloth fingerprint in every bundle - run: | - set -euo pipefail - MARK='Compiled by the Unsloth team' - shopt -s nullglob - tmp="$(mktemp -d)" - fail=0 - checked=0 - for arc in dist/app-*.tar.gz dist/app-*.zip dist/llama-*-bin-macos-*.tar.gz; do - d="$tmp/extract" - rm -rf "$d"; mkdir -p "$d" - case "$arc" in - *.zip) unzip -qo "$arc" -d "$d" ;; - *.tar.gz) tar -xzf "$arc" -C "$d" ;; - esac - if grep -arq "$MARK" "$d"; then - checked=$((checked + 1)) - else - echo "ERROR: $(basename "$arc"): no file carries the Unsloth fingerprint" >&2; fail=1 - fi - done - rm -rf "$tmp" - [ "$checked" -gt 0 ] || { echo "ERROR: no bundles found to verify (expected app-*/llama-*-bin-macos-* in dist/)" >&2; exit 1; } - [ "$fail" = 0 ] || { echo "ERROR: refusing to publish unbranded binaries" >&2; exit 1; } - echo "fingerprint verified in $checked bundles" - - # Ship the stamped source tree as release assets so the installer's - # source-build fallback reproduces the same fingerprinted binary. The resolve - # job's app-source-* artifact lands the tag-named tarball in dist/ (via the - # app-* download above); copy it to the commit name too. Both are the exact - # local bytes assemble_metadata hashes into the sha256 index. - - name: Fetch source archives - run: | - set -eux - TAG='${{ needs.resolve.outputs.tag }}' - SHA='${{ needs.resolve.outputs.commit }}' - cp "dist/llama.cpp-source-${TAG}.tar.gz" "dist/llama.cpp-source-commit-${SHA}.tar.gz" - - - name: Generate manifest + sha256 index - # prs carries upstream PR titles (arbitrary text), so pass it through env - # rather than inlining it: a title with a quote would otherwise break out - # of the shell command. Same handling in the publish step below. - env: - PRS_JSON: ${{ needs.resolve.outputs.prs }} - run: | - set -eux - python3 tooling/scripts/unsloth/assemble_metadata.py \ - --tag '${{ needs.resolve.outputs.tag }}' \ - --source-repo '${{ needs.resolve.outputs.repo }}' \ - --base-tag '${{ needs.resolve.outputs.base }}' \ - --pr-set "$PRS_JSON" \ - --commit '${{ needs.resolve.outputs.commit }}' \ - --dist dist --out dist \ - --publish-repo "$GITHUB_REPOSITORY" - ls -la dist - - - name: Upload full release set (artifacts) - uses: actions/upload-artifact@v6 - with: - name: unsloth-prebuilt-${{ needs.resolve.outputs.tag }} - path: dist/* - if-no-files-found: error - # Short retention: the published GitHub release is the canonical - # storage; this artifact is just debug fallback for failed-publish - # runs. Default 90 days × ~8 GB/run is real storage cost. - retention-days: 7 - - - name: Verify full bundle coverage before publish - if: ${{ (github.event_name == 'schedule' || inputs.publish) && needs.resolve.outputs.exists != 'true' }} - run: | - set -eu - TAG='${{ needs.resolve.outputs.tag }}' - fail=0 - # A partial CUDA set (cuda13 without cuda12) silently strands the - # cuda12-runtime majority; require matching x64 coverage on both lines. - for os in linux windows; do - ext=$([ "$os" = windows ] && echo zip || echo tar.gz) - c12=$(ls dist/app-*-"$os"-x64-cuda12-*."$ext" 2>/dev/null | sed -E 's/.*cuda12-(older|newer|portable)\..*/\1/' | sort -u | paste -sd, -) - c13=$(ls dist/app-*-"$os"-x64-cuda13-*."$ext" 2>/dev/null | sed -E 's/.*cuda13-(older|newer|portable)\..*/\1/' | sort -u | paste -sd, -) - echo "$os x64 coverage: cuda12=[$c12] cuda13=[$c13]" - [ -n "$c12" ] && [ "$c12" = "$c13" ] || { echo "ERROR: $os x64 cuda12/cuda13 coverage mismatch" >&2; fail=1; } - done - # Children passing is not the same as files landing in dist/ (a - # download-artifact anomaly leaves green jobs and missing bundles), - # so assert presence of every non-CUDA-x64 line the installer - # selects from. The resolve-time input guard already pins publish - # runs to the full default matrix, so these names are exact. - for f in \ - "llama-${TAG}-bin-macos-arm64.tar.gz" \ - "llama-${TAG}-bin-macos-x64.tar.gz" \ - "app-${TAG}-linux-arm64-cuda13-portable.tar.gz" \ - "app-${TAG}-linux-x64-cpu.tar.gz" \ - "app-${TAG}-windows-x64-cpu.zip" \ - "app-${TAG}-linux-arm64-cpu.tar.gz" \ - "app-${TAG}-windows-arm64-cpu.zip" \ - "app-${TAG}-linux-x64-vulkan.tar.gz" \ - "app-${TAG}-windows-x64-vulkan.zip"; do - [ -s "dist/$f" ] || { echo "ERROR: missing $f in dist/" >&2; fail=1; } - done - # Default ROCm set (mirrors the gfx_target input default): both OSes - # per family, or AMD hosts of that family silently lose their bundle. - for gfx in gfx1151 gfx1150 gfx120X gfx110X gfx103X gfx90a gfx908; do - [ -s "dist/app-${TAG}-linux-x64-rocm-${gfx}.tar.gz" ] || { echo "ERROR: missing linux rocm ${gfx} bundle in dist/" >&2; fail=1; } - [ -s "dist/app-${TAG}-windows-x64-rocm-${gfx}.zip" ] || { echo "ERROR: missing windows rocm ${gfx} bundle in dist/" >&2; fail=1; } - done - [ "$fail" = 0 ] || { echo "ERROR: refusing to publish a partial release" >&2; exit 1; } - - - name: Publish GitHub release - if: ${{ (github.event_name == 'schedule' || inputs.publish) && needs.resolve.outputs.exists != 'true' }} - # prs carries upstream PR titles (arbitrary text), so pass it through env - # rather than inlining it: a title with a quote would otherwise break out - # of the shell command. - env: - PRS_JSON: ${{ needs.resolve.outputs.prs }} - run: | - set -eux - TAG='${{ needs.resolve.outputs.tag }}' - REPO="$GITHUB_REPOSITORY" - PRS="$PRS_JSON" - BASE='${{ needs.resolve.outputs.base }}' - # Link the base to the upstream release tag (always resolves). Each PR - # line is " (#<n>, commit <sha>)": GitHub does not expand a bare - # reference into the PR title inline (only on hover), so we bake the - # title in ourselves. #<n> links to the upstream PR (full URL, so it - # resolves to upstream no matter which repo hosts the release, and - # GitHub still attaches its hovercard), and <sha> links to that pin's - # commit-in-PR URL. We deliberately do not link the merged commit: in - # mix mode it is a throwaway merge made on the runner and never pushed - # anywhere, so any repo@sha URL for it 404s. - NOTES="Automated Unsloth llama.cpp CUDA + ROCm + Vulkan + macOS + CPU prebuild for upstream [${BASE}](https://github.com/ggml-org/llama.cpp/releases/tag/${BASE})" - if [ "$(jq length <<<"$PRS")" = 0 ]; then - NOTES="${NOTES}." - else - PR_LIST="$(jq -r 'map("- \(.title) ([#\(.number)](https://github.com/ggml-org/llama.cpp/pull/\(.number)), commit [\(.sha[0:7])](\(.url)))") | join("\n")' <<<"$PRS")" - NOTES="$(printf '%s, merged with:\n\n%s' "$NOTES" "$PR_LIST")" - fi - - # Atomic publish: upload as draft (hidden from the anon GitHub API - # the installer uses), then flip draft=false only once every asset - # landed. Leftover drafts from failed runs are detected as "not - # exists" by resolve and rebuilt on the next run. - if [ "$(gh release view "$TAG" --repo "$REPO" --json isDraft --jq .isDraft 2>/dev/null || true)" = "true" ]; then - gh release delete "$TAG" --repo "$REPO" --yes - fi - gh release create "$TAG" --repo "$REPO" --draft \ - --title "llama.cpp prebuilt $TAG" \ - --notes "$NOTES" \ - dist/* - gh release edit "$TAG" --repo "$REPO" --draft=false diff --git a/.github/workflows/ztest-arm64-live-gen.yml b/.github/workflows/ztest-arm64-live-gen.yml new file mode 100644 index 00000000000..efa81863714 --- /dev/null +++ b/.github/workflows/ztest-arm64-live-gen.yml @@ -0,0 +1,95 @@ +# Throwaway: do not merge. Runs the fork's CPU bundles on real runners of each +# arch (x86_64 + arm64, Linux + Windows) and asserts llama-server actually +# loads a tiny GGUF and generates a token stream. Downloads the shipped release +# asset directly (not built here), so this tests exactly what users get. + +name: "ztest: CPU bundle live generation" + +on: + push: + branches: [test-arm64-live-gen] + +permissions: + contents: read + +env: + REL: b9899-mix-5dd3721 + MODEL_URL: https://huggingface.co/unsloth/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf + PROMPT: '{"prompt":"The capital of France is","n_predict":24,"temperature":0.3,"cache_prompt":false}' + +jobs: + linux: + name: linux/${{ matrix.arch }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - { arch: x64, runner: ubuntu-22.04, asset: linux-x64-cpu.tar.gz } + - { arch: arm64, runner: ubuntu-24.04-arm, asset: linux-arm64-cpu.tar.gz } + steps: + - name: Download bundle + model + run: | + set -eux + uname -m + curl -sSfL -o bundle.tar.gz "https://github.com/unslothai/llama.cpp/releases/download/${REL}/app-${REL}-${{ matrix.asset }}" + mkdir bundle && tar -xzf bundle.tar.gz -C bundle + curl -sSfL -o model.gguf "$MODEL_URL" + file bundle/llama-server + - name: Version + generate + run: | + set -eux + ./bundle/llama-server --version + nohup ./bundle/llama-server -m model.gguf --host 127.0.0.1 --port 8080 -t 4 -c 1024 > server.log 2>&1 & + for i in $(seq 1 90); do + code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/health || true) + [ "$code" = "200" ] && break + sleep 2 + done + resp=$(curl -sS http://127.0.0.1:8080/completion -H 'Content-Type: application/json' -d "$PROMPT") + echo "RESPONSE: $resp" + echo "$resp" | python3 -c "import json,sys; d=json.load(sys.stdin); c=d.get('content',''); print('GENERATED:', repr(c)); assert c.strip(), 'empty generation'; print('OK tokens_predicted=', d.get('tokens_predicted'))" + echo '--- server.log tail ---'; tail -20 server.log + + windows: + name: windows/${{ matrix.arch }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - { arch: x64, runner: windows-2025, asset: windows-x64-cpu.zip } + - { arch: arm64, runner: windows-11-arm, asset: windows-arm64-cpu.zip } + defaults: + run: + shell: pwsh + steps: + - name: Download bundle + model + run: | + curl.exe -sSfL -o bundle.zip "https://github.com/unslothai/llama.cpp/releases/download/$env:REL/app-$env:REL-${{ matrix.asset }}" + Expand-Archive -Path bundle.zip -DestinationPath bundle -Force + curl.exe -sSfL -o model.gguf "$env:MODEL_URL" + - name: Version + generate + run: | + & bundle\llama-server.exe --version + if ($LASTEXITCODE -ne 0) { Write-Error "llama-server --version failed: $LASTEXITCODE"; exit 1 } + $p = Start-Process -FilePath bundle\llama-server.exe ` + -ArgumentList '-m','model.gguf','--host','127.0.0.1','--port','8080','-t','4','-c','1024' ` + -NoNewWindow -PassThru -RedirectStandardOutput server.out.log -RedirectStandardError server.err.log + $ok = $false + foreach ($i in 1..90) { + Start-Sleep -Seconds 2 + if ($p.HasExited) { break } + try { if ((Invoke-WebRequest http://127.0.0.1:8080/health -UseBasicParsing -TimeoutSec 3).StatusCode -eq 200) { $ok = $true; break } } catch {} + } + if (-not $ok) { + Write-Host '--- server.err.log ---'; Get-Content server.err.log -ErrorAction SilentlyContinue + Write-Error 'server did not become healthy'; exit 1 + } + $resp = Invoke-RestMethod -Uri http://127.0.0.1:8080/completion -Method Post -ContentType 'application/json' -Body $env:PROMPT + Write-Host "GENERATED:" $resp.content + if ([string]::IsNullOrWhiteSpace([string]$resp.content)) { Write-Error 'empty generation'; exit 1 } + Write-Host "OK tokens_predicted=" $resp.tokens_predicted + if (-not $p.HasExited) { $p.Kill() }