Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions .pipelines/templates/fetch-test-data-from-blob.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Download test model data from blob storage into a local directory.
# Uses azcopy with AZCLI auth so the agent identity can read from storage.

parameters:
- name: destinationPath
type: string
default: '$(Build.SourcesDirectory)/test-data-shared'

steps:
- task: AzureCLI@2
displayName: 'Fetch test data from blob'
inputs:
azureSubscription: 'ortcibuild_readonly_mi2-ONNX Runtime-AIFoundryLocal'
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
$ErrorActionPreference = 'Stop'

$destination = '${{ parameters.destinationPath }}'
$storageAccount = 'foundrylocalmodels'
$container = 'models'
$prefix = 'foundrylocal/models'

if (-not (Get-Command azcopy -ErrorAction SilentlyContinue)) {
if ($IsMacOS) {
Write-Host 'azcopy not found on macOS agent. Installing via Homebrew...'
if (-not (Get-Command brew -ErrorAction SilentlyContinue)) {
throw 'Homebrew is required to install azcopy on macOS agents, but brew was not found.'
}

brew update --quiet
brew install --quiet azcopy
} else {
throw 'azcopy is not installed on this agent.'
}
}

azcopy --version

New-Item -ItemType Directory -Path $destination -Force | Out-Null
$publisherRoot = Join-Path $destination 'Microsoft'
New-Item -ItemType Directory -Path $publisherRoot -Force | Out-Null

function Invoke-AzCopy {
param(
[string]$Source,
[string]$Target
)

$parent = Split-Path -Path $Target -Parent
if (-not [string]::IsNullOrEmpty($parent)) {
New-Item -ItemType Directory -Path $parent -Force | Out-Null
}

$args = @('copy', $Source, $Target, '--recursive')

& azcopy @args
if ($LASTEXITCODE -ne 0) {
throw "azcopy failed downloading test data from $Source"
}
}

function Generate-InferenceModelJson {
param(
[string]$ModelRoot,
[string]$TargetAlias
)

if ($TargetAlias -notmatch '-\d+$') {
throw "TargetAlias '$TargetAlias' does not end with a numeric version suffix."
}

# Convert '<name>-<version>' to '<name>:<version>' by replacing the last '-'.
$modelId = $TargetAlias -replace '-(?=[^-]+$)', ':'

$inferenceModelPath = Join-Path $ModelRoot 'inference_model.json'
@{ Name = $modelId } |
ConvertTo-Json -Depth 2 |
Set-Content -Path $inferenceModelPath -Encoding utf8

Write-Host "Generated inference_model.json: $inferenceModelPath (Name=$modelId)"
}

# The active SDK tests resolve versioned model aliases in cache.
# Keep explicit version per model, then derive target alias and v{N} path.
$modelMappings = @(
@{
SourcePath = 'qwen2.5-0.5b-instruct'
Version = 4
},
@{
SourcePath = 'openai-whisper-tiny'
Version = 4
},
@{
SourcePath = 'nemotron-speech-streaming-en-0.6b'
Version = 3
},
@{
SourcePath = 'qwen3-embedding-0.6b'
Version = 1
},
@{
SourcePath = 'qwen3.5-0.8b'
Version = 2
},
@{
SourcePath = 'deepseek-r1-distill-qwen-14b'
Version = 4
}
)

foreach ($model in $modelMappings) {
$targetAlias = "$($model.SourcePath)-generic-cpu-$($model.Version)"
$requestedUrl = "https://$storageAccount.blob.core.windows.net/$container/$prefix/$($model.SourcePath)/onnx/cpu_and_mobile/v$($model.Version)/*"
Write-Host "Requested blob URL: $requestedUrl"

$target = Join-Path (Join-Path $publisherRoot $targetAlias) ("v" + $model.Version)
Invoke-AzCopy -Source $requestedUrl -Target $target
Generate-InferenceModelJson -ModelRoot $target -TargetAlias $targetAlias
}

$count = @(Get-ChildItem -Path $destination -Recurse -File).Count

if ($count -eq 0) {
throw "Downloaded test data is empty at $destination"
}

Write-Host "Downloaded $count files into $destination"
Write-Host "Cache directory contents for $destination"
(Get-ChildItem -Path $destination -Recurse -Force |
Select-Object FullName, Length |
Format-Table -AutoSize |
Out-String).TrimEnd() | Write-Host
env:
AZCOPY_AUTO_LOGIN_TYPE: AZCLI
19 changes: 3 additions & 16 deletions .pipelines/v2/templates/stages-cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ stages:
steps:
- checkout: self
clean: true
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-cs.yml
parameters:
flNugetDir: '$(Pipeline.Workspace)/cpp-nuget'
Expand Down Expand Up @@ -98,9 +96,7 @@ stages:
steps:
- checkout: self
clean: true
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-cs.yml
parameters:
flNugetDir: '$(Pipeline.Workspace)/cpp-nuget'
Expand Down Expand Up @@ -128,16 +124,7 @@ stages:
steps:
- checkout: self
clean: true
- bash: |
set -euo pipefail
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git lfs install
displayName: 'Install git-lfs (macOS)'
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-cs.yml
parameters:
flNugetDir: '$(Pipeline.Workspace)/cpp-nuget'
Expand Down
19 changes: 3 additions & 16 deletions .pipelines/v2/templates/stages-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,7 @@ stages:
steps:
- checkout: self
clean: true
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-js.yml
parameters:
rid: 'win-x64'
Expand All @@ -224,9 +222,7 @@ stages:
steps:
- checkout: self
clean: true
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-js.yml
parameters:
rid: 'linux-x64'
Expand All @@ -252,16 +248,7 @@ stages:
steps:
- checkout: self
clean: true
- bash: |
set -euo pipefail
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git lfs install
displayName: 'Install git-lfs (macOS)'
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-js.yml
parameters:
rid: 'osx-arm64'
Expand Down
15 changes: 2 additions & 13 deletions .pipelines/v2/templates/stages-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,7 @@ stages:
steps:
- checkout: self
clean: true
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-python.yml
parameters:
wheelDir: '$(Pipeline.Workspace)/python-sdk-linux-x64'
Expand All @@ -216,16 +214,7 @@ stages:
steps:
- checkout: self
clean: true
- bash: |
set -euo pipefail
if ! command -v git-lfs >/dev/null 2>&1; then
brew install git-lfs
fi
git lfs install
displayName: 'Install git-lfs (macOS)'
- template: ../../templates/checkout-steps.yml@self
parameters:
repoName: test-data-shared
- template: ../../templates/fetch-test-data-from-blob.yml@self
- template: steps-test-python.yml
parameters:
wheelDir: '$(Pipeline.Workspace)/python-sdk-osx-arm64'
Expand Down
5 changes: 2 additions & 3 deletions .pipelines/v2/templates/steps-build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ steps:
displayName: 'Append version define'

- ${{ if eq(parameters.runTests, true) }}:
- template: ../../templates/checkout-steps.yml@self
- template: ../../templates/fetch-test-data-from-blob.yml@self
parameters:
repoName: test-data-shared
basePath: '$(Agent.BuildDirectory)'
destinationPath: '$(Agent.BuildDirectory)/test-data-shared'

- bash: |
set -euo pipefail
Expand Down
5 changes: 2 additions & 3 deletions .pipelines/v2/templates/steps-build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ steps:
displayName: 'Append version define'

- ${{ if eq(parameters.runTests, true) }}:
- template: ../../templates/checkout-steps.yml@self
- template: ../../templates/fetch-test-data-from-blob.yml@self
parameters:
repoName: test-data-shared
basePath: '$(Agent.BuildDirectory)'
destinationPath: '$(Agent.BuildDirectory)/test-data-shared'

- bash: |
set -euo pipefail
Expand Down
5 changes: 2 additions & 3 deletions .pipelines/v2/templates/steps-build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ steps:
# Tests need shared model files.
- ${{ if eq(parameters.runTests, true) }}:
- template: ../../templates/checkout-steps.yml@self
- template: ../../templates/fetch-test-data-from-blob.yml@self
parameters:
repoName: test-data-shared
basePath: '$(Agent.BuildDirectory)'
destinationPath: '$(Agent.BuildDirectory)/test-data-shared'

- ${{ if eq(parameters.arch, 'x64') }}:
- script: >-
Expand Down
4 changes: 2 additions & 2 deletions .pipelines/v2/templates/steps-test-cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# (FoundryLocal.Tests). The caller is responsible for:
# - placing the Foundry Local Runtime nupkg in `flNugetDir`
# - downloading the `version-info` pipeline artifact
# - checking out `test-data-shared` at `testDataSharedDir`
# - populating the model cache directory at `testDataSharedDir`

parameters:
- name: flNugetDir
type: string
displayName: 'Path to directory containing the Foundry Local Runtime .nupkg'
- name: testDataSharedDir
type: string
displayName: 'Absolute path to a checked-out test-data-shared working tree'
displayName: 'Absolute path to the model cache directory used by tests'
- name: additionalTestArgs
type: string
default: ''
Expand Down
8 changes: 7 additions & 1 deletion .pipelines/v2/templates/steps-test-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ parameters:
displayName: 'Path to the downloaded js-prebuild-<rid> artifact directory (root with prebuilds/ inside)'
- name: testDataSharedDir
type: string
displayName: 'Path to the checked-out test-data-shared repo'
displayName: 'Path to the model cache directory used by tests'

steps:

Expand Down Expand Up @@ -62,6 +62,9 @@ steps:

- pwsh: |
Set-Location "$(Build.SourcesDirectory)/sdk_v2/js"
Write-Host "FOUNDRY_TEST_DATA_DIR=$env:FOUNDRY_TEST_DATA_DIR"
if (-not (Test-Path $env:FOUNDRY_TEST_DATA_DIR)) { throw "FOUNDRY_TEST_DATA_DIR does not exist: $env:FOUNDRY_TEST_DATA_DIR" }
Get-ChildItem -Path $env:FOUNDRY_TEST_DATA_DIR -Force | ForEach-Object { Write-Host " $($_.FullName)" }
npx --no-install vitest run --reporter=verbose
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
displayName: 'Run vitest'
Expand All @@ -84,6 +87,9 @@ steps:
- bash: |
set -euo pipefail
cd "$(Build.SourcesDirectory)/sdk_v2/js"
echo "FOUNDRY_TEST_DATA_DIR=${FOUNDRY_TEST_DATA_DIR}"
test -d "${FOUNDRY_TEST_DATA_DIR}"
ls -la "${FOUNDRY_TEST_DATA_DIR}"
npx --no-install vitest run --reporter=verbose
displayName: 'Run vitest'
env:
Expand Down
2 changes: 1 addition & 1 deletion .pipelines/v2/templates/steps-test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# The caller is responsible for:
# * placing the built wheel (.whl) under `wheelDir`
# * checking out test-data-shared at `testDataSharedDir`
# * populating the model cache directory at `testDataSharedDir`

parameters:
- name: wheelDir
Expand Down
18 changes: 18 additions & 0 deletions sdk_v2/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,24 @@ pwsh ./build_and_test_all.ps1 -ContinueOnError

See `pwsh ./build_and_test_all.ps1 -?` for the full parameter list.

## Model cache for tests

Model-dependent sdk_v2 tests require `FOUNDRY_TEST_DATA_DIR` to point to a
local model cache directory.

In CI, sdk_v2 pipelines download model data from blob storage into this path.
For local runs, set it explicitly before invoking test commands.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should developers download these models for testing?
Should we instead just download the model from FL SDK for local runs and use the cache for CI runs?
Or perhaps can we share instructions that developers should do to run these tests locally?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And instructions for how to update the test models available.


Example:

```powershell
$env:FOUNDRY_TEST_DATA_DIR = 'C:\path\to\model-cache'
pwsh ./build_and_test_all.ps1
```

The directory can have any name. What matters is that it already contains the
model files expected by the tests you are running.

## Troubleshooting

* **`cl.exe ... HostX86\x86\cl.exe` during pip install** — your shell has
Expand Down
Loading