|
| 1 | +#!/usr/bin/env pwsh |
| 2 | +# Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | +# All rights reserved. |
| 4 | +# |
| 5 | +# This source code is licensed under the BSD-style license found in the |
| 6 | +# LICENSE file in the root directory of this source tree. |
| 7 | + |
| 8 | +param( |
| 9 | + [Parameter(Mandatory = $true)] |
| 10 | + [string]$Device, |
| 11 | + [Parameter(Mandatory = $true)] |
| 12 | + [string]$HfModel, |
| 13 | + [Parameter(Mandatory = $true)] |
| 14 | + [string]$QuantName, |
| 15 | + [string]$ModelDir = ".", |
| 16 | + [string]$ExpectedCudaVersion = "" |
| 17 | +) |
| 18 | + |
| 19 | +Set-StrictMode -Version Latest |
| 20 | +$ErrorActionPreference = "Stop" |
| 21 | +$PSNativeCommandUseErrorActionPreference = $true |
| 22 | +$ProgressPreference = "SilentlyContinue" |
| 23 | + |
| 24 | +if ($Device -ne "cuda-windows") { |
| 25 | + throw "Unsupported device '$Device'. Expected 'cuda-windows'." |
| 26 | +} |
| 27 | + |
| 28 | +Write-Host "Testing model: $HfModel (quantization: $QuantName)" |
| 29 | + |
| 30 | +$resolvedModelDir = (Resolve-Path -Path $ModelDir).Path |
| 31 | +$modelPte = Join-Path -Path $resolvedModelDir -ChildPath "model.pte" |
| 32 | +$cudaBlob = Join-Path -Path $resolvedModelDir -ChildPath "aoti_cuda_blob.ptd" |
| 33 | + |
| 34 | +if (-not (Test-Path -Path $modelPte -PathType Leaf)) { |
| 35 | + throw "model.pte not found in '$resolvedModelDir'" |
| 36 | +} |
| 37 | +if (-not (Test-Path -Path $cudaBlob -PathType Leaf)) { |
| 38 | + throw "aoti_cuda_blob.ptd not found in '$resolvedModelDir'" |
| 39 | +} |
| 40 | + |
| 41 | +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path |
| 42 | +$executorchRoot = (Resolve-Path -Path (Join-Path -Path $scriptDir -ChildPath "..\..")).Path |
| 43 | + |
| 44 | +switch ($HfModel) { |
| 45 | + "mistralai/Voxtral-Mini-3B-2507" { |
| 46 | + $runnerTarget = "voxtral_runner" |
| 47 | + $runnerPath = "voxtral" |
| 48 | + $runnerPreset = "voxtral-cuda" |
| 49 | + $expectedOutput = "identity" |
| 50 | + $preprocessor = "voxtral_preprocessor.pte" |
| 51 | + $tokenizerUrl = "https://huggingface.co/mistralai/Voxtral-Mini-3B-2507/resolve/main" # @lint-ignore |
| 52 | + $tokenizerFile = "tekken.json" |
| 53 | + $audioUrl = "https://github.com/voxserv/audio_quality_testing_samples/raw/refs/heads/master/testaudio/16000/test01_20s.wav" |
| 54 | + $audioFile = "poem.wav" |
| 55 | + } |
| 56 | + "nvidia/parakeet-tdt" { |
| 57 | + $runnerTarget = "parakeet_runner" |
| 58 | + $runnerPath = "parakeet" |
| 59 | + $runnerPreset = "parakeet-cuda" |
| 60 | + $expectedOutput = "Phoebe" |
| 61 | + $preprocessor = "" |
| 62 | + $tokenizerUrl = "" |
| 63 | + $tokenizerFile = "tokenizer.model" |
| 64 | + $audioUrl = "https://dldata-public.s3.us-east-2.amazonaws.com/2086-149220-0033.wav" |
| 65 | + $audioFile = "test_audio.wav" |
| 66 | + } |
| 67 | + default { |
| 68 | + throw "Unsupported model '$HfModel'. Supported: mistralai/Voxtral-Mini-3B-2507, nvidia/parakeet-tdt" |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +function Download-IfNeeded { |
| 73 | + param( |
| 74 | + [Parameter(Mandatory = $true)] |
| 75 | + [string]$Url, |
| 76 | + [Parameter(Mandatory = $true)] |
| 77 | + [string]$OutFile |
| 78 | + ) |
| 79 | + |
| 80 | + if (Test-Path -Path $OutFile -PathType Leaf) { |
| 81 | + Write-Host "Using existing file: $OutFile" |
| 82 | + return |
| 83 | + } |
| 84 | + Write-Host "Downloading $Url -> $OutFile" |
| 85 | + Invoke-WebRequest -Uri $Url -OutFile $OutFile |
| 86 | +} |
| 87 | + |
| 88 | +Push-Location $executorchRoot |
| 89 | +try { |
| 90 | + Write-Host "::group::Check CUDA toolchain" |
| 91 | + $nvccOutput = nvcc --version | Out-String |
| 92 | + Write-Host $nvccOutput |
| 93 | + nvidia-smi |
| 94 | + if (-not [string]::IsNullOrWhiteSpace($ExpectedCudaVersion)) { |
| 95 | + $versionMatch = [Regex]::Match($nvccOutput, "release\s+(\d+\.\d+)") |
| 96 | + if (-not $versionMatch.Success) { |
| 97 | + throw "Failed to parse CUDA version from nvcc output." |
| 98 | + } |
| 99 | + $actualCudaVersion = $versionMatch.Groups[1].Value |
| 100 | + if ($actualCudaVersion -ne $ExpectedCudaVersion) { |
| 101 | + throw "CUDA version mismatch. Expected: $ExpectedCudaVersion, Actual: $actualCudaVersion" |
| 102 | + } |
| 103 | + Write-Host "CUDA version check passed: $actualCudaVersion" |
| 104 | + } |
| 105 | + Write-Host "::endgroup::" |
| 106 | + |
| 107 | + Write-Host "::group::Build ExecuTorch (CUDA)" |
| 108 | + $numCores = [Math]::Max([Environment]::ProcessorCount - 1, 1) |
| 109 | + cmake --preset llm-release-cuda |
| 110 | + cmake --build cmake-out --target install --config Release -j $numCores |
| 111 | + Write-Host "::endgroup::" |
| 112 | + |
| 113 | + Write-Host "::group::Build $runnerTarget" |
| 114 | + Push-Location (Join-Path -Path $executorchRoot -ChildPath "examples\models\$runnerPath") |
| 115 | + try { |
| 116 | + cmake --preset $runnerPreset |
| 117 | + cmake --build (Join-Path -Path $executorchRoot -ChildPath "cmake-out\examples\models\$runnerPath") --target $runnerTarget --config Release -j $numCores |
| 118 | + } |
| 119 | + finally { |
| 120 | + Pop-Location |
| 121 | + } |
| 122 | + Write-Host "::endgroup::" |
| 123 | + |
| 124 | + Write-Host "::group::Prepare Artifacts" |
| 125 | + if ($preprocessor -ne "") { |
| 126 | + $preprocessorPath = Join-Path -Path $resolvedModelDir -ChildPath $preprocessor |
| 127 | + if (-not (Test-Path -Path $preprocessorPath -PathType Leaf)) { |
| 128 | + throw "Required preprocessor artifact not found: $preprocessorPath" |
| 129 | + } |
| 130 | + } |
| 131 | + if ($tokenizerFile -ne "") { |
| 132 | + $tokenizerPath = Join-Path -Path $resolvedModelDir -ChildPath $tokenizerFile |
| 133 | + if (-not (Test-Path -Path $tokenizerPath -PathType Leaf) -and $tokenizerUrl -eq "") { |
| 134 | + throw "Required tokenizer artifact not found: $tokenizerPath" |
| 135 | + } |
| 136 | + } |
| 137 | + if ($tokenizerUrl -ne "") { |
| 138 | + Download-IfNeeded -Url "$tokenizerUrl/$tokenizerFile" -OutFile (Join-Path -Path $resolvedModelDir -ChildPath $tokenizerFile) |
| 139 | + } |
| 140 | + if ($audioUrl -ne "") { |
| 141 | + Download-IfNeeded -Url $audioUrl -OutFile (Join-Path -Path $resolvedModelDir -ChildPath $audioFile) |
| 142 | + } |
| 143 | + Get-ChildItem -Path $resolvedModelDir |
| 144 | + Write-Host "::endgroup::" |
| 145 | + |
| 146 | + Write-Host "::group::Run $runnerTarget" |
| 147 | + $runnerExeCandidates = @( |
| 148 | + (Join-Path -Path $executorchRoot -ChildPath "cmake-out\examples\models\$runnerPath\Release\$runnerTarget.exe"), |
| 149 | + (Join-Path -Path $executorchRoot -ChildPath "cmake-out\examples\models\$runnerPath\$runnerTarget.exe") |
| 150 | + ) |
| 151 | + $runnerExe = $runnerExeCandidates | Where-Object { Test-Path -Path $_ -PathType Leaf } | Select-Object -First 1 |
| 152 | + if (-not $runnerExe) { |
| 153 | + throw "Runner executable not found. Checked: $($runnerExeCandidates -join ', ')" |
| 154 | + } |
| 155 | + |
| 156 | + $runnerArgs = @("--model_path", $modelPte, "--data_path", $cudaBlob) |
| 157 | + switch ($HfModel) { |
| 158 | + "mistralai/Voxtral-Mini-3B-2507" { |
| 159 | + $runnerArgs += @( |
| 160 | + "--temperature", "0", |
| 161 | + "--tokenizer_path", (Join-Path -Path $resolvedModelDir -ChildPath $tokenizerFile), |
| 162 | + "--audio_path", (Join-Path -Path $resolvedModelDir -ChildPath $audioFile), |
| 163 | + "--processor_path", (Join-Path -Path $resolvedModelDir -ChildPath $preprocessor) |
| 164 | + ) |
| 165 | + } |
| 166 | + "nvidia/parakeet-tdt" { |
| 167 | + $runnerArgs = @( |
| 168 | + "--model_path", $modelPte, |
| 169 | + "--audio_path", (Join-Path -Path $resolvedModelDir -ChildPath $audioFile), |
| 170 | + "--tokenizer_path", (Join-Path -Path $resolvedModelDir -ChildPath $tokenizerFile), |
| 171 | + "--data_path", $cudaBlob |
| 172 | + ) |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + $stdoutFile = Join-Path -Path $env:TEMP -ChildPath ("et_runner_stdout_{0}.log" -f ([Guid]::NewGuid().ToString("N"))) |
| 177 | + $stderrFile = Join-Path -Path $env:TEMP -ChildPath ("et_runner_stderr_{0}.log" -f ([Guid]::NewGuid().ToString("N"))) |
| 178 | + try { |
| 179 | + $proc = Start-Process ` |
| 180 | + -FilePath $runnerExe ` |
| 181 | + -ArgumentList $runnerArgs ` |
| 182 | + -NoNewWindow ` |
| 183 | + -Wait ` |
| 184 | + -PassThru ` |
| 185 | + -RedirectStandardOutput $stdoutFile ` |
| 186 | + -RedirectStandardError $stderrFile |
| 187 | + |
| 188 | + $stdout = if (Test-Path -Path $stdoutFile -PathType Leaf) { Get-Content -Path $stdoutFile -Raw } else { "" } |
| 189 | + $stderr = if (Test-Path -Path $stderrFile -PathType Leaf) { Get-Content -Path $stderrFile -Raw } else { "" } |
| 190 | + $output = @($stdout, $stderr) -join [Environment]::NewLine |
| 191 | + $exitCode = $proc.ExitCode |
| 192 | + } |
| 193 | + finally { |
| 194 | + Remove-Item -Path $stdoutFile -ErrorAction SilentlyContinue |
| 195 | + Remove-Item -Path $stderrFile -ErrorAction SilentlyContinue |
| 196 | + } |
| 197 | + Write-Host "Runner output:" |
| 198 | + Write-Host $output |
| 199 | + |
| 200 | + if ($exitCode -ne 0) { |
| 201 | + throw "Runner exited with code $exitCode`n$output" |
| 202 | + } |
| 203 | + |
| 204 | + if ($expectedOutput -ne "" -and $output -notmatch [Regex]::Escape($expectedOutput)) { |
| 205 | + throw "Expected output '$expectedOutput' not found in runner output" |
| 206 | + } |
| 207 | + Write-Host "Success: '$expectedOutput' found in output" |
| 208 | + Write-Host "::endgroup::" |
| 209 | +} |
| 210 | +finally { |
| 211 | + Pop-Location |
| 212 | +} |
0 commit comments