| 
 | 1 | +name: Test Metal Backend  | 
 | 2 | + | 
 | 3 | +on:  | 
 | 4 | +  pull_request:  | 
 | 5 | +  push:  | 
 | 6 | +    branches:  | 
 | 7 | +      - main  | 
 | 8 | +      - release/*  | 
 | 9 | + | 
 | 10 | +concurrency:  | 
 | 11 | +  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.event_name == 'workflow_dispatch' }}-${{ github.event_name == 'schedule' }}  | 
 | 12 | +  cancel-in-progress: false  | 
 | 13 | + | 
 | 14 | +jobs:  | 
 | 15 | +  test-metal-builds:  | 
 | 16 | +    name: test-executorch-metal-build  | 
 | 17 | +    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main  | 
 | 18 | +    with:  | 
 | 19 | +      runner: macos-m2-stable  | 
 | 20 | +      python-version: '3.11'  | 
 | 21 | +      submodules: 'recursive'  | 
 | 22 | +      ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}  | 
 | 23 | +      timeout: 90  | 
 | 24 | +      script: |  | 
 | 25 | +        set -eux  | 
 | 26 | +
  | 
 | 27 | +        echo "::group::Test ExecuTorch Metal build"  | 
 | 28 | +        PYTHON_EXECUTABLE=python CMAKE_ARGS="-DEXECUTORCH_BUILD_METAL=ON" ${CONDA_RUN} --no-capture-output ./install_executorch.sh  | 
 | 29 | +        echo "::endgroup::"  | 
 | 30 | +
  | 
 | 31 | +  export-voxtral-metal-artifact:  | 
 | 32 | +    name: export-voxtral-metal-artifact  | 
 | 33 | +    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main  | 
 | 34 | +    secrets: inherit  | 
 | 35 | +    with:  | 
 | 36 | +      runner: macos-m2-stable  | 
 | 37 | +      python-version: '3.11'  | 
 | 38 | +      submodules: 'recursive'  | 
 | 39 | +      ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}  | 
 | 40 | +      timeout: 90  | 
 | 41 | +      secrets-env: EXECUTORCH_HF_TOKEN  | 
 | 42 | +      upload-artifact: voxtral-metal-export  | 
 | 43 | +      script: |  | 
 | 44 | +        set -eux  | 
 | 45 | +
  | 
 | 46 | +        echo "::group::Setup Huggingface"  | 
 | 47 | +        ${CONDA_RUN} pip install -U "huggingface_hub[cli]" accelerate  | 
 | 48 | +        ${CONDA_RUN} huggingface-cli login --token $SECRET_EXECUTORCH_HF_TOKEN  | 
 | 49 | +        echo "::endgroup::"  | 
 | 50 | +
  | 
 | 51 | +        echo "::group::Setup Optimum-ExecuTorch"  | 
 | 52 | +        OPTIMUM_ET_VERSION=$(cat .ci/docker/ci_commit_pins/optimum-executorch.txt)  | 
 | 53 | +        echo "Using optimum-executorch version: ${OPTIMUM_ET_VERSION}"  | 
 | 54 | +        ${CONDA_RUN} pip install git+https://github.com/huggingface/optimum-executorch.git@${OPTIMUM_ET_VERSION}  | 
 | 55 | +        ${CONDA_RUN} pip install mistral-common librosa  | 
 | 56 | +        echo "::endgroup::"  | 
 | 57 | +
  | 
 | 58 | +        echo "::group::Setup ExecuTorch"  | 
 | 59 | +        PYTHON_EXECUTABLE=python ${CONDA_RUN} ./install_executorch.sh  | 
 | 60 | +        echo "::endgroup::"  | 
 | 61 | +
  | 
 | 62 | +        echo "::group::Pip List"  | 
 | 63 | +        ${CONDA_RUN} pip list  | 
 | 64 | +        echo "::endgroup::"  | 
 | 65 | +
  | 
 | 66 | +        echo "::group::Export Voxtral"  | 
 | 67 | +        ${CONDA_RUN} optimum-cli export executorch \  | 
 | 68 | +            --model "mistralai/Voxtral-Mini-3B-2507" \  | 
 | 69 | +            --task "multimodal-text-to-text" \  | 
 | 70 | +            --recipe "metal" \  | 
 | 71 | +            --dtype bfloat16 \  | 
 | 72 | +            --max_seq_len 1024 \  | 
 | 73 | +            --output_dir ./  | 
 | 74 | +        ${CONDA_RUN} python -m executorch.extension.audio.mel_spectrogram \  | 
 | 75 | +            --feature_size 128 \  | 
 | 76 | +            --stack_output \  | 
 | 77 | +            --max_audio_len 300 \  | 
 | 78 | +            --output_file voxtral_preprocessor.pte  | 
 | 79 | +
  | 
 | 80 | +        test -f model.pte  | 
 | 81 | +        test -f aoti_metal_blob.ptd  | 
 | 82 | +        test -f voxtral_preprocessor.pte  | 
 | 83 | +        echo "::endgroup::"  | 
 | 84 | +
  | 
 | 85 | +        echo "::group::Store Voxtral Artifacts"  | 
 | 86 | +        mkdir -p "${RUNNER_ARTIFACT_DIR}"  | 
 | 87 | +        cp model.pte "${RUNNER_ARTIFACT_DIR}/"  | 
 | 88 | +        cp aoti_metal_blob.ptd "${RUNNER_ARTIFACT_DIR}/"  | 
 | 89 | +        cp voxtral_preprocessor.pte "${RUNNER_ARTIFACT_DIR}/"  | 
 | 90 | +        ls -al "${RUNNER_ARTIFACT_DIR}"  | 
 | 91 | +        echo "::endgroup::"  | 
 | 92 | +
  | 
 | 93 | +  test-voxtral-metal-e2e:  | 
 | 94 | +    name: test-voxtral-metal-e2e  | 
 | 95 | +    needs: export-voxtral-metal-artifact  | 
 | 96 | +    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main  | 
 | 97 | +    with:  | 
 | 98 | +      runner: macos-m2-stable  | 
 | 99 | +      python-version: '3.11'  | 
 | 100 | +      submodules: 'recursive'  | 
 | 101 | +      ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}  | 
 | 102 | +      timeout: 90  | 
 | 103 | +      download-artifact: voxtral-metal-export  | 
 | 104 | +      script: |  | 
 | 105 | +        set -eux  | 
 | 106 | +
  | 
 | 107 | +        echo "::group::Print machine info"  | 
 | 108 | +        uname -a  | 
 | 109 | +        if [ $(uname -s) == Darwin ]; then  | 
 | 110 | +          sw_vers  | 
 | 111 | +          # Print RAM in GB  | 
 | 112 | +          RAM_BYTES=$(sysctl -n hw.memsize)  | 
 | 113 | +          RAM_GB=$(echo "scale=2; $RAM_BYTES/1024/1024/1024" | bc)  | 
 | 114 | +          echo "Available RAM (GB): $RAM_GB"  | 
 | 115 | +          sysctl machdep.cpu.brand_string  | 
 | 116 | +          sysctl machdep.cpu.core_count  | 
 | 117 | +          # Print number of GPU cores (Apple Silicon)  | 
 | 118 | +          if command -v system_profiler &> /dev/null; then  | 
 | 119 | +            GPU_CORES=$(system_profiler SPDisplaysDataType | awk '/Total Number of Cores/ {print $5; exit}')  | 
 | 120 | +            if [ -z "$GPU_CORES" ]; then  | 
 | 121 | +              # Fallback: try to parse "Core Count" from Apple GPU section  | 
 | 122 | +              GPU_CORES=$(system_profiler SPDisplaysDataType | awk '/Core Count/ {print $3; exit}')  | 
 | 123 | +            fi  | 
 | 124 | +            echo "GPU Cores: ${GPU_CORES:-Unknown}"  | 
 | 125 | +          else  | 
 | 126 | +            echo "system_profiler not available, cannot determine GPU cores."  | 
 | 127 | +          fi  | 
 | 128 | +        fi  | 
 | 129 | +        echo "::endgroup::"  | 
 | 130 | +
  | 
 | 131 | +        echo "::group::Setup ExecuTorch Requirements"  | 
 | 132 | +        CMAKE_ARGS="-DEXECUTORCH_BUILD_METAL=ON" ${CONDA_RUN} --no-capture-output ./install_requirements.sh  | 
 | 133 | +        echo "::endgroup::"  | 
 | 134 | +
  | 
 | 135 | +        echo "::group::Pip List"  | 
 | 136 | +        ${CONDA_RUN} pip list  | 
 | 137 | +        echo "::endgroup::"  | 
 | 138 | +
  | 
 | 139 | +        echo "::group::Prepare Voxtral Artifacts"  | 
 | 140 | +        cp "${RUNNER_ARTIFACT_DIR}/model.pte" .  | 
 | 141 | +        cp "${RUNNER_ARTIFACT_DIR}/aoti_metal_blob.ptd" .  | 
 | 142 | +        cp "${RUNNER_ARTIFACT_DIR}/voxtral_preprocessor.pte" .  | 
 | 143 | +        TOKENIZER_URL="https://huggingface.co/mistralai/Voxtral-Mini-3B-2507/resolve/main/tekken.json"  | 
 | 144 | +        curl -L $TOKENIZER_URL -o tekken.json  | 
 | 145 | +        ls -al model.pte aoti_metal_blob.ptd voxtral_preprocessor.pte tekken.json  | 
 | 146 | +        echo "::endgroup::"  | 
 | 147 | +
  | 
 | 148 | +        echo "::group::Create Test Audio File"  | 
 | 149 | +        say -o call_samantha_hall.aiff "Call Samantha Hall"  | 
 | 150 | +        afconvert -f WAVE -d LEI16 call_samantha_hall.aiff call_samantha_hall.wav  | 
 | 151 | +        echo "::endgroup::"  | 
 | 152 | +
  | 
 | 153 | +        echo "::group::Build Voxtral Runner"  | 
 | 154 | +        ${CONDA_RUN} cmake --preset llm \  | 
 | 155 | +              -DEXECUTORCH_BUILD_METAL=ON \  | 
 | 156 | +              -DCMAKE_INSTALL_PREFIX=cmake-out \  | 
 | 157 | +              -DCMAKE_BUILD_TYPE=Release \  | 
 | 158 | +              -Bcmake-out -S.  | 
 | 159 | +        ${CONDA_RUN} cmake --build cmake-out -j$(( $(sysctl -n hw.ncpu) - 1 )) --target install --config Release  | 
 | 160 | +
  | 
 | 161 | +        ${CONDA_RUN} cmake -DEXECUTORCH_BUILD_METAL=ON \  | 
 | 162 | +              -DCMAKE_BUILD_TYPE=Release \  | 
 | 163 | +              -Sexamples/models/voxtral \  | 
 | 164 | +              -Bcmake-out/examples/models/voxtral/  | 
 | 165 | +        ${CONDA_RUN} cmake --build cmake-out/examples/models/voxtral --target voxtral_runner --config Release  | 
 | 166 | +        echo "::endgroup::"  | 
 | 167 | +
  | 
 | 168 | +        echo "::group::Run Voxtral Runner"  | 
 | 169 | +        set +e  | 
 | 170 | +        OUTPUT=$(cmake-out/examples/models/voxtral/voxtral_runner \  | 
 | 171 | +              --model_path model.pte \  | 
 | 172 | +              --data_path aoti_metal_blob.ptd \  | 
 | 173 | +              --tokenizer_path tekken.json \  | 
 | 174 | +              --audio_path call_samantha_hall.wav \  | 
 | 175 | +              --processor_path voxtral_preprocessor.pte \  | 
 | 176 | +              --temperature 0 2>&1)  | 
 | 177 | +        EXIT_CODE=$?  | 
 | 178 | +        set -e  | 
 | 179 | +
  | 
 | 180 | +        echo "$OUTPUT"  | 
 | 181 | +
  | 
 | 182 | +        if ! echo "$OUTPUT" | grep -iq "Samantha"; then  | 
 | 183 | +          echo "Expected output 'Samantha' not found in output"  | 
 | 184 | +          exit 1  | 
 | 185 | +        fi  | 
 | 186 | +
  | 
 | 187 | +        if [ $EXIT_CODE -ne 0 ]; then  | 
 | 188 | +          echo "Unexpected exit code: $EXIT_CODE"  | 
 | 189 | +          exit $EXIT_CODE  | 
 | 190 | +        fi  | 
 | 191 | +        echo "::endgroup::"  | 
0 commit comments