diff --git a/.github/workflows/samples-integration-test.yml b/.github/workflows/samples-integration-test.yml index 7ca9192f2..574a055d3 100644 --- a/.github/workflows/samples-integration-test.yml +++ b/.github/workflows/samples-integration-test.yml @@ -200,17 +200,6 @@ jobs: /p:TreatWarningsAsErrors=false ` --configuration Release - # Build WinML SDK package (Windows only) - if ($IsWindows) { - dotnet pack sdk/cs/src/Microsoft.AI.Foundry.Local.csproj ` - -o local-packages ` - /p:Version=0.9.0-dev-20260324 ` - /p:UseWinML=true ` - /p:IsPacking=true ` - /p:TreatWarningsAsErrors=false ` - --configuration Release - } - Write-Host "Local packages:" Get-ChildItem local-packages/*.nupkg | ForEach-Object { Write-Host " $($_.Name)" } diff --git a/.pipelines/v2/sdk_v2-js-pipeline-plan.md b/.pipelines/v2/sdk_v2-js-pipeline-plan.md index fd149a9c3..ec5d2ebd6 100644 --- a/.pipelines/v2/sdk_v2-js-pipeline-plan.md +++ b/.pipelines/v2/sdk_v2-js-pipeline-plan.md @@ -19,7 +19,6 @@ Out of scope (deferred): - **Linux ARM64.** Cross-cutting ARM64 work item will add this for C++, C#, Python, and JS together. -- **WinML variant.** JS is scoped out by the base plan. - **npm publishing.** Pipeline produces the `.tgz` as an artifact only. ## Supported platforms diff --git a/.pipelines/v2/sdk_v2-pipeline-plan.md b/.pipelines/v2/sdk_v2-pipeline-plan.md index 243a3503f..a6bad8c9b 100644 --- a/.pipelines/v2/sdk_v2-pipeline-plan.md +++ b/.pipelines/v2/sdk_v2-pipeline-plan.md @@ -39,9 +39,10 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. `/p:FoundryLocalRuntimeVersion=$(sdkVersion)` where `sdkVersion` is the same string used to pack the Runtime nupkg. SDK and Runtime always ship as a matched pair. -2. **C# WinML SKU.** `UseWinML=true` flips the `PackageReference` to - `Microsoft.AI.Foundry.Local.Runtime.WinML`. The csproj has two - `UseWinML`-conditional blocks (one for `.Runtime`, one for `.Runtime.WinML`). +2. **Single C# package bundles WinML.** The SDK csproj references one + `Microsoft.AI.Foundry.Local.Runtime` package, which carries the reg-free + WinML 2.x runtime on Windows. There is no separate WinML SKU or + `UseWinML` switch. 3. **Python SDK wheel bundles `foundry_local` directly** at `_native//foundry_local.{ext}`. No separate `foundry-local-runtime` wheel. Trade-off accepted: SDK and runtime upgrade together. Non-SDK @@ -62,11 +63,11 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. per-track files (`sdkVersion.txt`, `pyVersion.txt`, plus `*.v1.txt` counterparts). Both the sdk_v2 and sdk_v1 coordinators read from this artifact rather than recomputing a timestamp. -7. **Python WinML wheel name via custom PEP 517 backend.** The build - backend at `sdk_v2/python/_build_backend/__init__.py` wraps - `setuptools.build_meta` and rewrites the project name to - `foundry-local-sdk-winml` when `FL_PYTHON_PACKAGE_NAME` is set in the - environment. The same backend also handles ORT pin rewriting (decision 8). +7. **Single Python wheel name; custom PEP 517 backend rewrites ORT pins.** + One wheel, `foundry-local-sdk`, bundles the WinML runtime on Windows. The + build backend at `sdk_v2/python/_build_backend/__init__.py` wraps + `setuptools.build_meta` solely to rewrite the ORT/GenAI pins (decision 8); + it no longer rewrites the project name. 8. **Single source of truth for ORT/GenAI versions.** ORT and GenAI versions live in `sdk_v2/deps_versions.json`. The file shape is `{ "onnxruntime": { "version": "..." }, "onnxruntime-genai": { "version": "..." } }`. @@ -80,10 +81,10 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. at wheel-build time. If the backend is ever bypassed, `pip install` fails fast with "no matching version" (intentional loud failure). - Bumping ORT/GenAI is a one-file edit per variant. + Bumping ORT/GenAI is a one-file edit. 9. **ORT/GenAI come from public PyPI.** No private feed plumbing required for the wheel install path: - - `onnxruntime-core` (Windows/macOS, standard + WinML variants) + - `onnxruntime-core` (Windows/macOS) - `onnxruntime-genai-core` (Windows/macOS) - `onnxruntime-gpu` / `onnxruntime-genai-cuda` (Linux) @@ -97,15 +98,21 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. copy the artifact verbatim — they do not re-filter. This keeps the "what ships next to `foundry_local`" decision in exactly one place. - Staging includes all `*.dll`/`*.pdb` (Windows), `*.so`/`*.so.*`/`lib*.so*` - (Linux), `*.dylib` (macOS) from the build output bin directory and - excludes: - - ORT/GenAI (`onnxruntime*`, `Microsoft.Windows.AI.MachineLearning.*`) — - provided by pip on the Python side and by NuGet on the C# side. - - Test/example binaries (`*_tests.*`, `*_example.*`, `gtest*`, - `cmake_test_discovery_*`). - - The step fails loudly if `foundry_local` itself is missing. + Each staging step copies an explicit allow-list, not a glob: just the + redistributable `foundry_local` library (`.dll` + `.pdb` + `.lib` on + Windows, `libfoundry_local.so` / `.dylib` elsewhere). vcpkg statically + links the rest of the closure (ORT/GenAI/azure-*/curl/ssl/zlib/spdlog/fmt) + into `foundry_local` itself, so nothing else needs to travel. ORT/GenAI + are resolved separately at runtime — from pip on the Python side and from + NuGet on the C# side. + + On Windows the allow-list also includes the delay-loaded + `Microsoft.Windows.AI.MachineLearning.dll` (the reg-free WinML 2.x + runtime, ~922 KB, Microsoft-signed). It is the single payload difference + that used to distinguish the WinML SKU; bundling it unconditionally is + what lets one package serve every consumer. + + Each step fails loudly if its primary library is missing. 11. **Python runtime ORT discovery.** `lib_loader.py::prepare_native_dependencies()` bridges between the in-wheel `foundry_local` and the pip-installed ORT packages: @@ -120,7 +127,7 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. 12. **`foundry-local-install` CLI.** Declared via `[project.scripts]` in `pyproject.toml`, backed by `sdk_v2/python/src/foundry_local_sdk/_native/installer.py`. Flags: - `--winml`, `--verbose`. Verifies installed packages via + `--verbose`. Verifies installed packages via `importlib.util.find_spec` to avoid triggering DLL load during verification. @@ -130,18 +137,18 @@ are gated separately via `.pipelines/v1/templates/stages-sdk-v1.yml`. .pipelines/v2/ └── templates/ ├── stages-sdk-v2.yml # Coordinator: native + cs + python - ├── stages-build-native.yml # 6 build stages + 2 pack stages (C++) - ├── stages-cs.yml # C# build + test (variant: base | winml) - ├── stages-python.yml # Python build + test (variant: base | winml) + ├── stages-build-native.yml # 4 build stages + 1 pack stage (C++) + ├── stages-cs.yml # C# build + test + ├── stages-python.yml # Python build + test ├── steps-prefetch-nuget.yml # ORT/GenAI/WinML NuGet pre-fetch (pwsh + bash) - ├── steps-build-windows.yml # arch: x64 | arm64; useWinml: true | false + ├── steps-build-windows.yml # arch: x64 | arm64 (always bundles WinML) ├── steps-build-linux.yml ├── steps-build-macos.yml ├── steps-build-cs.yml # restore + build + ESRP-sign + pack + ESRP-sign nupkg ├── steps-test-cs.yml # restore + build + run tests ├── steps-build-python.yml # pass-through copy + python -m build --wheel ├── steps-test-python.yml # install wheel + pytest - └── steps-pack-nuget.yml # Runs sdk_v2/cpp/nuget/pack.py (variant: base | winml) + └── steps-pack-nuget.yml # Runs sdk_v2/cpp/nuget/pack.py ``` The repo-shared `.pipelines/templates/checkout-steps.yml` is reused for @@ -155,32 +162,25 @@ compute_version | |-- cpp_build_win_x64 ----------+ |-- cpp_build_win_arm64 --------| - |-- cpp_build_linux_x64 --------+--> pack_nuget --+--> cs_build_base --+--> cs_test_win_x64 - |-- cpp_build_osx_arm64 --------+ | |--> cs_test_linux_x64 - | | +--> cs_test_osx_arm64 - | +--> (cs-sdk-v2-base artifact) - | - | +--> py_build_base_win_x64 --> py_test_base_win_x64 - +-->|--> py_build_base_linux_x64 --> py_test_base_linux_x64 - | +--> py_build_base_osx_arm64 --> py_test_base_osx_arm64 + |-- cpp_build_linux_x64 --------+--> pack_nuget --+--> cs_build --+--> cs_test_win_x64 + |-- cpp_build_osx_arm64 --------+ | |--> cs_test_linux_x64 + | | +--> cs_test_osx_arm64 + | +--> (cs-sdk-v2 artifact) | - |-- cpp_build_win_x64_winml ---+ - |-- cpp_build_win_arm64_winml -+--> pack_nuget_winml --> cs_build_winml --> cs_test_win_x64_winml - | | - | +--> py_build_winml_win_x64 --> py_test_winml_win_x64 - | py_build_winml_win_arm64 (no test — cross-compile) + | +--> py_build_win_x64 --> py_test_win_x64 + +-->|--> py_build_linux_x64 --> py_test_linux_x64 + +--> py_build_osx_arm64 --> py_test_osx_arm64 ``` * All build stages are independent (`dependsOn: [compute_version]`) and run in parallel. -* Both pack stages run on every build (PR and `main`). -* WinML and non-WinML build stages link against the same ORT version - (`ortVersion`, currently 1.25.x). WinML 2.x is reg-free and uses the - standard ORT package, so a separate WinML-aligned ORT pin is no longer - required. -* Tests run on `cpp_build_win_x64`, `cpp_build_win_x64_winml`, - `cpp_build_linux_x64`, and `cpp_build_osx_arm64`. The two ARM64 Windows - stages cross-compile from an x64 host and skip tests. +* The pack stage runs on every build (PR and `main`). +* The Windows native build always bundles the reg-free WinML 2.x runtime, + which links against the same `ortVersion` as every other platform — there + is no separate WinML-aligned ORT pin or build flavor. +* Tests run on `cpp_build_win_x64`, `cpp_build_linux_x64`, and + `cpp_build_osx_arm64`. The ARM64 Windows stage cross-compiles from an x64 + host and skips tests. ## Per-stage artifacts @@ -189,15 +189,12 @@ Published via 1ES `templateContext.outputs` (no manual `PublishPipelineArtifact` | Stage | Artifact name | Contents | |-----------------------------|--------------------------------|------------------------------------------------------------| | `compute_version` | `version-info` | `sdkVersion.txt`, `pyVersion.txt`, `flcVersion.txt` | -| `cpp_build_win_x64` | `cpp-native-win-x64` | `foundry_local.dll`, `foundry_local.pdb` + vcpkg closure | +| `cpp_build_win_x64` | `cpp-native-win-x64` | `foundry_local.dll`, `.pdb`, `.lib` + `Microsoft.Windows.AI.MachineLearning.dll` | | `cpp_build_win_x64` | `cpp-native-include` | Public headers (sourced once, from win-x64) | -| `cpp_build_win_arm64` | `cpp-native-win-arm64` | `foundry_local.dll`, `foundry_local.pdb` + vcpkg closure | -| `cpp_build_linux_x64` | `cpp-native-linux-x64` | `libfoundry_local.so` + vcpkg closure | -| `cpp_build_osx_arm64` | `cpp-native-osx-arm64` | `libfoundry_local.dylib` + vcpkg closure | -| `cpp_build_win_x64_winml` | `cpp-native-win-x64-winml` | `foundry_local.dll`, `foundry_local.pdb` (WinML) | -| `cpp_build_win_arm64_winml` | `cpp-native-win-arm64-winml` | `foundry_local.dll`, `foundry_local.pdb` (WinML) | -| `cpp_pack_nuget` | `cpp-nuget` | `Microsoft.AI.Foundry.Local.Runtime..nupkg` | -| `cpp_pack_nuget_winml` | `cpp-nuget-winml` | `Microsoft.AI.Foundry.Local.Runtime.WinML..nupkg` | +| `cpp_build_win_arm64` | `cpp-native-win-arm64` | `foundry_local.dll`, `.pdb`, `.lib` + `Microsoft.Windows.AI.MachineLearning.dll` | +| `cpp_build_linux_x64` | `cpp-native-linux-x64` | `libfoundry_local.so` | +| `cpp_build_osx_arm64` | `cpp-native-osx-arm64` | `libfoundry_local.dylib` | +| `cpp_pack_nuget` | `cpp-nuget` | `Microsoft.AI.Foundry.Local.Runtime..nupkg` (bundles WinML on Windows) | ## Versioning @@ -278,13 +275,12 @@ set the env var. SDK *build* stages do not need test-data-shared. Test data is fetched on every stage that runs tests: * `cpp_build_win_x64` -* `cpp_build_win_x64_winml` * `cpp_build_linux_x64` * `cpp_build_osx_arm64` * All `cs_test_*` and `py_test_*` stages -Skipped on `cpp_build_win_arm64` and `cpp_build_win_arm64_winml` -(cross-compile, no test execution on the host). +Skipped on `cpp_build_win_arm64` (cross-compile, no test execution on the +host). ## Build commands diff --git a/.pipelines/v2/templates/stages-build-native.yml b/.pipelines/v2/templates/stages-build-native.yml index 09a75c33e..b8f8f0e87 100644 --- a/.pipelines/v2/templates/stages-build-native.yml +++ b/.pipelines/v2/templates/stages-build-native.yml @@ -1,13 +1,12 @@ # Native build + pack stages for the Foundry Local C++ SDK. # -# Per-platform stages each produce a `cpp-native-[-winml]` pipeline -# artifact. Two pack stages assemble the artifacts into separate NuGet -# packages on every build (PR and main): +# Per-platform stages each produce a `cpp-native-` pipeline artifact. On +# Windows the build always includes the reg-free WinML 2.x runtime. A single +# pack stage assembles the artifacts into one NuGet package on every build +# (PR and main): # -# pack_nuget – Microsoft.AI.Foundry.Local.Runtime -# (win-x64, win-arm64, linux-x64, osx-arm64) -# pack_nuget_winml – Microsoft.AI.Foundry.Local.Runtime.WinML -# (win-x64, win-arm64; both built with --use_winml) +# pack_nuget – Microsoft.AI.Foundry.Local.Runtime +# (win-x64, win-arm64, linux-x64, osx-arm64; Windows ships WinML) parameters: - name: buildConfig @@ -149,81 +148,10 @@ stages: runTests: true # ==================================================================== - # Windows x64 (WinML) — build + test - # The WinML variant links against the WinML-aligned ORT (1.23.x), which - # is older than the base ORT and lacks some ops used by cataloged vision - # models (e.g. com.microsoft:CausalConvWithState). The C++ VisionFixture - # and the C# VisionTests both skip themselves on WinML so the rest of - # the suite still runs against this configuration. - # ==================================================================== - - stage: cpp_build_win_x64_winml - displayName: 'C++ Native: Windows x64 (WinML)' - dependsOn: - - compute_version - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - outputs: - - output: pipelineArtifact - artifactName: 'cpp-native-win-x64-winml' - targetPath: '$(Build.ArtifactStagingDirectory)/native' - steps: - - template: steps-build-windows.yml - parameters: - arch: x64 - buildConfig: ${{ parameters.buildConfig }} - ortVersion: ${{ parameters.ortVersion }} - genaiVersion: ${{ parameters.genaiVersion }} - winmlVersion: ${{ parameters.winmlVersion }} - useWinml: true - runTests: true - stageHeaders: false - - # ==================================================================== - # Windows ARM64 (WinML) — cross-compile only - # ==================================================================== - - stage: cpp_build_win_arm64_winml - displayName: 'C++ Native: Windows ARM64 (WinML)' - dependsOn: - - compute_version - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - outputs: - - output: pipelineArtifact - artifactName: 'cpp-native-win-arm64-winml' - targetPath: '$(Build.ArtifactStagingDirectory)/native' - steps: - - template: steps-build-windows.yml - parameters: - arch: arm64 - buildConfig: ${{ parameters.buildConfig }} - ortVersion: ${{ parameters.ortVersion }} - genaiVersion: ${{ parameters.genaiVersion }} - winmlVersion: ${{ parameters.winmlVersion }} - useWinml: true - runTests: false - stageHeaders: false - - # ==================================================================== - # Pack — base NuGet package (all 4 platforms) + # Pack — NuGet package (all 4 platforms; Windows includes WinML) # ==================================================================== - stage: cpp_pack_nuget - displayName: 'C++ Native: Pack NuGet (base)' + displayName: 'C++ Native: Pack NuGet' dependsOn: - compute_version - cpp_build_win_x64 @@ -261,40 +189,3 @@ stages: parameters: ortVersion: ${{ parameters.ortVersion }} genaiVersion: ${{ parameters.genaiVersion }} - variant: base - - # ==================================================================== - # Pack — WinML NuGet package (Windows x64 + arm64 only) - # ==================================================================== - - stage: cpp_pack_nuget_winml - displayName: 'C++ Native: Pack NuGet (WinML)' - dependsOn: - - compute_version - - cpp_build_win_x64_winml - - cpp_build_win_arm64_winml - jobs: - - job: pack - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-win-x64-winml' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-x64-winml' - - input: pipelineArtifact - artifactName: 'cpp-native-win-arm64-winml' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-arm64-winml' - outputs: - - output: pipelineArtifact - artifactName: 'cpp-nuget-winml' - targetPath: '$(Build.ArtifactStagingDirectory)/nuget' - steps: - - template: steps-pack-nuget.yml - parameters: - ortVersion: ${{ parameters.ortVersion }} - genaiVersion: ${{ parameters.genaiVersion }} - variant: winml diff --git a/.pipelines/v2/templates/stages-cs.yml b/.pipelines/v2/templates/stages-cs.yml index c271dda13..3bb83a0e7 100644 --- a/.pipelines/v2/templates/stages-cs.yml +++ b/.pipelines/v2/templates/stages-cs.yml @@ -1,241 +1,145 @@ -# Build + test stages for the sdk_v2 C# SDK, parameterized by variant. +# Build + test stages for the sdk_v2 C# SDK. # -# variant: base -> Microsoft.AI.Foundry.Local (build + test win/linux/osx) -# variant: winml -> Microsoft.AI.Foundry.Local.WinML (build + test win only) +# Produces Microsoft.AI.Foundry.Local (build + test win/linux/osx). On Windows +# the native runtime it consumes bundles the reg-free WinML 2.x runtime, so a +# single package serves every platform. # -# Depends on the matching native pack stage from stages-build-native.yml: -# base -> cpp_pack_nuget (artifact: cpp-nuget) -# winml -> cpp_pack_nuget_winml (artifact: cpp-nuget-winml) +# Depends on the native pack stage from stages-build-native.yml: +# cpp_pack_nuget (artifact: cpp-nuget) # -# Produces a `cs-sdk-v2-` pipeline artifact containing the signed -# .nupkg (+ .snupkg). - -parameters: -- name: variant - type: string - default: 'base' - values: ['base', 'winml'] - -# Per-variant config block selected at template-expansion time. Keeps the -# stage bodies free of nested ${{ if }} branching for static naming. -- name: _config_base - type: object - default: - suffix: 'base' - isWinML: false - packStage: 'cpp_pack_nuget' - nativeArtifact: 'cpp-nuget' - csArtifact: 'cs-sdk-v2-base' - -- name: _config_winml - type: object - default: - suffix: 'winml' - isWinML: true - packStage: 'cpp_pack_nuget_winml' - nativeArtifact: 'cpp-nuget-winml' - csArtifact: 'cs-sdk-v2-winml' +# Produces a `cs-sdk-v2` pipeline artifact containing the signed .nupkg (+ .snupkg). stages: # ==================================================================== # Build stage — produces the C# SDK NuGet package. # ==================================================================== -- ${{ if eq(parameters.variant, 'base') }}: - - stage: cs_build_base - displayName: 'C# SDK: Build (base)' - dependsOn: - - compute_version - - ${{ parameters._config_base.packStage }} - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_base.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - outputs: - - output: pipelineArtifact - artifactName: ${{ parameters._config_base.csArtifact }} - targetPath: '$(Build.ArtifactStagingDirectory)/cs-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - isWinML: false - outputDir: '$(Build.ArtifactStagingDirectory)/cs-sdk' - -- ${{ if eq(parameters.variant, 'winml') }}: - - stage: cs_build_winml - displayName: 'C# SDK: Build (WinML)' - dependsOn: - - compute_version - - ${{ parameters._config_winml.packStage }} - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_winml.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_winml.nativeArtifact }}' - outputs: - - output: pipelineArtifact - artifactName: ${{ parameters._config_winml.csArtifact }} - targetPath: '$(Build.ArtifactStagingDirectory)/cs-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_winml.nativeArtifact }}' - isWinML: true - outputDir: '$(Build.ArtifactStagingDirectory)/cs-sdk' +- stage: cs_build + displayName: 'C# SDK: Build' + dependsOn: + - compute_version + - cpp_pack_nuget + jobs: + - job: build + pool: + name: onnxruntime-Win-CPU-2022 + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-nuget' + targetPath: '$(Pipeline.Workspace)/cpp-nuget' + outputs: + - output: pipelineArtifact + artifactName: 'cs-sdk-v2' + targetPath: '$(Build.ArtifactStagingDirectory)/cs-sdk' + steps: + - checkout: self + clean: true + - template: steps-build-cs.yml + parameters: + flNugetDir: '$(Pipeline.Workspace)/cpp-nuget' + outputDir: '$(Build.ArtifactStagingDirectory)/cs-sdk' # ==================================================================== # Test — Windows x64 # ==================================================================== -- ${{ if eq(parameters.variant, 'base') }}: - - stage: cs_test_win_x64 - displayName: 'C# SDK: Test Windows x64 (base)' - dependsOn: - - cs_build_base - jobs: - - job: test - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_base.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - isWinML: false - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - -- ${{ if eq(parameters.variant, 'winml') }}: - - stage: cs_test_win_x64_winml - displayName: 'C# SDK: Test Windows x64 (WinML)' - dependsOn: - - cs_build_winml - jobs: - - job: test - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_winml.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_winml.nativeArtifact }}' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_winml.nativeArtifact }}' - isWinML: true - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' +- stage: cs_test_win_x64 + displayName: 'C# SDK: Test Windows x64' + dependsOn: + - cs_build + jobs: + - job: test + pool: + name: onnxruntime-Win-CPU-2022 + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-nuget' + targetPath: '$(Pipeline.Workspace)/cpp-nuget' + steps: + - checkout: self + clean: true + - template: ../../templates/checkout-steps.yml@self + parameters: + repoName: test-data-shared + - template: steps-test-cs.yml + parameters: + flNugetDir: '$(Pipeline.Workspace)/cpp-nuget' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' # ==================================================================== -# Test — Linux x64 / macOS ARM64 (base variant only; WinML is Windows-only) +# Test — Linux x64 / macOS ARM64 # ==================================================================== -- ${{ if eq(parameters.variant, 'base') }}: - - stage: cs_test_linux_x64 - displayName: 'C# SDK: Test Linux x64 (base)' - dependsOn: - - cs_build_base - jobs: - - job: test - pool: - name: onnxruntime-Ubuntu2404-AMD-CPU - os: linux - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_base.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - isWinML: false - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' +- stage: cs_test_linux_x64 + displayName: 'C# SDK: Test Linux x64' + dependsOn: + - cs_build + jobs: + - job: test + pool: + name: onnxruntime-Ubuntu2404-AMD-CPU + os: linux + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-nuget' + targetPath: '$(Pipeline.Workspace)/cpp-nuget' + steps: + - checkout: self + clean: true + - template: ../../templates/checkout-steps.yml@self + parameters: + repoName: test-data-shared + - template: steps-test-cs.yml + parameters: + flNugetDir: '$(Pipeline.Workspace)/cpp-nuget' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - - stage: cs_test_osx_arm64 - displayName: 'C# SDK: Test macOS ARM64 (base)' - dependsOn: - - cs_build_base - jobs: - - job: test - pool: - name: AcesShared - os: macOS - demands: - - ImageOverride -equals ACES_VM_SharedPool_Sequoia - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: ${{ parameters._config_base.nativeArtifact }} - targetPath: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - 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: steps-test-cs.yml - parameters: - flNugetDir: '$(Pipeline.Workspace)/${{ parameters._config_base.nativeArtifact }}' - isWinML: false - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - additionalTestArgs: '--settings $(Build.SourcesDirectory)/sdk_v2/cs/test/FoundryLocal.Tests/sequential.runsettings' +- stage: cs_test_osx_arm64 + displayName: 'C# SDK: Test macOS ARM64' + dependsOn: + - cs_build + jobs: + - job: test + pool: + name: AcesShared + os: macOS + demands: + - ImageOverride -equals ACES_VM_SharedPool_Sequoia + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-nuget' + targetPath: '$(Pipeline.Workspace)/cpp-nuget' + 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: steps-test-cs.yml + parameters: + flNugetDir: '$(Pipeline.Workspace)/cpp-nuget' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' + additionalTestArgs: '--settings $(Build.SourcesDirectory)/sdk_v2/cs/test/FoundryLocal.Tests/sequential.runsettings' diff --git a/.pipelines/v2/templates/stages-python.yml b/.pipelines/v2/templates/stages-python.yml index 7a07c7dae..e6b859dc9 100644 --- a/.pipelines/v2/templates/stages-python.yml +++ b/.pipelines/v2/templates/stages-python.yml @@ -1,346 +1,233 @@ -# Build + test stages for the sdk_v2 Python SDK, parameterized by variant. +# Build + test stages for the sdk_v2 Python SDK (foundry-local-sdk). # -# variant: base -> foundry-local-sdk (build win-x64 + win-arm64 + linux-x64 + osx-arm64; -# test win-x64 + linux-x64 + osx-arm64) -# variant: winml -> foundry-local-sdk-winml (build + test win-x64; build-only win-arm64) +# Builds win-x64 + win-arm64 + linux-x64 + osx-arm64; tests win-x64 + linux-x64 +# + osx-arm64 (win-arm64 is build-only, matching the C# matrix). # # Each build stage depends on the matching native build stage from -# stages-build-native.yml and consumes the `cpp-native-[-winml]` -# pipeline artifact directly (no NuGet — the wheel bundles the native lib -# at src/foundry_local_sdk/_native//). +# stages-build-native.yml and consumes the `cpp-native-` pipeline artifact +# directly (no NuGet — the wheel bundles the native lib at +# src/foundry_local_sdk/_native//). On Windows that native artifact carries +# the reg-free WinML 2.x runtime, so the single wheel includes WinML support. # -# Each build stage emits one wheel under the -# `python-sdk--` pipeline artifact. - -parameters: -- name: variant - type: string - default: 'base' - values: ['base', 'winml'] +# Each build stage emits one wheel under the `python-sdk-` pipeline artifact. stages: # ===================================================================== -# Base variant: build win-x64 / win-arm64 / linux-x64 / osx-arm64 +# Build win-x64 / win-arm64 / linux-x64 / osx-arm64 # ===================================================================== -- ${{ if eq(parameters.variant, 'base') }}: - - - stage: python_build_base_win_x64 - displayName: 'Python SDK: Build Windows x64 (base)' - dependsOn: - - compute_version - - cpp_build_win_x64 - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-win-x64' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-x64' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-base-win-x64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-x64' - rid: 'win-x64' - isWinML: false - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - - - stage: python_build_base_win_arm64 - displayName: 'Python SDK: Build Windows ARM64 (base)' - dependsOn: - - compute_version - - cpp_build_win_arm64 - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-win-arm64' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-arm64' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-base-win-arm64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-arm64' - rid: 'win-arm64' - isWinML: false - targetArch: 'arm64' - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - - - stage: python_build_base_linux_x64 - displayName: 'Python SDK: Build Linux x64 (base)' - dependsOn: - - compute_version - - cpp_build_linux_x64 - jobs: - - job: build - pool: - name: onnxruntime-Ubuntu2404-AMD-CPU - os: linux - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-linux-x64' - targetPath: '$(Pipeline.Workspace)/cpp-native-linux-x64' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-base-linux-x64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-linux-x64' - rid: 'linux-x64' - isWinML: false - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' +- stage: python_build_win_x64 + displayName: 'Python SDK: Build Windows x64' + dependsOn: + - compute_version + - cpp_build_win_x64 + jobs: + - job: build + pool: + name: onnxruntime-Win-CPU-2022 + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-native-win-x64' + targetPath: '$(Pipeline.Workspace)/cpp-native-win-x64' + outputs: + - output: pipelineArtifact + artifactName: 'python-sdk-win-x64' + targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' + steps: + - checkout: self + clean: true + - template: steps-build-python.yml + parameters: + nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-x64' + rid: 'win-x64' + outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - - stage: python_build_base_osx_arm64 - displayName: 'Python SDK: Build macOS ARM64 (base)' - dependsOn: - - compute_version - - cpp_build_osx_arm64 - jobs: - - job: build - pool: - name: AcesShared - os: macOS - demands: - - ImageOverride -equals ACES_VM_SharedPool_Sequoia - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-osx-arm64' - targetPath: '$(Pipeline.Workspace)/cpp-native-osx-arm64' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-base-osx-arm64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-osx-arm64' - rid: 'osx-arm64' - isWinML: false - pythonArchitecture: 'arm64' - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' +- stage: python_build_win_arm64 + displayName: 'Python SDK: Build Windows ARM64' + dependsOn: + - compute_version + - cpp_build_win_arm64 + jobs: + - job: build + pool: + name: onnxruntime-Win-CPU-2022 + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-native-win-arm64' + targetPath: '$(Pipeline.Workspace)/cpp-native-win-arm64' + outputs: + - output: pipelineArtifact + artifactName: 'python-sdk-win-arm64' + targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' + steps: + - checkout: self + clean: true + - template: steps-build-python.yml + parameters: + nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-arm64' + rid: 'win-arm64' + targetArch: 'arm64' + outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - # ================================================================= - # Tests — base variant (win-x64, linux-x64, osx-arm64). - # win-arm64 is build-only (matches legacy + C# matrix). - # ================================================================= - - stage: python_test_base_win_x64 - displayName: 'Python SDK: Test Windows x64 (base)' - dependsOn: - - python_build_base_win_x64 - jobs: - - job: test - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'python-sdk-base-win-x64' - targetPath: '$(Pipeline.Workspace)/python-sdk-base-win-x64' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-python.yml - parameters: - wheelDir: '$(Pipeline.Workspace)/python-sdk-base-win-x64' - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - isWinML: false +- stage: python_build_linux_x64 + displayName: 'Python SDK: Build Linux x64' + dependsOn: + - compute_version + - cpp_build_linux_x64 + jobs: + - job: build + pool: + name: onnxruntime-Ubuntu2404-AMD-CPU + os: linux + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-native-linux-x64' + targetPath: '$(Pipeline.Workspace)/cpp-native-linux-x64' + outputs: + - output: pipelineArtifact + artifactName: 'python-sdk-linux-x64' + targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' + steps: + - checkout: self + clean: true + - template: steps-build-python.yml + parameters: + nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-linux-x64' + rid: 'linux-x64' + outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - - stage: python_test_base_linux_x64 - displayName: 'Python SDK: Test Linux x64 (base)' - dependsOn: - - python_build_base_linux_x64 - jobs: - - job: test - pool: - name: onnxruntime-Ubuntu2404-AMD-CPU - os: linux - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'python-sdk-base-linux-x64' - targetPath: '$(Pipeline.Workspace)/python-sdk-base-linux-x64' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-python.yml - parameters: - wheelDir: '$(Pipeline.Workspace)/python-sdk-base-linux-x64' - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - isWinML: false - - - stage: python_test_base_osx_arm64 - displayName: 'Python SDK: Test macOS ARM64 (base)' - dependsOn: - - python_build_base_osx_arm64 - jobs: - - job: test - pool: - name: AcesShared - os: macOS - demands: - - ImageOverride -equals ACES_VM_SharedPool_Sequoia - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'python-sdk-base-osx-arm64' - targetPath: '$(Pipeline.Workspace)/python-sdk-base-osx-arm64' - 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: steps-test-python.yml - parameters: - wheelDir: '$(Pipeline.Workspace)/python-sdk-base-osx-arm64' - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - isWinML: false - pythonArchitecture: 'arm64' - -# ===================================================================== -# WinML variant: Windows only (x64 + arm64); only x64 is tested. -# ===================================================================== -- ${{ if eq(parameters.variant, 'winml') }}: +- stage: python_build_osx_arm64 + displayName: 'Python SDK: Build macOS ARM64' + dependsOn: + - compute_version + - cpp_build_osx_arm64 + jobs: + - job: build + pool: + name: AcesShared + os: macOS + demands: + - ImageOverride -equals ACES_VM_SharedPool_Sequoia + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'version-info' + targetPath: '$(Pipeline.Workspace)/version-info' + - input: pipelineArtifact + artifactName: 'cpp-native-osx-arm64' + targetPath: '$(Pipeline.Workspace)/cpp-native-osx-arm64' + outputs: + - output: pipelineArtifact + artifactName: 'python-sdk-osx-arm64' + targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' + steps: + - checkout: self + clean: true + - template: steps-build-python.yml + parameters: + nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-osx-arm64' + rid: 'osx-arm64' + pythonArchitecture: 'arm64' + outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' - - stage: python_build_winml_win_x64 - displayName: 'Python SDK: Build Windows x64 (WinML)' - dependsOn: - - compute_version - - cpp_build_win_x64_winml - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-win-x64-winml' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-x64-winml' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-winml-win-x64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-x64-winml' - rid: 'win-x64' - isWinML: true - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' +# ================================================================= +# Tests — win-x64, linux-x64, osx-arm64. win-arm64 is build-only. +# ================================================================= +- stage: python_test_win_x64 + displayName: 'Python SDK: Test Windows x64' + dependsOn: + - python_build_win_x64 + jobs: + - job: test + pool: + name: onnxruntime-Win-CPU-2022 + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'python-sdk-win-x64' + targetPath: '$(Pipeline.Workspace)/python-sdk-win-x64' + steps: + - checkout: self + clean: true + - template: ../../templates/checkout-steps.yml@self + parameters: + repoName: test-data-shared + - template: steps-test-python.yml + parameters: + wheelDir: '$(Pipeline.Workspace)/python-sdk-win-x64' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - - stage: python_build_winml_win_arm64 - displayName: 'Python SDK: Build Windows ARM64 (WinML)' - dependsOn: - - compute_version - - cpp_build_win_arm64_winml - jobs: - - job: build - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'version-info' - targetPath: '$(Pipeline.Workspace)/version-info' - - input: pipelineArtifact - artifactName: 'cpp-native-win-arm64-winml' - targetPath: '$(Pipeline.Workspace)/cpp-native-win-arm64-winml' - outputs: - - output: pipelineArtifact - artifactName: 'python-sdk-winml-win-arm64' - targetPath: '$(Build.ArtifactStagingDirectory)/python-sdk' - steps: - - checkout: self - clean: true - - template: steps-build-python.yml - parameters: - nativeArtifactDir: '$(Pipeline.Workspace)/cpp-native-win-arm64-winml' - rid: 'win-arm64' - isWinML: true - targetArch: 'arm64' - outputDir: '$(Build.ArtifactStagingDirectory)/python-sdk' +- stage: python_test_linux_x64 + displayName: 'Python SDK: Test Linux x64' + dependsOn: + - python_build_linux_x64 + jobs: + - job: test + pool: + name: onnxruntime-Ubuntu2404-AMD-CPU + os: linux + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'python-sdk-linux-x64' + targetPath: '$(Pipeline.Workspace)/python-sdk-linux-x64' + steps: + - checkout: self + clean: true + - template: ../../templates/checkout-steps.yml@self + parameters: + repoName: test-data-shared + - template: steps-test-python.yml + parameters: + wheelDir: '$(Pipeline.Workspace)/python-sdk-linux-x64' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - - stage: python_test_winml_win_x64 - displayName: 'Python SDK: Test Windows x64 (WinML)' - dependsOn: - - python_build_winml_win_x64 - jobs: - - job: test - pool: - name: onnxruntime-Win-CPU-2022 - os: windows - templateContext: - inputs: - - input: pipelineArtifact - artifactName: 'python-sdk-winml-win-x64' - targetPath: '$(Pipeline.Workspace)/python-sdk-winml-win-x64' - steps: - - checkout: self - clean: true - - template: ../../templates/checkout-steps.yml@self - parameters: - repoName: test-data-shared - - template: steps-test-python.yml - parameters: - wheelDir: '$(Pipeline.Workspace)/python-sdk-winml-win-x64' - testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' - isWinML: true +- stage: python_test_osx_arm64 + displayName: 'Python SDK: Test macOS ARM64' + dependsOn: + - python_build_osx_arm64 + jobs: + - job: test + pool: + name: AcesShared + os: macOS + demands: + - ImageOverride -equals ACES_VM_SharedPool_Sequoia + templateContext: + inputs: + - input: pipelineArtifact + artifactName: 'python-sdk-osx-arm64' + targetPath: '$(Pipeline.Workspace)/python-sdk-osx-arm64' + 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: steps-test-python.yml + parameters: + wheelDir: '$(Pipeline.Workspace)/python-sdk-osx-arm64' + testDataSharedDir: '$(Build.SourcesDirectory)/test-data-shared' + pythonArchitecture: 'arm64' diff --git a/.pipelines/v2/templates/stages-sdk-v2.yml b/.pipelines/v2/templates/stages-sdk-v2.yml index 5a1c59bab..755edd17b 100644 --- a/.pipelines/v2/templates/stages-sdk-v2.yml +++ b/.pipelines/v2/templates/stages-sdk-v2.yml @@ -2,8 +2,9 @@ # # Composes: # 1. Native C++ build + pack (templates/stages-build-native.yml) -# 2. C# SDK base + WinML (templates/stages-cs.yml × 2) -# 3. Python SDK base + WinML (templates/stages-python.yml × 2) +# 2. C# SDK (templates/stages-cs.yml) +# 3. Python SDK (templates/stages-python.yml) +# 4. JS SDK (templates/stages-js.yml) # # Assumes the caller has already emitted a `compute_version` stage that # publishes the `version-info` pipeline artifact (containing sdkVersion.txt @@ -31,25 +32,11 @@ stages: genaiVersion: ${{ parameters.genaiVersion }} winmlVersion: ${{ parameters.winmlVersion }} -# ── C# SDK (base) ── +# ── C# SDK ── - template: stages-cs.yml - parameters: - variant: base - -# ── C# SDK (WinML) ── -- template: stages-cs.yml - parameters: - variant: winml -# ── Python SDK (base) ── +# ── Python SDK ── - template: stages-python.yml - parameters: - variant: base - -# ── Python SDK (WinML) ── -- template: stages-python.yml - parameters: - variant: winml # ── JS SDK (single multi-platform tarball) ── - template: stages-js.yml diff --git a/.pipelines/v2/templates/steps-build-cs.yml b/.pipelines/v2/templates/steps-build-cs.yml index 1c77af191..2ebf7e085 100644 --- a/.pipelines/v2/templates/steps-build-cs.yml +++ b/.pipelines/v2/templates/steps-build-cs.yml @@ -1,8 +1,8 @@ # Inner steps to restore, build, sign, and pack the sdk_v2 C# SDK -# (Microsoft.AI.Foundry.Local / Microsoft.AI.Foundry.Local.WinML). +# (Microsoft.AI.Foundry.Local). # # The caller is responsible for placing the Foundry Local Runtime nupkg -# (Microsoft.AI.Foundry.Local.Runtime[.WinML]) in `flNugetDir`, and for +# (Microsoft.AI.Foundry.Local.Runtime) in `flNugetDir`, and for # downloading the `version-info` pipeline artifact so this template can # read $(Pipeline.Workspace)/version-info/sdkVersion.txt. @@ -10,9 +10,6 @@ parameters: - name: flNugetDir type: string displayName: 'Path to directory containing the Foundry Local Runtime .nupkg' -- name: isWinML - type: boolean - default: false - name: outputDir type: string default: '$(Build.ArtifactStagingDirectory)/cs-sdk' @@ -92,7 +89,6 @@ steps: if (-not (Test-Path $proj)) { throw "Project not found: $proj" } dotnet restore $proj ` --configfile "$(customNugetConfig)" ` - /p:UseWinML=${{ parameters.isWinML }} ` /p:FoundryLocalRuntimeVersion=$(packageVersion) ` /p:FoundryLocalNativeBinDir= if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -105,7 +101,6 @@ steps: script: | dotnet build "$(Build.SourcesDirectory)/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj" ` --no-restore --configuration Release ` - /p:UseWinML=${{ parameters.isWinML }} ` /p:FoundryLocalRuntimeVersion=$(packageVersion) ` /p:FoundryLocalNativeBinDir= if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -145,7 +140,6 @@ steps: --no-build --no-restore --configuration Release ` --output "${{ parameters.outputDir }}" ` /p:PackageVersion=$(packageVersion) ` - /p:UseWinML=${{ parameters.isWinML }} ` /p:FoundryLocalRuntimeVersion=$(packageVersion) ` /p:FoundryLocalNativeBinDir= ` /p:IncludeSymbols=true ` diff --git a/.pipelines/v2/templates/steps-build-js.yml b/.pipelines/v2/templates/steps-build-js.yml index 5e6ab1e77..1b0cba2cf 100644 --- a/.pipelines/v2/templates/steps-build-js.yml +++ b/.pipelines/v2/templates/steps-build-js.yml @@ -123,9 +123,10 @@ steps: FOUNDRY_LOCAL_PREBUILD_DIR: '$(Build.SourcesDirectory)/sdk_v2/js/prebuilds/$(prebuildDir)' # Stage prebuilds/-/ with the two .node addons + foundry_local -# shared library. binding.gyp's copy_addon_to_prebuilds target already -# drops the .node files into sdk_v2/js/prebuilds/-/; we copy -# from there into the artifact dir alongside the shared library. +# shared library (and, on Windows, the reg-free WinML 2.x runtime DLL). +# binding.gyp's copy_addon_to_prebuilds target already drops the .node files +# into sdk_v2/js/prebuilds/-/; we copy from there into the artifact +# dir alongside the shared library. - ${{ if or(eq(parameters.rid, 'win-x64'), eq(parameters.rid, 'win-arm64')) }}: - pwsh: | $dst = "${{ parameters.outputDir }}/prebuilds/$(prebuildDir)" @@ -145,6 +146,16 @@ steps: Copy-Item $nativeLibPath -Destination $dst -Force Write-Host " staged $(nativeLib)" + # WinML 2.x runtime ships next to foundry_local.dll so WinML EPs work out + # of the box. It is Microsoft-signed (NuGet) — excluded from ESRP signing. + $winmlDll = Join-Path "${{ parameters.nativeArtifactDir }}" 'Microsoft.Windows.AI.MachineLearning.dll' + if (Test-Path $winmlDll) { + Copy-Item $winmlDll -Destination $dst -Force + Write-Host " staged Microsoft.Windows.AI.MachineLearning.dll" + } else { + Write-Warning "WinML runtime DLL not found in native artifact: $winmlDll" + } + Get-ChildItem $dst | ForEach-Object { Write-Host " $($_.Name) $($_.Length) bytes" } displayName: 'Stage prebuild directory' diff --git a/.pipelines/v2/templates/steps-build-python.yml b/.pipelines/v2/templates/steps-build-python.yml index fe81d69cd..50f86c9da 100644 --- a/.pipelines/v2/templates/steps-build-python.yml +++ b/.pipelines/v2/templates/steps-build-python.yml @@ -13,13 +13,10 @@ parameters: - name: nativeArtifactDir type: string - displayName: 'Path to the downloaded cpp-native-[-winml] artifact directory' + displayName: 'Path to the downloaded cpp-native- artifact directory' - name: rid type: string displayName: 'Runtime identifier (win-x64, win-arm64, linux-x64, osx-arm64)' -- name: isWinML - type: boolean - default: false - name: outputDir type: string default: '$(Build.ArtifactStagingDirectory)/python-sdk' @@ -270,13 +267,6 @@ steps: script: | $outDir = "${{ parameters.outputDir }}" New-Item -ItemType Directory -Force -Path $outDir | Out-Null - $isWinML = [bool]::Parse('${{ parameters.isWinML }}') - if ($isWinML) { - $env:FL_PYTHON_PACKAGE_NAME = 'foundry-local-sdk-winml' - Write-Host "WinML variant: FL_PYTHON_PACKAGE_NAME=$env:FL_PYTHON_PACKAGE_NAME" - } else { - Remove-Item Env:FL_PYTHON_PACKAGE_NAME -ErrorAction SilentlyContinue - } $targetArch = '${{ parameters.targetArch }}' $extraArgs = @() if ($targetArch -eq 'arm64') { diff --git a/.pipelines/v2/templates/steps-build-windows.yml b/.pipelines/v2/templates/steps-build-windows.yml index 0e17f07b6..cc439f54f 100644 --- a/.pipelines/v2/templates/steps-build-windows.yml +++ b/.pipelines/v2/templates/steps-build-windows.yml @@ -10,7 +10,6 @@ # ortVersion – Microsoft.ML.OnnxRuntime.Foundry version # genaiVersion – Microsoft.ML.OnnxRuntimeGenAI.Foundry version # winmlVersion – Microsoft.Windows.AI.MachineLearning version -# useWinml – Build the WinML variant (--use_winml). # runTests – Whether to run tests # stageHeaders – Whether to stage public headers as a separate artifact @@ -26,9 +25,6 @@ parameters: type: string - name: winmlVersion type: string -- name: useWinml - type: boolean - default: false - name: runTests type: boolean default: false @@ -60,7 +56,7 @@ steps: ortVersion: ${{ parameters.ortVersion }} genaiVersion: ${{ parameters.genaiVersion }} winmlVersion: ${{ parameters.winmlVersion }} - includeWinml: ${{ parameters.useWinml }} + includeWinml: true shell: pwsh # Bake the pipeline-computed version into the binary so FoundryLocalGetVersionString() @@ -83,12 +79,13 @@ steps: repoName: test-data-shared basePath: '$(Agent.BuildDirectory)' -- ${{ if and(eq(parameters.arch, 'x64'), eq(parameters.useWinml, false)) }}: +- ${{ if eq(parameters.arch, 'x64') }}: - script: >- python build.py --configure --build --config ${{ parameters.buildConfig }} --cmake_generator "Visual Studio 17 2022" + --winml_sdk_version ${{ parameters.winmlVersion }} --cmake_extra_defines $(cmakeFetchDefines) displayName: 'Configure and build (x64)' workingDirectory: $(Build.SourcesDirectory)/sdk_v2/cpp @@ -96,26 +93,13 @@ steps: VCPKG_ROOT: $(Build.BinariesDirectory)\vcpkg PKG_CONFIG: $(Build.BinariesDirectory)\tools\pkg-config.bat -- ${{ if and(eq(parameters.arch, 'x64'), eq(parameters.useWinml, true)) }}: - - script: >- - python build.py - --configure --build - --config ${{ parameters.buildConfig }} - --cmake_generator "Visual Studio 17 2022" - --use_winml --winml_sdk_version ${{ parameters.winmlVersion }} - --cmake_extra_defines $(cmakeFetchDefines) - displayName: 'Configure and build (x64, WinML)' - workingDirectory: $(Build.SourcesDirectory)/sdk_v2/cpp - env: - VCPKG_ROOT: $(Build.BinariesDirectory)\vcpkg - PKG_CONFIG: $(Build.BinariesDirectory)\tools\pkg-config.bat - -- ${{ if and(eq(parameters.arch, 'arm64'), eq(parameters.useWinml, false)) }}: +- ${{ if eq(parameters.arch, 'arm64') }}: - script: >- python build.py --configure --build --arm64 --config ${{ parameters.buildConfig }} --cmake_generator "Visual Studio 17 2022" + --winml_sdk_version ${{ parameters.winmlVersion }} --cmake_extra_defines $(cmakeFetchDefines) displayName: 'Configure and build (arm64 cross-compile)' workingDirectory: $(Build.SourcesDirectory)/sdk_v2/cpp @@ -123,20 +107,6 @@ steps: VCPKG_ROOT: $(Build.BinariesDirectory)\vcpkg PKG_CONFIG: $(Build.BinariesDirectory)\tools\pkg-config.bat -- ${{ if and(eq(parameters.arch, 'arm64'), eq(parameters.useWinml, true)) }}: - - script: >- - python build.py - --configure --build --arm64 - --config ${{ parameters.buildConfig }} - --cmake_generator "Visual Studio 17 2022" - --use_winml --winml_sdk_version ${{ parameters.winmlVersion }} - --cmake_extra_defines $(cmakeFetchDefines) - displayName: 'Configure and build (arm64 cross-compile, WinML)' - workingDirectory: $(Build.SourcesDirectory)/sdk_v2/cpp - env: - VCPKG_ROOT: $(Build.BinariesDirectory)\vcpkg - PKG_CONFIG: $(Build.BinariesDirectory)\tools\pkg-config.bat - - ${{ if and(eq(parameters.runTests, true), eq(parameters.arch, 'x64')) }}: - script: python build.py --test --config ${{ parameters.buildConfig }} displayName: 'Run tests' @@ -145,17 +115,15 @@ steps: VCPKG_ROOT: $(Build.BinariesDirectory)\vcpkg FOUNDRY_TEST_DATA_DIR: $(Agent.BuildDirectory)\test-data-shared -# Stage the redistributable native artifacts. -# - Standard build: vcpkg statically links ORT/GenAI/azure-*/spdlog/fmt/ -# libcurl/libssl/zlib/brotli* into foundry_local.dll (see -# sdk_v2/cpp/triplets/x64-windows.cmake), so the only runtime payload is -# foundry_local.dll itself. -# - WinML build: foundry_local.dll picks up one extra runtime dependency, -# Microsoft.Windows.AI.MachineLearning.dll, which must travel with the wheel. -# The WinML DLL is delay-loaded (see /DELAYLOAD in CMakeLists.txt) so it is -# NOT needed at foundry_local.dll load time, but the cmake post-build copy -# stages it next to foundry_local.dll for runtime EP discovery. ORT/GenAI -# come from the onnxruntime-core / onnxruntime-genai-core pip deps. +# Stage the redistributable native artifacts. vcpkg statically links +# ORT/GenAI/azure-*/spdlog/fmt/libcurl/libssl/zlib/brotli* into foundry_local.dll +# (see sdk_v2/cpp/triplets/x64-windows.cmake), so foundry_local.dll carries that +# payload itself. It also picks up one extra runtime dependency, +# Microsoft.Windows.AI.MachineLearning.dll, which must travel with the wheel. +# The WinML DLL is delay-loaded (see /DELAYLOAD in CMakeLists.txt) so it is +# NOT needed at foundry_local.dll load time, but the cmake post-build copy +# stages it next to foundry_local.dll for runtime EP discovery. ORT/GenAI +# come from the onnxruntime-core / onnxruntime-genai-core pip deps. - task: PowerShell@2 displayName: 'Stage native artifacts' inputs: @@ -170,11 +138,9 @@ steps: $sources = @( (Join-Path $binDir 'foundry_local.dll'), (Join-Path $binDir 'foundry_local.pdb'), - (Join-Path $linkDir 'foundry_local.lib') + (Join-Path $linkDir 'foundry_local.lib'), + (Join-Path $binDir 'Microsoft.Windows.AI.MachineLearning.dll') ) - if ($${{ parameters.useWinml }}) { - $sources += (Join-Path $binDir 'Microsoft.Windows.AI.MachineLearning.dll') - } foreach ($s in $sources) { if (-not (Test-Path $s)) { diff --git a/.pipelines/v2/templates/steps-pack-nuget.yml b/.pipelines/v2/templates/steps-pack-nuget.yml index bde97d772..eb088e4b5 100644 --- a/.pipelines/v2/templates/steps-pack-nuget.yml +++ b/.pipelines/v2/templates/steps-pack-nuget.yml @@ -4,21 +4,15 @@ # Inputs are downloaded by the parent job's templateContext.inputs into # $(Pipeline.Workspace)//. # -# Variants: -# base -> Microsoft.AI.Foundry.Local.Runtime -# (win-x64, win-arm64, linux-x64, osx-arm64) -# winml -> Microsoft.AI.Foundry.Local.Runtime.WinML -# (win-x64, win-arm64 only — both built with --use_winml) +# Produces Microsoft.AI.Foundry.Local.Runtime (win-x64, win-arm64, linux-x64, +# osx-arm64). On Windows the artifacts carry the reg-free WinML 2.x runtime DLL, +# which pack.py forwards automatically. parameters: - name: ortVersion type: string - name: genaiVersion type: string -- name: variant - type: string - default: 'base' - values: ['base', 'winml'] steps: @@ -45,58 +39,30 @@ steps: } } -- ${{ if eq(parameters.variant, 'base') }}: - - task: PowerShell@2 - displayName: 'Pack NuGet package (base)' - inputs: - targetType: inline - pwsh: true - script: | - $ErrorActionPreference = 'Stop' - - $version = (Get-Content "$(Pipeline.Workspace)/version-info/sdkVersion.txt" -Raw).Trim() - Write-Host "Packing version: $version" - - $outDir = "$(Build.ArtifactStagingDirectory)/nuget" - New-Item -ItemType Directory -Force -Path $outDir | Out-Null - - python "$(Build.SourcesDirectory)/sdk_v2/cpp/nuget/pack.py" ` - --version "$version" ` - --package_id "Microsoft.AI.Foundry.Local.Runtime" ` - --ort_version "${{ parameters.ortVersion }}" ` - --genai_version "${{ parameters.genaiVersion }}" ` - --win_x64 "$(Pipeline.Workspace)/cpp-native-win-x64" ` - --win_arm64 "$(Pipeline.Workspace)/cpp-native-win-arm64" ` - --linux_x64 "$(Pipeline.Workspace)/cpp-native-linux-x64" ` - --osx_arm64 "$(Pipeline.Workspace)/cpp-native-osx-arm64" ` - --output_dir "$outDir" - - Write-Host "Generated packages:" - Get-ChildItem $outDir -Filter '*.nupkg' | ForEach-Object { Write-Host " $($_.Name)" } - -- ${{ if eq(parameters.variant, 'winml') }}: - - task: PowerShell@2 - displayName: 'Pack NuGet package (WinML)' - inputs: - targetType: inline - pwsh: true - script: | - $ErrorActionPreference = 'Stop' +- task: PowerShell@2 + displayName: 'Pack NuGet package' + inputs: + targetType: inline + pwsh: true + script: | + $ErrorActionPreference = 'Stop' - $version = (Get-Content "$(Pipeline.Workspace)/version-info/sdkVersion.txt" -Raw).Trim() - Write-Host "Packing version: $version" + $version = (Get-Content "$(Pipeline.Workspace)/version-info/sdkVersion.txt" -Raw).Trim() + Write-Host "Packing version: $version" - $outDir = "$(Build.ArtifactStagingDirectory)/nuget" - New-Item -ItemType Directory -Force -Path $outDir | Out-Null + $outDir = "$(Build.ArtifactStagingDirectory)/nuget" + New-Item -ItemType Directory -Force -Path $outDir | Out-Null - python "$(Build.SourcesDirectory)/sdk_v2/cpp/nuget/pack.py" ` - --version "$version" ` - --package_id "Microsoft.AI.Foundry.Local.Runtime.WinML" ` - --ort_version "${{ parameters.ortVersion }}" ` - --genai_version "${{ parameters.genaiVersion }}" ` - --win_x64 "$(Pipeline.Workspace)/cpp-native-win-x64-winml" ` - --win_arm64 "$(Pipeline.Workspace)/cpp-native-win-arm64-winml" ` - --output_dir "$outDir" + python "$(Build.SourcesDirectory)/sdk_v2/cpp/nuget/pack.py" ` + --version "$version" ` + --package_id "Microsoft.AI.Foundry.Local.Runtime" ` + --ort_version "${{ parameters.ortVersion }}" ` + --genai_version "${{ parameters.genaiVersion }}" ` + --win_x64 "$(Pipeline.Workspace)/cpp-native-win-x64" ` + --win_arm64 "$(Pipeline.Workspace)/cpp-native-win-arm64" ` + --linux_x64 "$(Pipeline.Workspace)/cpp-native-linux-x64" ` + --osx_arm64 "$(Pipeline.Workspace)/cpp-native-osx-arm64" ` + --output_dir "$outDir" - Write-Host "Generated packages:" - Get-ChildItem $outDir -Filter '*.nupkg' | ForEach-Object { Write-Host " $($_.Name)" } + Write-Host "Generated packages:" + Get-ChildItem $outDir -Filter '*.nupkg' | ForEach-Object { Write-Host " $($_.Name)" } diff --git a/.pipelines/v2/templates/steps-test-cs.yml b/.pipelines/v2/templates/steps-test-cs.yml index 73274e275..4bca80729 100644 --- a/.pipelines/v2/templates/steps-test-cs.yml +++ b/.pipelines/v2/templates/steps-test-cs.yml @@ -8,9 +8,6 @@ parameters: - name: flNugetDir type: string displayName: 'Path to directory containing the Foundry Local Runtime .nupkg' -- name: isWinML - type: boolean - default: false - name: testDataSharedDir type: string displayName: 'Absolute path to a checked-out test-data-shared working tree' @@ -81,7 +78,7 @@ steps: displayName: 'Authenticate NuGet feeds' # Per-job NuGet isolation to prevent "Central Directory corrupt" / file-locking -# errors when multiple C# test jobs (regular + WinML) run concurrently on the +# errors when multiple C# test jobs run concurrently on the # same reused agent. Keyed by $(System.JobId); cleaned on each run. - task: PowerShell@2 displayName: 'Set isolated NuGet packages path' @@ -117,7 +114,6 @@ steps: dotnet restore $proj ` --configfile "$(customNugetConfig)" ` - /p:UseWinML=${{ parameters.isWinML }} ` /p:FoundryLocalRuntimeVersion=$(packageVersion) ` /p:FoundryLocalNativeBinDir= ` /p:RuntimeIdentifiers=$rid @@ -125,7 +121,6 @@ steps: dotnet build $proj ` --no-restore --configuration Release ` - /p:UseWinML=${{ parameters.isWinML }} ` /p:FoundryLocalRuntimeVersion=$(packageVersion) ` /p:FoundryLocalNativeBinDir= ` /p:RuntimeIdentifiers=$rid @@ -161,15 +156,14 @@ steps: # Test TFMs: # * net9.0 covers the .NET (Core) runtime test surface. - # * net462 (Windows only, non-WinML) exercises the netstandard2.0 surface + # * net462 (Windows only) exercises the netstandard2.0 surface # + polyfills at runtime on .NET Framework. # The csproj also targets net8.0, but only as build-time coverage — the .NET # back-compat guarantee means a successful net9.0 test run validates the same # assemblies for net8.0 consumers. $frameworks = @('net9.0') $isWin = $IsWindows -or ($PSVersionTable.Platform -eq $null) - $isWinML = '${{ parameters.isWinML }}' -eq 'True' - if ($isWin -and -not $isWinML) { + if ($isWin) { $frameworks += 'net462' } @@ -178,7 +172,6 @@ steps: dotnet test $proj ` --no-build --configuration Release ` --framework $tfm ` - /p:UseWinML=${{ parameters.isWinML }} ` ${{ parameters.additionalTestArgs }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } diff --git a/.pipelines/v2/templates/steps-test-python.yml b/.pipelines/v2/templates/steps-test-python.yml index 9396a7cce..5dfd4bab6 100644 --- a/.pipelines/v2/templates/steps-test-python.yml +++ b/.pipelines/v2/templates/steps-test-python.yml @@ -12,9 +12,6 @@ parameters: - name: testDataSharedDir type: string displayName: 'Absolute path to a checked-out test-data-shared working tree' -- name: isWinML - type: boolean - default: false - name: pythonArchitecture type: string default: 'x64' @@ -56,8 +53,8 @@ steps: targetType: inline pwsh: true script: | - # Locate the wheel. Both variants land in their own per-job artifact - # so a single match is expected. + # Locate the wheel. Each job's wheel lands in its own artifact, so a + # single match is expected. $wheels = @(Get-ChildItem "${{ parameters.wheelDir }}" -Recurse -Filter '*.whl') if ($wheels.Count -eq 0) { throw "No .whl found under ${{ parameters.wheelDir }}" } if ($wheels.Count -gt 1) { diff --git a/README.md b/README.md index 405f45d93..b86e183cd 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,6 @@ User data never leaves the device, responses start immediately with zero network 1. Install the SDK: ```bash - # Windows (recommended for hardware acceleration) - npm install foundry-local-sdk-winml - - # macOS/linux npm install foundry-local-sdk ``` @@ -90,10 +86,6 @@ User data never leaves the device, responses start immediately with zero network 1. Install the SDK: ```bash - # Windows (recommended for hardware acceleration) - pip install foundry-local-sdk-winml - - # macOS/Linux pip install foundry-local-sdk ``` diff --git a/memories/repo/cs-local-packages.md b/memories/repo/cs-local-packages.md index 2851a696e..7285ecc51 100644 --- a/memories/repo/cs-local-packages.md +++ b/memories/repo/cs-local-packages.md @@ -14,11 +14,9 @@ From `sdk_v2/cs/`: ``` dotnet pack src/Microsoft.AI.Foundry.Local.csproj -o ../../local-packages /p:IsPacking=true /p:TreatWarningsAsErrors=false -c Release -dotnet pack src/Microsoft.AI.Foundry.Local.csproj -o ../../local-packages /p:IsPacking=true /p:UseWinML=true /p:TreatWarningsAsErrors=false -c Release ``` - `IsPacking=true` auto-sets `Version=0.5.0-dev.local.` (see `Microsoft.AI.Foundry.Local.csproj`). -- `UseWinML=true` flips `PackageId`/`AssemblyName` to `Microsoft.AI.Foundry.Local.WinML`; both SKUs share the `net8.0;net9.0` TFM set (WinML SKU omits `netstandard2.0`). -- Cross-platform + WinML are independent packages — most samples reference WinML on Windows and the cross-platform package elsewhere, so both must be packed. +- The single `Microsoft.AI.Foundry.Local` package targets `net8.0;net9.0;netstandard2.0` and bundles the WinML 2.x runtime on Windows automatically — there is no separate WinML SKU to pack. ## Full clean-rebuild ``` diff --git a/samples/cs/Directory.Packages.props b/samples/cs/Directory.Packages.props index 431b27a82..bc8bd3ea3 100644 --- a/samples/cs/Directory.Packages.props +++ b/samples/cs/Directory.Packages.props @@ -5,8 +5,7 @@ - - + diff --git a/samples/cs/README.md b/samples/cs/README.md index fb594717e..1fa48259e 100644 --- a/samples/cs/README.md +++ b/samples/cs/README.md @@ -1,11 +1,6 @@ # 🚀 Foundry Local C# Samples -These samples demonstrate how to use the Foundry Local C# SDK. Each sample uses a **unified project file** that automatically detects your operating system and selects the optimal NuGet package: - -- **Windows**: Uses `Microsoft.AI.Foundry.Local.WinML` for hardware acceleration via Windows ML. -- **macOS / Linux**: Uses `Microsoft.AI.Foundry.Local` for cross-platform support. - -Both packages provide the same APIs, so the same source code works on all platforms. +These samples demonstrate how to use the Foundry Local C# SDK. Each sample uses the `Microsoft.AI.Foundry.Local` NuGet package, which bundles the WinML runtime on Windows automatically. ## Samples diff --git a/samples/cs/audio-transcription-example/AudioTranscriptionExample.csproj b/samples/cs/audio-transcription-example/AudioTranscriptionExample.csproj index 12b6962be..68f4199c0 100644 --- a/samples/cs/audio-transcription-example/AudioTranscriptionExample.csproj +++ b/samples/cs/audio-transcription-example/AudioTranscriptionExample.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/embeddings/Embeddings.csproj b/samples/cs/embeddings/Embeddings.csproj index 5b67d2a59..d5a4063f8 100644 --- a/samples/cs/embeddings/Embeddings.csproj +++ b/samples/cs/embeddings/Embeddings.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/foundry-local-web-server-responses-vision/FoundryLocalWebServerResponsesVision.csproj b/samples/cs/foundry-local-web-server-responses-vision/FoundryLocalWebServerResponsesVision.csproj index 06e29a5d2..ead8de106 100644 --- a/samples/cs/foundry-local-web-server-responses-vision/FoundryLocalWebServerResponsesVision.csproj +++ b/samples/cs/foundry-local-web-server-responses-vision/FoundryLocalWebServerResponsesVision.csproj @@ -4,32 +4,10 @@ Exe enable enable - - - - - net9.0-windows10.0.18362.0 - ARM64;x64 - None - false - - - - net9.0 - - $(NETCoreSdkRuntimeIdentifier) - - - - - - - - - + diff --git a/samples/cs/foundry-local-web-server/FoundryLocalWebServer.csproj b/samples/cs/foundry-local-web-server/FoundryLocalWebServer.csproj index 9a6cd1bc2..1005ed07c 100644 --- a/samples/cs/foundry-local-web-server/FoundryLocalWebServer.csproj +++ b/samples/cs/foundry-local-web-server/FoundryLocalWebServer.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/live-audio-transcription/LiveAudioTranscriptionExample.csproj b/samples/cs/live-audio-transcription/LiveAudioTranscriptionExample.csproj index 6289e8614..42750f2bd 100644 --- a/samples/cs/live-audio-transcription/LiveAudioTranscriptionExample.csproj +++ b/samples/cs/live-audio-transcription/LiveAudioTranscriptionExample.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/model-management-example/ModelManagementExample.csproj b/samples/cs/model-management-example/ModelManagementExample.csproj index 5b67d2a59..d5a4063f8 100644 --- a/samples/cs/model-management-example/ModelManagementExample.csproj +++ b/samples/cs/model-management-example/ModelManagementExample.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/native-chat-completions/NativeChatCompletions.csproj b/samples/cs/native-chat-completions/NativeChatCompletions.csproj index 5b67d2a59..d5a4063f8 100644 --- a/samples/cs/native-chat-completions/NativeChatCompletions.csproj +++ b/samples/cs/native-chat-completions/NativeChatCompletions.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tool-calling-foundry-local-sdk/ToolCallingFoundryLocalSdk.csproj b/samples/cs/tool-calling-foundry-local-sdk/ToolCallingFoundryLocalSdk.csproj index 5b67d2a59..d5a4063f8 100644 --- a/samples/cs/tool-calling-foundry-local-sdk/ToolCallingFoundryLocalSdk.csproj +++ b/samples/cs/tool-calling-foundry-local-sdk/ToolCallingFoundryLocalSdk.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tool-calling-foundry-local-web-server/ToolCallingFoundryLocalWebServer.csproj b/samples/cs/tool-calling-foundry-local-web-server/ToolCallingFoundryLocalWebServer.csproj index 9a6cd1bc2..1005ed07c 100644 --- a/samples/cs/tool-calling-foundry-local-web-server/ToolCallingFoundryLocalWebServer.csproj +++ b/samples/cs/tool-calling-foundry-local-web-server/ToolCallingFoundryLocalWebServer.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj b/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj index 20467e09b..972ec6210 100644 --- a/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj +++ b/samples/cs/tutorial-chat-assistant/TutorialChatAssistant.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj b/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj index 20467e09b..972ec6210 100644 --- a/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj +++ b/samples/cs/tutorial-document-summarizer/TutorialDocumentSummarizer.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj b/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj index 20467e09b..972ec6210 100644 --- a/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj +++ b/samples/cs/tutorial-tool-calling/TutorialToolCalling.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj b/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj index 20467e09b..972ec6210 100644 --- a/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj +++ b/samples/cs/tutorial-voice-to-text/TutorialVoiceToText.csproj @@ -7,13 +7,7 @@ net9.0 - - - - - - - + diff --git a/samples/cs/verify-winml/README.md b/samples/cs/verify-winml/README.md index 88540fbc8..4a0ff47bb 100644 --- a/samples/cs/verify-winml/README.md +++ b/samples/cs/verify-winml/README.md @@ -11,10 +11,10 @@ EP-backed model variants and finishes with one native streaming chat check. ## Build & Run -This sample uses the `Microsoft.AI.Foundry.Local.WinML` SDK package selected by +This sample uses the `Microsoft.AI.Foundry.Local` SDK package selected by the shared central package versions. The SDK package owns its native -`Microsoft.AI.Foundry.Local.Core.WinML` dependency, so it restores the matching -Core package transitively. +WinML runtime dependency on Windows, so it restores the matching +runtime package transitively. ```bash dotnet run diff --git a/samples/cs/verify-winml/VerifyWinML.csproj b/samples/cs/verify-winml/VerifyWinML.csproj index 860aa6740..dc4f1df19 100644 --- a/samples/cs/verify-winml/VerifyWinML.csproj +++ b/samples/cs/verify-winml/VerifyWinML.csproj @@ -7,15 +7,7 @@ net9.0 - - $(NETCoreSdkRuntimeIdentifier) - - - - - - - + diff --git a/samples/js/README.md b/samples/js/README.md index 0b7d677c4..98ee6b498 100644 --- a/samples/js/README.md +++ b/samples/js/README.md @@ -48,4 +48,4 @@ These samples demonstrate how to use the Foundry Local JavaScript SDK (`foundry- ``` > [!TIP] -> Each sample's `package.json` includes `foundry-local-sdk` as a dependency and `foundry-local-sdk-winml` as an optional dependency. On **Windows**, the WinML variant installs automatically for broader hardware acceleration. On **macOS and Linux**, the standard SDK is used. Just run `npm install` — platform detection is handled for you. +> Each sample's `package.json` includes the single `foundry-local-sdk` package. On **Windows**, it bundles WinML hardware acceleration automatically. Just run `npm install` — no separate package or flag is needed. diff --git a/samples/js/audio-transcription-example/package.json b/samples/js/audio-transcription-example/package.json index 14a2aafab..4bcaa8d15 100644 --- a/samples/js/audio-transcription-example/package.json +++ b/samples/js/audio-transcription-example/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/chat-and-audio-foundry-local/package.json b/samples/js/chat-and-audio-foundry-local/package.json index 7404589ec..700775a62 100644 --- a/samples/js/chat-and-audio-foundry-local/package.json +++ b/samples/js/chat-and-audio-foundry-local/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/copilot-sdk-foundry-local/package.json b/samples/js/copilot-sdk-foundry-local/package.json index b2457d9aa..d01a25a91 100644 --- a/samples/js/copilot-sdk-foundry-local/package.json +++ b/samples/js/copilot-sdk-foundry-local/package.json @@ -12,9 +12,6 @@ "foundry-local-sdk": "latest", "zod": "^3.0.0" }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" - }, "devDependencies": { "tsx": "^4.0.0", "typescript": "^5.0.0" diff --git a/samples/js/electron-chat-application/package.json b/samples/js/electron-chat-application/package.json index 3609b2eea..4c2461ea2 100644 --- a/samples/js/electron-chat-application/package.json +++ b/samples/js/electron-chat-application/package.json @@ -12,9 +12,6 @@ "highlight.js": "^11.11.1", "marked": "^15.0.6" }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" - }, "devDependencies": { "electron": "^42.3.3" } diff --git a/samples/js/embeddings/package.json b/samples/js/embeddings/package.json index 8353cb655..e0215fa2e 100644 --- a/samples/js/embeddings/package.json +++ b/samples/js/embeddings/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/langchain-integration-example/package.json b/samples/js/langchain-integration-example/package.json index bb5fb635e..4511cdf3a 100644 --- a/samples/js/langchain-integration-example/package.json +++ b/samples/js/langchain-integration-example/package.json @@ -10,8 +10,5 @@ "@langchain/core": "latest", "@langchain/openai": "latest", "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/native-chat-completions/package.json b/samples/js/native-chat-completions/package.json index eeba0acd1..5a3aa0ccd 100644 --- a/samples/js/native-chat-completions/package.json +++ b/samples/js/native-chat-completions/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/tool-calling-foundry-local/package.json b/samples/js/tool-calling-foundry-local/package.json index 6ae9c0322..d31258b38 100644 --- a/samples/js/tool-calling-foundry-local/package.json +++ b/samples/js/tool-calling-foundry-local/package.json @@ -8,8 +8,5 @@ "dependencies": { "foundry-local-sdk": "latest", "openai": "^6.25.0" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/tutorial-chat-assistant/package.json b/samples/js/tutorial-chat-assistant/package.json index 8a36a2884..2fcb60f62 100644 --- a/samples/js/tutorial-chat-assistant/package.json +++ b/samples/js/tutorial-chat-assistant/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/tutorial-document-summarizer/package.json b/samples/js/tutorial-document-summarizer/package.json index c97e416fb..af370c4a4 100644 --- a/samples/js/tutorial-document-summarizer/package.json +++ b/samples/js/tutorial-document-summarizer/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/tutorial-tool-calling/package.json b/samples/js/tutorial-tool-calling/package.json index ab7f62d6c..ee44785c6 100644 --- a/samples/js/tutorial-tool-calling/package.json +++ b/samples/js/tutorial-tool-calling/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/tutorial-voice-to-text/package.json b/samples/js/tutorial-voice-to-text/package.json index 3efb0d4bd..19a2d0bf7 100644 --- a/samples/js/tutorial-voice-to-text/package.json +++ b/samples/js/tutorial-voice-to-text/package.json @@ -8,8 +8,5 @@ }, "dependencies": { "foundry-local-sdk": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/js/web-server-example/package.json b/samples/js/web-server-example/package.json index 336705142..f97dee96d 100644 --- a/samples/js/web-server-example/package.json +++ b/samples/js/web-server-example/package.json @@ -9,8 +9,5 @@ "dependencies": { "foundry-local-sdk": "latest", "openai": "latest" - }, - "optionalDependencies": { - "foundry-local-sdk-winml": "latest" } } diff --git a/samples/python/README.md b/samples/python/README.md index 49e99c8a6..6be2c7c7c 100644 --- a/samples/python/README.md +++ b/samples/python/README.md @@ -45,4 +45,4 @@ These samples demonstrate how to use Foundry Local with Python. ``` > [!TIP] -> Each sample's `requirements.txt` uses environment markers to automatically install the right SDK for your platform. On **Windows**, `foundry-local-sdk-winml` is installed for broader hardware acceleration. On **macOS and Linux**, the standard `foundry-local-sdk` is used. Just run `pip install -r requirements.txt` — platform detection is handled for you. +> Each sample's `requirements.txt` installs the single `foundry-local-sdk` package. On **Windows**, it bundles WinML hardware acceleration automatically. Just run `pip install -r requirements.txt` — no separate package or flag is needed. diff --git a/samples/python/audio-transcription/requirements.txt b/samples/python/audio-transcription/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/audio-transcription/requirements.txt +++ b/samples/python/audio-transcription/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/embeddings/requirements.txt b/samples/python/embeddings/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/embeddings/requirements.txt +++ b/samples/python/embeddings/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/langchain-integration/requirements.txt b/samples/python/langchain-integration/requirements.txt index 9a6b61817..0ded700aa 100644 --- a/samples/python/langchain-integration/requirements.txt +++ b/samples/python/langchain-integration/requirements.txt @@ -1,5 +1,4 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk openai langchain-openai langchain-core diff --git a/samples/python/live-audio-transcription/requirements.txt b/samples/python/live-audio-transcription/requirements.txt index 6677976f6..70244f878 100644 --- a/samples/python/live-audio-transcription/requirements.txt +++ b/samples/python/live-audio-transcription/requirements.txt @@ -1,5 +1,4 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk # pyaudio is optional — only needed for live microphone capture. # Install manually: pip install pyaudio # The sample falls back to synthetic audio if pyaudio is unavailable. diff --git a/samples/python/native-chat-completions/requirements.txt b/samples/python/native-chat-completions/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/native-chat-completions/requirements.txt +++ b/samples/python/native-chat-completions/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/tool-calling/requirements.txt b/samples/python/tool-calling/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/tool-calling/requirements.txt +++ b/samples/python/tool-calling/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/tutorial-chat-assistant/requirements.txt b/samples/python/tutorial-chat-assistant/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/tutorial-chat-assistant/requirements.txt +++ b/samples/python/tutorial-chat-assistant/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/tutorial-document-summarizer/requirements.txt b/samples/python/tutorial-document-summarizer/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/tutorial-document-summarizer/requirements.txt +++ b/samples/python/tutorial-document-summarizer/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/tutorial-tool-calling/requirements.txt b/samples/python/tutorial-tool-calling/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/tutorial-tool-calling/requirements.txt +++ b/samples/python/tutorial-tool-calling/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/tutorial-voice-to-text/requirements.txt b/samples/python/tutorial-voice-to-text/requirements.txt index 7602a48b7..c79aa6dd6 100644 --- a/samples/python/tutorial-voice-to-text/requirements.txt +++ b/samples/python/tutorial-voice-to-text/requirements.txt @@ -1,2 +1 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk diff --git a/samples/python/verify-winml/README.md b/samples/python/verify-winml/README.md index eabfd7201..3a7320ebb 100644 --- a/samples/python/verify-winml/README.md +++ b/samples/python/verify-winml/README.md @@ -17,8 +17,8 @@ If you want to reuse your existing Python environment instead, delete that environment's `Lib\site-packages\foundry_local_core` directory before reinstalling so stale native files are not left behind. -`requirements.txt` installs the WinML SDK variant, which brings the matching -WinML native package transitively. Either install path is enough: +`requirements.txt` installs the single `foundry-local-sdk` package, which bundles +WinML hardware acceleration on Windows automatically. Either install path is enough: ```bash python -m venv .venv diff --git a/samples/python/verify-winml/requirements.txt b/samples/python/verify-winml/requirements.txt index 481d9dc46..c79aa6dd6 100644 --- a/samples/python/verify-winml/requirements.txt +++ b/samples/python/verify-winml/requirements.txt @@ -1 +1 @@ -foundry-local-sdk-winml +foundry-local-sdk diff --git a/samples/python/web-server-responses/README.md b/samples/python/web-server-responses/README.md index 95666d910..9c76df271 100644 --- a/samples/python/web-server-responses/README.md +++ b/samples/python/web-server-responses/README.md @@ -18,8 +18,7 @@ pip install -r requirements.txt That installs: -- `foundry-local-sdk` on non-Windows platforms -- `foundry-local-sdk-winml` on Windows +- `foundry-local-sdk`, which bundles WinML hardware acceleration on Windows automatically - `openai` The sample downloads/registers Foundry Local execution providers and downloads the `qwen2.5-0.5b` model the first time it runs. diff --git a/samples/python/web-server-responses/requirements.txt b/samples/python/web-server-responses/requirements.txt index db870f608..5a0f14ae1 100644 --- a/samples/python/web-server-responses/requirements.txt +++ b/samples/python/web-server-responses/requirements.txt @@ -1,3 +1,2 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk openai diff --git a/samples/python/web-server/requirements.txt b/samples/python/web-server/requirements.txt index db870f608..5a0f14ae1 100644 --- a/samples/python/web-server/requirements.txt +++ b/samples/python/web-server/requirements.txt @@ -1,3 +1,2 @@ -foundry-local-sdk; sys_platform != "win32" -foundry-local-sdk-winml; sys_platform == "win32" +foundry-local-sdk openai diff --git a/sdk_v2/DEVELOPMENT.md b/sdk_v2/DEVELOPMENT.md index bc8afb6c2..febd9a8db 100644 --- a/sdk_v2/DEVELOPMENT.md +++ b/sdk_v2/DEVELOPMENT.md @@ -13,7 +13,7 @@ If that passes, your machine is correctly configured. ## Prerequisites All four SDKs (C++, C#, Python, JS/TS) build on **Windows**, **Linux**, and -**macOS**. The WinML variant (`-UseWinml`) is Windows-only. +**macOS**. WinML 2.x hardware acceleration is bundled automatically on Windows. ### All platforms @@ -23,7 +23,7 @@ All four SDKs (C++, C#, Python, JS/TS) build on **Windows**, **Linux**, and | CMake | 3.20 | Driven by `sdk_v2/cpp/build.py`; do not invoke `cmake --build` directly. | | vcpkg | recent | Set `VCPKG_ROOT`, or use the copy bundled with Visual Studio (auto-detected). | | Python | 3.11–3.14, **64-bit** | Required by `build.py` and for the Python SDK. 32-bit Python will not work. | -| .NET SDK | 9.0 | The SDK targets `net8.0;net9.0;netstandard2.0`; the test project additionally targets `net462` on Windows (via the .NET Framework Targeting Pack from VS); samples target `net9.0`. The WinML SKU shares the same SDK TFMs (minus `netstandard2.0`) — the Windows OS-version floor for WinML 2.x is enforced by the native runtime (`LoadLibraryW` + `RtlGetVersion` in `winml_ep_bootstrapper.cc`), not by a .NET TFM. | +| .NET SDK | 9.0 | The SDK targets `net8.0;net9.0;netstandard2.0`; the test project additionally targets `net462` on Windows (via the .NET Framework Targeting Pack from VS); samples target `net9.0`. The single package bundles WinML 2.x on Windows — its OS-version floor is enforced by the native runtime (`LoadLibraryW` + `RtlGetVersion` in `winml_ep_bootstrapper.cc`), not by a .NET TFM. | | Node.js | 20 LTS or newer | Brings `npm`. The JS SDK declares `"engines": { "node": ">=20" }`. | | PowerShell | 7+ (`pwsh`) | The one-shot script and `samples/js/test-v2.ps1` are written for PowerShell 7. | @@ -64,8 +64,8 @@ install per-SDK package dependencies on first run: | SDK | What runs | | ------ | -------------------------------------------------------------------------------------- | -| C++ | `python build.py [--config ...] [--use_winml] [--skip_tests]` — configure + build + ctest. vcpkg restores native deps; ORT/GenAI come from NuGet via FetchContent (versions from `sdk_v2/deps_versions.json`). | -| C# | `dotnet test Microsoft.AI.Foundry.Local.SDK.sln -c Release [-p:UseWinML=true]` — restores NuGet packages on demand. | +| C++ | `python build.py [--config ...] [--skip_tests]` — configure + build + ctest. vcpkg restores native deps; ORT/GenAI come from NuGet via FetchContent (versions from `sdk_v2/deps_versions.json`). | +| C# | `dotnet test Microsoft.AI.Foundry.Local.SDK.sln -c Release` — restores NuGet packages on demand. | | Python | `python -m pip install -e .[dev]` (compiles the cffi extension; needs MSVC/Clang) → `python -m pytest test/`. | | JS | `npm install` (runs `node-gyp` against the C++ build output) → `npm run build` → `npm test` (vitest). | @@ -75,9 +75,6 @@ install per-SDK package dependencies on first run: # Full build + test, default config (RelWithDebInfo / Release). pwsh ./build_and_test_all.ps1 -# WinML variant across all SDKs (Windows only). -pwsh ./build_and_test_all.ps1 -UseWinml - # Just rebuild the native and the JS bindings, skip the slow C++ test pass. pwsh ./build_and_test_all.ps1 -Only cpp,js -SkipCppTests @@ -102,7 +99,3 @@ See `pwsh ./build_and_test_all.ps1 -?` for the full parameter list. or pass `--build_dir` to `build.py`. The C# tests pin an absolute path to `sdk_v2/cpp/build///`; bypassing `build.py` puts the binary somewhere else. -* **Switching between WinML and non-WinML** — the C++ FetchContent NuGet - packages (`Microsoft.ML.OnnxRuntime.Foundry` vs `.WinML`) are cached and - do not auto-refresh on variant flip. If you hit linker errors after - toggling `-UseWinml`, wipe `sdk_v2/cpp/build/` and rerun. diff --git a/sdk_v2/build_and_test_all.ps1 b/sdk_v2/build_and_test_all.ps1 index e01561bdd..cb44da791 100644 --- a/sdk_v2/build_and_test_all.ps1 +++ b/sdk_v2/build_and_test_all.ps1 @@ -15,14 +15,6 @@ unless -ContinueOnError is supplied, and prints a per-SDK pass/fail summary at the end. -.PARAMETER UseWinml - Build the WinML variant across all SDKs: - * C++: passes --use_winml to build.py - * C#: passes -p:UseWinML=true to dotnet test - * Python: sets FL_PYTHON_PACKAGE_NAME=foundry-local-sdk-winml before pip install - * JS: rebuilds the native addon against the WinML C++ build - Windows only. - .PARAMETER Skip SDKs to skip. Any of: cpp, cs, python, js. @@ -39,11 +31,7 @@ .EXAMPLE pwsh ./build_and_test_all.ps1 - # Full build + test, no WinML. - -.EXAMPLE - pwsh ./build_and_test_all.ps1 -UseWinml - # Full build + test against the WinML variant. + # Full build + test of all SDKs. .EXAMPLE pwsh ./build_and_test_all.ps1 -Only cpp,js -SkipCppTests @@ -51,7 +39,6 @@ #> [CmdletBinding()] param( - [switch] $UseWinml, [ValidateSet('cpp', 'cs', 'python', 'js')] [string[]] $Skip = @(), [ValidateSet('cpp', 'cs', 'python', 'js')] @@ -71,10 +58,6 @@ $csDir = Join-Path $sdkRoot 'cs' $pythonDir = Join-Path $sdkRoot 'python' $jsDir = Join-Path $sdkRoot 'js' -if ($UseWinml -and -not $IsWindows) { - throw "-UseWinml is Windows-only." -} - # Resolve which SDKs to run. $all = @('cpp', 'cs', 'python', 'js') if ($Only) { @@ -100,7 +83,7 @@ function Invoke-Step { ) Write-Host "" Write-Host "============================================================" -ForegroundColor Cyan - Write-Host "==> [$Name] start (UseWinml=$UseWinml)" -ForegroundColor Cyan + Write-Host "==> [$Name] start" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan $start = Get-Date $ok = $false @@ -133,7 +116,6 @@ try { if ('cpp' -in $targets) { Invoke-Step 'cpp' { $args = @('build.py', '--config', $Config) - if ($UseWinml) { $args += '--use_winml' } if ($SkipCppTests) { $args += '--skip_tests' } Push-Location $cppDir try { @@ -152,16 +134,13 @@ try { try { $sln = 'Microsoft.AI.Foundry.Local.SDK.sln' $buildArgs = @('build', $sln, '-c', $dotnetConfig, '--nologo') - if ($UseWinml) { $buildArgs += '-p:UseWinML=true' } dotnet @buildArgs if ($LASTEXITCODE -ne 0) { throw "dotnet build exit $LASTEXITCODE" } - # The test csproj multi-targets net8.0/net9.0 (and net462 on Windows - # non-WinML) for build coverage; run the .NET (Core) test suite once - # on net9.0 (back-compat covers net8.0 consumers) plus net462 on - # Windows non-WinML to exercise the netstandard polyfills at runtime. + # Test on net9.0 (back-compat covers net8.0 consumers); on Windows + # also run net462 to exercise the netstandard2.0 polyfills. $frameworks = @('net9.0') - if ($IsWindows -and -not $UseWinml) { $frameworks += 'net462' } + if ($IsWindows) { $frameworks += 'net462' } foreach ($tfm in $frameworks) { Write-Host "=== dotnet test --framework $tfm ===" @@ -172,7 +151,6 @@ try { '--framework', $tfm, '--nologo' ) - if ($UseWinml) { $testArgs += '-p:UseWinML=true' } dotnet @testArgs if ($LASTEXITCODE -ne 0) { throw "dotnet test ($tfm) exit $LASTEXITCODE" } } @@ -214,8 +192,6 @@ print(sys.executable) $env:Platform = 'x64' } - $env:FL_PYTHON_PACKAGE_NAME = - if ($UseWinml) { 'foundry-local-sdk-winml' } else { 'foundry-local-sdk' } try { python -m pip install -e '.[dev]' if ($LASTEXITCODE -ne 0) { throw "pip install exit $LASTEXITCODE" } @@ -223,7 +199,6 @@ print(sys.executable) python -m pytest test/ -v if ($LASTEXITCODE -ne 0) { throw "pytest exit $LASTEXITCODE" } } finally { - Remove-Item Env:FL_PYTHON_PACKAGE_NAME -ErrorAction SilentlyContinue if ($null -eq $restoreTgt) { Remove-Item Env:VSCMD_ARG_TGT_ARCH -ErrorAction SilentlyContinue } else { $env:VSCMD_ARG_TGT_ARCH = $restoreTgt } if ($null -eq $restoreHost) { Remove-Item Env:VSCMD_ARG_HOST_ARCH -ErrorAction SilentlyContinue } else { $env:VSCMD_ARG_HOST_ARCH = $restoreHost } if ($null -eq $restorePlat) { Remove-Item Env:Platform -ErrorAction SilentlyContinue } else { $env:Platform = $restorePlat } @@ -241,8 +216,7 @@ print(sys.executable) npm install if ($LASTEXITCODE -ne 0) { throw "npm install exit $LASTEXITCODE" } - # JS picks up the native library copied from the C++ build dir; - # the WinML/non-WinML distinction is whichever C++ build ran above. + # JS picks up the native library copied from the C++ build dir. npm run build if ($LASTEXITCODE -ne 0) { throw "npm run build exit $LASTEXITCODE" } diff --git a/sdk_v2/cpp/CMakeLists.txt b/sdk_v2/cpp/CMakeLists.txt index 671c7648f..6faa7fe19 100644 --- a/sdk_v2/cpp/CMakeLists.txt +++ b/sdk_v2/cpp/CMakeLists.txt @@ -43,7 +43,6 @@ endif() option(FOUNDRY_LOCAL_BUILD_TESTS "Build unit tests" ON) option(FOUNDRY_LOCAL_BUILD_EXAMPLES "Build example programs" ON) option(FOUNDRY_LOCAL_BUILD_SERVICE "Build web service support (requires oat++)" ON) -option(FOUNDRY_LOCAL_USE_WINML "Enable the WinML 2.x EP catalog (Microsoft.Windows.AI.MachineLearning) for hardware EP discovery" OFF) option(FOUNDRY_LOCAL_ENABLE_ASAN "Enable AddressSanitizer + UndefinedBehaviorSanitizer (Linux only)" OFF) # Android: interactive examples and host tools don't run on device @@ -81,9 +80,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(OnnxRuntimeGenAI REQUIRED) find_package(OnnxRuntime REQUIRED) -# WinML EP Catalog — Windows-only, for EP discovery and download. Only fetched -# when the WinML SKU is requested so non-WinML builds don't pull the catalog DLL. -if(FOUNDRY_LOCAL_USE_WINML) +# WinML EP Catalog — Windows-only, for hardware EP discovery and download. The +# WinML 2.x runtime is reg-free, delay-loaded, and degrades gracefully when the +# DLL is absent, so it is always included on Windows; other platforms skip it. +if(WIN32) find_package(WinMLEpCatalog) endif() @@ -111,10 +111,10 @@ else() endif() # WinML EP bootstrapper is only built when the WinML EP catalog package is -# available — the source unconditionally references its headers/types. Other -# Windows builds (no WinML SKU) skip it entirely; manager.cc gates the call -# site on FOUNDRY_LOCAL_HAS_EP_CATALOG so DiscoverProviders is only invoked -# when this translation unit is linked in. +# available — the source unconditionally references its headers/types, which +# exist only on Windows. manager.cc gates the call site on +# FOUNDRY_LOCAL_HAS_EP_CATALOG so DiscoverProviders is only invoked when this +# translation unit is linked in. if(WinMLEpCatalog_FOUND) list(APPEND FOUNDRY_LOCAL_PLATFORM_SOURCES src/ep_detection/winml_ep_bootstrapper.cc @@ -395,9 +395,10 @@ if(TARGET OnnxRuntime::OnnxRuntime) # macOS: copy dylibs so consumers that only link libfoundry_local.dylib (e.g. cache_only_tests) find the correct # ORT version instead of any system-installed ORT, which would cause an Ort::InitApi() version mismatch. # - # The ORT nupkg's libonnxruntime.dylib has install_name @rpath/libonnxruntime..dylib (the major version - # is the soname; e.g. libonnxruntime.1.dylib), so foundry_local records exactly that name as its ORT dependency. - # Copy the real library under the soname so dyld resolves it at load time. + # The ORT nupkg ships the library as libonnxruntime.dylib with install_name @rpath/libonnxruntime..dylib + # (the major version is the soname; e.g. libonnxruntime.1.dylib). foundry_local records that soname as its ORT + # dependency, while GenAI dlopen()s the unversioned libonnxruntime.dylib at runtime — so we keep the shipped file + # under its own name and add a soname symlink beside it. Mirrors the Linux libonnxruntime.so / .so.1 layout below. string(REGEX MATCH "^[0-9]+" ORT_VERSION_MAJOR "${ORT_VERSION}") add_custom_command(TARGET foundry_local POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different @@ -405,14 +406,10 @@ if(TARGET OnnxRuntime::OnnxRuntime) $ COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ORT_LIB_DIR}/libonnxruntime.dylib" - "$/libonnxruntime.${ORT_VERSION_MAJOR}.dylib" - ) - - # Unversioned symlink: GenAI dlopen()s "libonnxruntime.dylib" at runtime, and -lonnxruntime resolves it at link time. - add_custom_command(TARGET foundry_local POST_BUILD + $ COMMAND ${CMAKE_COMMAND} -E create_symlink - libonnxruntime.${ORT_VERSION_MAJOR}.dylib - $/libonnxruntime.dylib + libonnxruntime.dylib + $/libonnxruntime.${ORT_VERSION_MAJOR}.dylib ) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID) # Linux and Android both use .so files diff --git a/sdk_v2/cpp/build.py b/sdk_v2/cpp/build.py index 82acbff4f..b1274cd60 100644 --- a/sdk_v2/cpp/build.py +++ b/sdk_v2/cpp/build.py @@ -160,17 +160,10 @@ class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescript parser.add_argument( "--skip_service", action="store_true", help="Skip building web service support (oat++)." ) - parser.add_argument( - "--use_winml", action="store_true", - help="Enable the WinML EP catalog (Microsoft.Windows.AI.MachineLearning, WinML 2.x reg-free " - "runtime) for hardware EP discovery. ORT itself still comes from Microsoft.ML.OnnxRuntime.Foundry; " - "this flag only adds the WinML EP catalog client.", - ) parser.add_argument( "--winml_sdk_version", default=None, type=str, - help="Version of Microsoft.Windows.AI.MachineLearning NuGet package (used for the WinML EP " - "catalog when --use_winml is set; defaults to the version pinned in " - "FindWinMLEpCatalog.cmake).", + help="Override the Microsoft.Windows.AI.MachineLearning NuGet version for the WinML EP " + "catalog (Windows only). Defaults to the version pinned in deps_versions.json.", ) # Cross-compilation (mutually exclusive targets) @@ -462,13 +455,10 @@ def configure(args: argparse.Namespace) -> None: if build_tests == "ON": command += ["-DVCPKG_MANIFEST_FEATURES=tests"] - if args.use_winml: - command += ["-DFOUNDRY_LOCAL_USE_WINML=ON"] - if args.winml_sdk_version: - command += [f"-DWINML_EP_CATALOG_VERSION={args.winml_sdk_version}"] - else: - # Pass explicitly so a re-configure without the flag clears any cached ON value. - command += ["-DFOUNDRY_LOCAL_USE_WINML=OFF"] + # WinML EP catalog is enabled automatically on Windows by CMake. Allow an + # optional version override for the Microsoft.Windows.AI.MachineLearning NuGet. + if args.winml_sdk_version: + command += [f"-DWINML_EP_CATALOG_VERSION={args.winml_sdk_version}"] if args.ort_home: command += [f"-DORT_HOME={args.ort_home}"] diff --git a/sdk_v2/cpp/cmake/FindOnnxRuntime.cmake b/sdk_v2/cpp/cmake/FindOnnxRuntime.cmake index 197894e01..f11385873 100644 --- a/sdk_v2/cpp/cmake/FindOnnxRuntime.cmake +++ b/sdk_v2/cpp/cmake/FindOnnxRuntime.cmake @@ -4,8 +4,7 @@ # Sources ORT from Microsoft.ML.OnnxRuntime.Foundry (or Microsoft.ML.OnnxRuntime # on Android) via FetchContent — nuget.org for releases, the ORT-Nightly ADO # feed for -dev- versions. The version comes from sdk_v2/deps_versions.json and -# is shared by WinML and non-WinML builds; FOUNDRY_LOCAL_USE_WINML only gates -# the WinML EP catalog in FindWinMLEpCatalog.cmake. +# is shared by all platforms. # # Creates an IMPORTED target: OnnxRuntime::OnnxRuntime @@ -41,25 +40,6 @@ else() message(FATAL_ERROR "Unsupported platform for OnnxRuntime: ${CMAKE_GENERATOR_PLATFORM} on ${CMAKE_SYSTEM_NAME}") endif() -if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") - # WinML is only available on Windows - set(FOUNDRY_LOCAL_USE_WINML OFF) -endif() - -if(FOUNDRY_LOCAL_USE_WINML) - # FOUNDRY_LOCAL_USE_WINML opts in to the WinML EP catalog (see FindWinMLEpCatalog.cmake) but - # does NOT change where ORT comes from. We always link against our own ORT - # (Microsoft.ML.OnnxRuntime.Foundry) because it enables CUDA and WebGPU EPs. - # - # Which onnxruntime.dll the process actually binds to at runtime is determined by the - # binding-side preload contract (see sdk_v2/cpp/docs/OrtRuntimeLoading.md), not by build - # layout. Co-location of our onnxruntime.dll next to foundry_local.dll keeps in-tree - # tests and examples zero-config, but is not a correctness guarantee for arbitrary - # deployments — bindings preload the intended onnxruntime.dll by absolute path before - # loading foundry_local. - message(STATUS "FOUNDRY_LOCAL_USE_WINML=ON: WinML EP catalog enabled; ORT still sourced from Microsoft.ML.OnnxRuntime.Foundry") -endif() - if(ORT_HOME) # Use a pre-extracted ORT directory (e.g. from build.py --ort_home). # Android: expects headers/ and jni// from an extracted AAR. @@ -240,7 +220,7 @@ else() set_target_properties(OnnxRuntime::OnnxRuntime PROPERTIES IMPORTED_IMPLIB "${_ORT_LIB_DIR}/onnxruntime.lib" ) - # On Windows, the runtime DLL sits next to the import lib for both flavors. + # On Windows, the runtime DLL sits next to the import lib. if(NOT _ORT_DLL_DIR) set(_ORT_DLL_DIR "${_ORT_LIB_DIR}") endif() diff --git a/sdk_v2/cpp/cmake/FindWinMLEpCatalog.cmake b/sdk_v2/cpp/cmake/FindWinMLEpCatalog.cmake index 78793feeb..dc732a4ff 100644 --- a/sdk_v2/cpp/cmake/FindWinMLEpCatalog.cmake +++ b/sdk_v2/cpp/cmake/FindWinMLEpCatalog.cmake @@ -41,8 +41,8 @@ endif() # The package's own CMake config FATAL_ERRORs on architectures it doesn't ship # binaries for (anything other than x64/ARM64). Pre-check so we degrade to a soft -# disable instead of halting configuration when someone builds e.g. ARM64EC with -# FOUNDRY_LOCAL_USE_WINML=ON. +# disable instead of halting configuration when someone builds e.g. ARM64EC on +# Windows. if(CMAKE_GENERATOR_PLATFORM) string(TOUPPER "${CMAKE_GENERATOR_PLATFORM}" _WINML_PLATFORM_UPPER) elseif(CMAKE_VS_PLATFORM_NAME) diff --git a/sdk_v2/cpp/docs/CppPortGuide.md b/sdk_v2/cpp/docs/CppPortGuide.md index 5eaac27af..3a3506046 100644 --- a/sdk_v2/cpp/docs/CppPortGuide.md +++ b/sdk_v2/cpp/docs/CppPortGuide.md @@ -474,7 +474,6 @@ It ensures all threads are joined during shutdown. The C# equivalent relies on FOUNDRY_LOCAL_BUILD_TESTS=ON # Unit tests (default ON) FOUNDRY_LOCAL_BUILD_EXAMPLES=ON # Example programs (default ON) FOUNDRY_LOCAL_BUILD_SERVICE=ON # Web service (requires oatpp) (default ON) -FOUNDRY_LOCAL_USE_WINML=OFF # WinML support ``` --- diff --git a/sdk_v2/cpp/docs/EpDetectionPlan.md b/sdk_v2/cpp/docs/EpDetectionPlan.md index 935d3c53b..26433cbd2 100644 --- a/sdk_v2/cpp/docs/EpDetectionPlan.md +++ b/sdk_v2/cpp/docs/EpDetectionPlan.md @@ -141,7 +141,7 @@ STDAPI WinMLEpEnsureReadyAsync(WinMLEpHandle ep, WinMLAsyncBlock* async); - Model catalog headers (we have our own) **CMake integration:** -- `find_package(WinMLEpCatalog)` — gated behind `FOUNDRY_LOCAL_USE_WINML` +- `find_package(WinMLEpCatalog)` — gated behind `if(WIN32)` (always enabled on Windows) - Windows-only. - Linked with `/DELAYLOAD:Microsoft.Windows.AI.MachineLearning.dll` — the DLL may not be present on older systems diff --git a/sdk_v2/cpp/nuget/pack.py b/sdk_v2/cpp/nuget/pack.py index 205d6d7fb..902ba288a 100644 --- a/sdk_v2/cpp/nuget/pack.py +++ b/sdk_v2/cpp/nuget/pack.py @@ -43,12 +43,11 @@ # primary library (plus, on Windows, .pdb and .lib companions consumed by the # Python wheel build). We copy that one file into runtimes//native/. # -# WinML builds add one extra sibling DLL — Microsoft.Windows.AI.MachineLearning.dll -# — that ships the reg-free WinML 2.x runtime. The pipeline only stages it for -# WinML builds (the cmake post-build copy in sdk_v2/cpp/CMakeLists.txt drops it -# next to foundry_local.dll), so we forward any entry from OPTIONAL_SIBLINGS that -# happens to be present in the upstream artifact dir; absent files are silently -# skipped. +# On Windows the build also drops Microsoft.Windows.AI.MachineLearning.dll — the +# reg-free WinML 2.x runtime — next to foundry_local.dll (the cmake post-build +# copy in sdk_v2/cpp/CMakeLists.txt). We forward any entry from OPTIONAL_SIBLINGS +# that is present in the upstream artifact dir; absent files are silently skipped, +# so the same packing path serves every platform. RIDS: dict[str, tuple[str, str]] = { "win_x64": ("win-x64", "foundry_local.dll"), "win_arm64": ("win-arm64", "foundry_local.dll"), @@ -57,9 +56,8 @@ } # Sibling files copied into runtimes//native/ when present in the upstream -# artifact. WinML builds drop Microsoft.Windows.AI.MachineLearning.dll alongside -# foundry_local.dll; standard builds don't, and that's the only signal we need -# to differentiate. +# artifact. Windows builds drop Microsoft.Windows.AI.MachineLearning.dll alongside +# foundry_local.dll; other platforms don't, so presence alone drives inclusion. OPTIONAL_SIBLINGS: tuple[str, ...] = ( "Microsoft.Windows.AI.MachineLearning.dll", ) @@ -78,9 +76,9 @@ def _parse_args() -> argparse.Namespace: parser.add_argument("--genai_version", required=True, help="Minimum Microsoft.ML.OnnxRuntimeGenAI.Foundry version.") parser.add_argument("--package_id", default="Microsoft.AI.Foundry.Local.Runtime", - help="NuGet package id. Use Microsoft.AI.Foundry.Local.Runtime.WinML " - "for the WinML variant (Windows-only RIDs, ships the WinML 2.x " - "reg-free runtime alongside foundry_local).") + help="NuGet package id for the native runtime. On Windows the package " + "includes the reg-free WinML 2.x runtime " + "(Microsoft.Windows.AI.MachineLearning.dll) alongside foundry_local.") for arg_name, (rid, lib) in RIDS.items(): parser.add_argument(f"--{arg_name}", type=Path, default=None, @@ -154,7 +152,7 @@ def stage(args: argparse.Namespace, staging: Path) -> int: # The upstream artifact for each RID is the primary library plus, on # Windows, .pdb / .lib companions used by the Python wheel build, and - # — for the WinML variant — Microsoft.Windows.AI.MachineLearning.dll. + # Microsoft.Windows.AI.MachineLearning.dll (the WinML 2.x runtime). # We forward the primary library plus any present OPTIONAL_SIBLINGS; # everything else (.pdb, .lib) stays out of the NuGet runtime payload. shutil.copy2(lib_path, native_dir) diff --git a/sdk_v2/cpp/test/CMakeLists.txt b/sdk_v2/cpp/test/CMakeLists.txt index 08e23cafc..cbe2974f8 100644 --- a/sdk_v2/cpp/test/CMakeLists.txt +++ b/sdk_v2/cpp/test/CMakeLists.txt @@ -105,7 +105,7 @@ else() endif() include(GoogleTest) -# DISCOVERY_TIMEOUT defaults to 5s, which is not enough for the WinML build: +# DISCOVERY_TIMEOUT defaults to 5s, which is not enough for the Windows build: # foundry_local.dll links against Microsoft.Windows.AI.MachineLearning.dll # (delay-loaded) plus standalone ORT, and process startup + gtest enumeration # can exceed 5s on cold caches or slow CI agents. Bump to 60s for headroom. diff --git a/sdk_v2/cs/README.md b/sdk_v2/cs/README.md index 2d3d5adca..253c6a8a0 100644 --- a/sdk_v2/cs/README.md +++ b/sdk_v2/cs/README.md @@ -11,7 +11,7 @@ The Foundry Local C# SDK provides a .NET interface for running AI models locally - **Download progress** — wire up an `Action` callback for real-time download percentage - **Model variants** — select specific hardware/quantization variants per model alias - **Optional web service** — start an OpenAI-compatible REST endpoint (`/v1/chat_completions`, `/v1/models`) -- **WinML acceleration** — opt-in Windows hardware acceleration with automatic EP download +- **WinML acceleration** — built-in Windows hardware acceleration with automatic EP download - **Full async/await** — every operation supports `CancellationToken` and async patterns - **IDisposable** — deterministic cleanup of native resources @@ -32,19 +32,9 @@ Or open [Microsoft.AI.Foundry.Local.SDK.sln](./Microsoft.AI.Foundry.Local.SDK.sl ## WinML: Automatic Hardware Acceleration (Windows) -On Windows, Foundry Local can leverage WinML for GPU/NPU hardware acceleration via ONNX Runtime execution providers (EPs). EPs are large binaries downloaded on first use and cached for subsequent runs. +On Windows, Foundry Local leverages WinML for GPU/NPU hardware acceleration via ONNX Runtime execution providers (EPs). EPs are large binaries downloaded on first use and cached for subsequent runs. -Install the WinML package variant instead: - -```bash -dotnet add package Microsoft.AI.Foundry.Local.WinML -``` - -Or build from source with: - -```bash -dotnet build src/Microsoft.AI.Foundry.Local.csproj /p:UseWinML=true -``` +WinML acceleration is built into the `Microsoft.AI.Foundry.Local` package — it bundles the reg-free WinML 2.x runtime on Windows automatically, with no separate package or build flag required. ### Triggering EP download diff --git a/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj b/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj index cd4c1c6de..d61b6b7eb 100644 --- a/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj +++ b/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj @@ -29,13 +29,6 @@ true snupkg true - - - false - win-x64;win-arm64 @@ -81,24 +73,6 @@ - - - - Microsoft AI Foundry Local for WinML - Microsoft Foundry Local SDK for WinML - Microsoft.AI.Foundry.Local.WinML - Microsoft.AI.Foundry.Local.WinML - - net8.0;net9.0 - win-x64;win-arm64 - @@ -132,12 +106,9 @@ Option 2 (local build): set FoundryLocalNativeBinDir to point at a C++ build output, e.g.: dotnet build /p:FoundryLocalNativeBinDir=..\..\cpp\build\Windows\RelWithDebInfo\bin\RelWithDebInfo Option 3 (default): auto-detect the C++ build output from the repo-relative location. --> - + - - - diff --git a/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj b/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj index 9ab9aa853..be39e7c24 100644 --- a/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj +++ b/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj @@ -1,7 +1,6 @@  - false true net8.0 - net462;net8.0 + net462;net8.0 enable enable diff --git a/sdk_v2/js/script/copy-native.mjs b/sdk_v2/js/script/copy-native.mjs index 4cdb44e86..79a291902 100644 --- a/sdk_v2/js/script/copy-native.mjs +++ b/sdk_v2/js/script/copy-native.mjs @@ -58,28 +58,25 @@ const wanted = (() => { ]; } if (process.platform === "darwin") { - // The ORT dylib is referenced under two fixed names: foundry_local loads it by - // its soversion install_name (libonnxruntime.1.dylib), while GenAI's static - // initializer dlopen()s the unversioned libonnxruntime.dylib. Both must sit - // beside the addon. Shipped packages symlink one name to the other to avoid - // duplicating the ~24MB binary; this dev-only staging just copies both, since - // the duplicate never leaves the machine. + // Copy only the versioned ORT soname (libonnxruntime.1.dylib) — the name + // libfoundry_local records. GenAI is pointed at it via ORT_LIB_PATH (set by the + // addon loader in detail/native.ts), so the unversioned alias isn't needed. The + // C++ build stages the versioned name as a symlink; copyFileSync dereferences it. return [ "libfoundry_local.dylib", - "libonnxruntime.dylib", "libonnxruntime.1.dylib", "libonnxruntime-genai.dylib", ]; } - return ["libfoundry_local.so", "libonnxruntime.so", "libonnxruntime.so.1", "libonnxruntime-genai.so"]; + return ["libfoundry_local.so", "libonnxruntime.so.1", "libonnxruntime-genai.so"]; })(); let copied = 0; const available = new Set(readdirSync(sourceDir)); for (const file of wanted) { if (!available.has(file)) { - // Skip optional deps that aren't present in this build flavor (e.g. - // WinML bits in a non-WinML build). + // Skip optional deps not produced on this platform (e.g. the Windows ML + // runtime only exists in Windows builds). continue; } const src = resolve(sourceDir, file); diff --git a/sdk_v2/js/script/install-native.cjs b/sdk_v2/js/script/install-native.cjs index fb18a16ae..3e654adcd 100644 --- a/sdk_v2/js/script/install-native.cjs +++ b/sdk_v2/js/script/install-native.cjs @@ -225,41 +225,32 @@ async function installPackage(artifact, tempDir, binDir) { ); } -// Mirror the platform-specific post-build steps that sdk_v2/cpp/CMakeLists.txt -// runs after building libfoundry_local. The Foundry ORT nupkg ships only the -// unversioned `libonnxruntime.{so,dylib}`, but our libfoundry_local records -// versioned SONAME/install_name dependencies, so we have to create the -// matching alias next to it. -function applyOrtPlatformAliases(binDir, ortVersion) { +// The Foundry ORT nupkg extracts the unversioned `libonnxruntime.{so,dylib}`, but +// libfoundry_local records a versioned SONAME/install_name dependency +// (libonnxruntime.so.1 / libonnxruntime.1.dylib). Rename the extracted file to that +// versioned soname so foundry_local resolves it via rpath. GenAI is pointed at the +// same file by the ORT_LIB_PATH env var the addon loader sets (onnxruntime-genai +// honors it before its unversioned-name fallback), so no unversioned alias is +// shipped. Windows uses onnxruntime.dll, which has no soname. +function normalizeOrtLibName(binDir, ortVersion) { + let unversioned; + let versioned; if (os.platform() === 'linux') { - const unv = path.join(binDir, 'libonnxruntime.so'); - const soname = path.join(binDir, 'libonnxruntime.so.1'); - if (fs.existsSync(unv) && !fs.existsSync(soname)) { - try { - fs.symlinkSync('libonnxruntime.so', soname); - console.log(` Created symlink libonnxruntime.so.1 -> libonnxruntime.so`); - } catch (err) { - fs.copyFileSync(unv, soname); - console.log(` Copied libonnxruntime.so -> libonnxruntime.so.1 (symlink failed: ${err.message})`); - } - } + unversioned = path.join(binDir, 'libonnxruntime.so'); + versioned = path.join(binDir, 'libonnxruntime.so.1'); } else if (os.platform() === 'darwin') { const major = ortVersion.split('.')[0]; - const unv = path.join(binDir, 'libonnxruntime.dylib'); - const versioned = path.join(binDir, `libonnxruntime.${major}.dylib`); - if (fs.existsSync(unv) && !fs.existsSync(versioned)) { - // libfoundry_local.dylib references @rpath/libonnxruntime..dylib - // (the install_name baked into the nupkg's dylib). Provide that file. - fs.renameSync(unv, versioned); - console.log(` Renamed libonnxruntime.dylib -> libonnxruntime.${major}.dylib`); - // GenAI dlopen()s "libonnxruntime.dylib" at runtime and -lonnxruntime resolves it at link time. - try { - fs.symlinkSync(`libonnxruntime.${major}.dylib`, unv); - console.log(` Created symlink libonnxruntime.dylib -> libonnxruntime.${major}.dylib`); - } catch (err) { - console.warn(` Could not create libonnxruntime.dylib symlink: ${err.message}`); - } - } + unversioned = path.join(binDir, 'libonnxruntime.dylib'); + versioned = path.join(binDir, `libonnxruntime.${major}.dylib`); + } else { + return; + } + if (fs.existsSync(versioned)) { + return; + } + if (fs.existsSync(unversioned)) { + fs.renameSync(unversioned, versioned); + console.log(` Renamed ${path.basename(unversioned)} -> ${path.basename(versioned)}`); } } @@ -272,7 +263,7 @@ function applyOrtPlatformAliases(binDir, ortVersion) { for (const artifact of ARTIFACTS) { await installPackage(artifact, tempDir, BIN_DIR); } - applyOrtPlatformAliases(BIN_DIR, ortVersion); + normalizeOrtLibName(BIN_DIR, ortVersion); console.log('[foundry-local] Native runtime install complete.'); } catch (err) { console.error('[foundry-local] Installation failed:', err instanceof Error ? err.message : err); diff --git a/sdk_v2/js/script/pack-prebuilds.mjs b/sdk_v2/js/script/pack-prebuilds.mjs index b8129effd..36e1d0913 100644 --- a/sdk_v2/js/script/pack-prebuilds.mjs +++ b/sdk_v2/js/script/pack-prebuilds.mjs @@ -1,6 +1,7 @@ -// CI-only. Stages the foundry_local shared library into prebuilds/ for npm -// publish. ORT / ORT-GenAI / WinML are NOT bundled — the -// install-native.cjs postinstall hook fetches them from NuGet at the user's +// CI-only. Stages the foundry_local shared library — and, on Windows, the +// reg-free WinML 2.x runtime DLL (Microsoft.Windows.AI.MachineLearning.dll) — +// into prebuilds/ for npm publish. ORT / ORT-GenAI are NOT bundled; the +// install-native.cjs postinstall hook fetches them from NuGet on the user's // machine (see ort-loading-contract.instructions.md). The .node addon itself // is already produced into prebuilds/-/ by `node-gyp rebuild`. // @@ -41,13 +42,20 @@ if (!existsSync(sourceDir)) { const destDir = resolve(pkgRoot, "prebuilds", `${process.platform}-${process.arch}`); mkdirSync(destDir, { recursive: true }); -// Only foundry_local — ORT siblings are explicitly excluded. +// foundry_local is required; ORT/GenAI siblings are excluded (fetched at install +// time). The WinML EP catalog DLL is an optional sibling bundled on Windows. const wanted = (() => { if (process.platform === "win32") return ["foundry_local.dll"]; if (process.platform === "darwin") return ["libfoundry_local.dylib"]; return ["libfoundry_local.so"]; })(); +// Optional native siblings — copied when present, skipped (with a warning) when +// not. The reg-free WinML 2.x runtime ships next to foundry_local.dll on Windows +// so WinML hardware EPs work out of the box without an install-time download. +const optional = + process.platform === "win32" ? ["Microsoft.Windows.AI.MachineLearning.dll"] : []; + let copied = 0; const available = new Set(readdirSync(sourceDir)); for (const file of wanted) { @@ -63,6 +71,19 @@ for (const file of wanted) { copied += 1; } +for (const file of optional) { + if (!available.has(file)) { + console.warn(`[pack-prebuilds] optional file not found, skipping: ${file}`); + continue; + } + const src = resolve(sourceDir, file); + const dst = resolve(destDir, file); + copyFileSync(src, dst); + const size = statSync(dst).size; + console.log(`[pack-prebuilds] ${file} (${size} bytes)`); + copied += 1; +} + console.log(`[pack-prebuilds] Copied ${copied} file(s) to ${destDir}`); // Also copy deps_versions.json to the package root so install-native.cjs can diff --git a/sdk_v2/js/src/detail/native.ts b/sdk_v2/js/src/detail/native.ts index 7e607d7d9..0f5c2c758 100644 --- a/sdk_v2/js/src/detail/native.ts +++ b/sdk_v2/js/src/detail/native.ts @@ -258,6 +258,8 @@ function loadAddon(): NativeAddon { `Native addon not found at ${addonPath}.\nBuild it locally with:\n npm run copy-native:dev && npm run build:native\n(requires the C++ SDK to be built first via\n \`python sdk_v2/cpp/build.py --configure --build --config RelWithDebInfo\`).\nAlternatively, pass \`libraryPath\` in the FoundryLocalConfig (or call \`configureNativeLoader\`) to point at a directory containing the native library.`, ); } + // Point GenAI at the ORT bundled next to the addon before the addon loads. + applyOrtLibPath(prebuildDir); const require = createRequire(import.meta.url); return require(addonPath) as NativeAddon; } @@ -284,20 +286,44 @@ function nativeLibBasename(): string { /** * Candidate basenames for an ORT-family library on the current platform. The loader tries them in order and - * uses the first that exists on disk. macOS stages the real ORT library under its soversion - * (`libonnxruntime.1.dylib`, ORT's install_name). Linux ships `libonnxruntime.so` with a `.so.1` soname - * fallback for packaging variants that omit the unversioned symlink. GenAI has no versioned variant. + * uses the first that exists on disk. We ship the versioned soname (`libonnxruntime.so.1` / + * `libonnxruntime.1.dylib`) — the name libfoundry_local records — and point GenAI at it via ORT_LIB_PATH + * (see applyOrtLibPath). The unversioned name is kept only as a tolerant fallback for layouts that still + * stage it (e.g. the raw C++ build output). GenAI has no versioned variant of its own. */ function ortCandidateBasenames(name: "onnxruntime" | "onnxruntime-genai"): string[] { if (process.platform === "win32") return [`${name}.dll`]; if (process.platform === "darwin") { - if (name === "onnxruntime") return ["libonnxruntime.1.dylib"]; + if (name === "onnxruntime") return ["libonnxruntime.1.dylib", "libonnxruntime.dylib"]; return [`lib${name}.dylib`]; } - if (name === "onnxruntime") return ["libonnxruntime.so", "libonnxruntime.so.1"]; + if (name === "onnxruntime") return ["libonnxruntime.so.1", "libonnxruntime.so"]; return [`lib${name}.so`]; } +/** + * Point onnxruntime-genai at the exact ORT we ship, via the `ORT_LIB_PATH` env var. + * + * On macOS/Linux the GenAI library (built with dlopen support) resolves ORT in its `InitApi()` by first + * dlopen-ing `$ORT_LIB_PATH`, then falling back to the unversioned `libonnxruntime.{dylib,so}` leafname. We + * ship only the versioned soname (the name libfoundry_local records), so setting `ORT_LIB_PATH` to it is what + * lets the package omit the unversioned alias entirely. Windows links ORT directly (no dlopen), so this is a + * no-op there. + * + * Must run before the addon loads (GenAI's `InitApi` is lazy, fired by the first model load). Never clobbers a + * caller-provided `ORT_LIB_PATH`. + */ +function applyOrtLibPath(directory: string): void { + if (process.platform === "win32" || process.env.ORT_LIB_PATH) return; + for (const basename of ortCandidateBasenames("onnxruntime")) { + const fullPath = resolve(directory, basename); + if (existsSync(fullPath)) { + process.env.ORT_LIB_PATH = fullPath; + return; + } + } +} + /** * Pre-load ORT and ORT-GenAI from `directory` by absolute path so that when foundry_local is loaded, the OS * loader resolves its NEEDED entries against the already-resident modules instead of doing a filesystem search @@ -377,6 +403,9 @@ export function configureNativeLoader(opts: { libraryPath?: string }): void { // (.github/instructions/ort-loading-contract.instructions.md) — every binding must do this. preloadOrtIfPresent(libraryPath); + // Point GenAI at the ORT in this directory (takes precedence over the default prebuilds lookup). + applyOrtLibPath(libraryPath); + try { getPreloadAddon().preloadLibrary(fullPath); } catch (err) { diff --git a/sdk_v2/js/src/foundryLocalManager.ts b/sdk_v2/js/src/foundryLocalManager.ts index e3499462c..99e350a79 100644 --- a/sdk_v2/js/src/foundryLocalManager.ts +++ b/sdk_v2/js/src/foundryLocalManager.ts @@ -13,6 +13,41 @@ import { } from "./detail/native.js"; import type { EpDownloadResult, EpInfo } from "./types.js"; +// Once a native Manager exists, ONNX Runtime's process-wide teardown races with +// Node's environment teardown on a natural exit and crashes — a long-standing +// ORT-on-exit issue, independent of this SDK. It is avoided only by releasing +// the Manager and then leaving via `process.exit()`, which skips that graceful +// teardown. So track live managers and do exactly that on the way out: dispose +// on `beforeExit` then exit explicitly; an `exit` handler covers callers who +// invoke `process.exit()` themselves (releasing the env before the C runtime +// tears the ORT libraries down keeps their static destructors benign). +const liveManagers = new Set(); +let exitHandlersInstalled = false; + +function disposeLiveManagers(): void { + for (const manager of [...liveManagers]) { + try { + manager.dispose(); + } catch { + // Best-effort: a dispose failure must not block process exit. + } + } +} + +function installExitHandlersOnce(): void { + if (exitHandlersInstalled) { + return; + } + exitHandlersInstalled = true; + process.on("beforeExit", () => { + disposeLiveManagers(); + process.exit(process.exitCode ?? 0); + }); + process.on("exit", () => { + disposeLiveManagers(); + }); +} + export class FoundryLocalManager { readonly #native: NativeManager; #catalog: Catalog | undefined; @@ -58,6 +93,8 @@ export class FoundryLocalManager { } } this.#native = new (getAddon().Manager)(config); + liveManagers.add(this); + installExitHandlersOnce(); } /** @@ -179,6 +216,7 @@ export class FoundryLocalManager { * (and any method on a `Catalog` or `Model` obtained through this manager) throws a `FoundryLocalError`. */ dispose(): void { + liveManagers.delete(this); this.#native.dispose(); this.#catalog = undefined; this.#urls = []; diff --git a/sdk_v2/python/README.md b/sdk_v2/python/README.md index 3d7b1bbc3..262a56c02 100644 --- a/sdk_v2/python/README.md +++ b/sdk_v2/python/README.md @@ -15,22 +15,11 @@ The Foundry Local Python SDK is a native Python binding for the Foundry Local C+ ## Installation -Two package variants are published — choose the one that matches your target hardware: - -| Variant | Package | Native backends | -|---|---|---| -| Standard (cross-platform) | `foundry-local-sdk` | CPU / WebGPU / CUDA | -| WinML (Windows only) | `foundry-local-sdk-winml` | Windows ML + all standard backends | - ```bash -# Standard (cross-platform — Linux, macOS, Windows) pip install foundry-local-sdk - -# WinML (Windows only) -pip install foundry-local-sdk-winml ``` -The wheel ships the Foundry Local native library and pulls the matching ONNX Runtime + ONNX Runtime GenAI runtime packages as dependencies. The two variants are mutually exclusive — install only one per environment. +The wheel ships the Foundry Local native library — bundling the reg-free WinML 2.x runtime on Windows for hardware acceleration — and pulls the matching ONNX Runtime + ONNX Runtime GenAI runtime packages as dependencies. ### Building from source @@ -56,12 +45,7 @@ Then build the wheel: ```bash cd sdk_v2/python - -# Standard wheel python -m build --wheel - -# WinML wheel (uses the build_backend.py shim) -python -m build --wheel -C winml=true ``` For editable installs during development: @@ -72,15 +56,11 @@ pip install -e . ### Installing native runtime dependencies for development / CI -`foundry-local-install` is a convenience wrapper for end-user / CI environments that want the published wheel plus its ORT / ONNX Runtime GenAI runtime packages installed and verified in one step. It runs `pip install --upgrade foundry-local-sdk[-winml]` from PyPI and then probes that `onnxruntime[_core]` and `onnxruntime_genai[_core]` import cleanly. +`foundry-local-install` is a convenience wrapper for end-user / CI environments that want the published wheel plus its ORT / ONNX Runtime GenAI runtime packages installed and verified in one step. It runs `pip install --upgrade foundry-local-sdk` from PyPI and then probes that `onnxruntime[_core]` and `onnxruntime_genai[_core]` import cleanly. ```bash -# Standard foundry-local-install -# WinML (Windows only) -foundry-local-install --winml - # Add --verbose to print resolved binary paths after installation. ``` @@ -425,7 +405,7 @@ Enums: `ItemType`, `TextItemType`, `MessageRole`, `TensorDataType`. | Function | CLI name | Description | |---|---|---| -| `foundry_local_sdk._native.installer.main` | `foundry-local-install` | Install and verify native binaries (`--winml` for the WinML variant, `--verbose` to print resolved paths) | +| `foundry_local_sdk._native.installer.main` | `foundry-local-install` | Install and verify native binaries (`--verbose` to print resolved paths) | ## Running tests diff --git a/sdk_v2/python/_build_backend/__init__.py b/sdk_v2/python/_build_backend/__init__.py index a7a90014b..b6de7e945 100644 --- a/sdk_v2/python/_build_backend/__init__.py +++ b/sdk_v2/python/_build_backend/__init__.py @@ -1,27 +1,20 @@ # PEP 517 backend shim for foundry-local-sdk. # -# Delegates every hook to ``setuptools.build_meta``. The only added -# behavior is a temporary patch of ``pyproject.toml`` when the -# ``FL_PYTHON_PACKAGE_NAME`` environment variable is set, so the same -# source tree can produce two differently-named wheels: -# -# * unset -> foundry-local-sdk (default) -# * foundry-local-sdk-winml -> foundry-local-sdk-winml (WinML SKU) +# Delegates every hook to ``setuptools.build_meta``. The only added behavior is a +# temporary patch of ``pyproject.toml`` during the build that rewrites the +# ORT/GenAI version pins from ``deps_versions.json`` (the single source of truth), +# so the dependency versions never drift from the native build. # # This is wired into pyproject.toml via:: # # [build-system] # build-backend = "_build_backend" # backend-path = ["."] -# -# Local dev workflow is unchanged: without the env var, ``python -m build`` -# produces ``foundry-local-sdk`` exactly as before. from __future__ import annotations import contextlib import json -import os import re from collections.abc import Generator from pathlib import Path @@ -49,76 +42,50 @@ _HAS_EDITABLE = False -_ENV_VAR = "FL_PYTHON_PACKAGE_NAME" _PYPROJECT = Path(__file__).resolve().parent.parent / "pyproject.toml" -_SDK_V2_ROOT = _PYPROJECT.resolve().parent.parent -_DEPS_JSON_STD = _SDK_V2_ROOT / "deps_versions.json" - -# Match ``name = "..."`` only inside the [project] table. The regex is -# anchored to the first ``name = "foundry-local-sdk"`` occurrence which -# is the project name (the build backend itself is referenced by module -# name, not by a quoted package name, so there's no ambiguity). -_NAME_PATTERN = re.compile(r'(?m)^name\s*=\s*"foundry-local-sdk"') - -# Patterns for rewriting ORT/GenAI version pins in the dependencies list. -# Each captures the package name + ``==`` and we substitute in the version -# read from the appropriate deps_versions JSON. +_DEPS_JSON = _PYPROJECT.parent.parent / "deps_versions.json" + +# Patterns for rewriting ORT/GenAI version pins in the dependencies list. Each +# captures the package name + ``==`` and we substitute in the version read from +# deps_versions.json. _ORT_PIN_PATTERN = re.compile(r'("onnxruntime(?:-core|-gpu)==)[^\s";]+') _GENAI_PIN_PATTERN = re.compile(r'("onnxruntime-genai(?:-core|-cuda)==)[^\s";]+') -def _read_versions(deps_file: Path) -> tuple[str, str]: - if not deps_file.is_file(): - raise RuntimeError(f"Required versions file not found: {deps_file}") - data = json.loads(deps_file.read_text(encoding="utf-8")) +def _read_versions() -> tuple[str, str]: + if not _DEPS_JSON.is_file(): + raise RuntimeError(f"Required versions file not found: {_DEPS_JSON}") + data = json.loads(_DEPS_JSON.read_text(encoding="utf-8")) try: ort = data["onnxruntime"]["version"] genai = data["onnxruntime-genai"]["version"] except (KeyError, TypeError) as exc: raise RuntimeError( - f"{deps_file} is missing required keys 'onnxruntime.version' / 'onnxruntime-genai.version'" + f"{_DEPS_JSON} is missing required keys 'onnxruntime.version' / 'onnxruntime-genai.version'" ) from exc return str(ort), str(genai) -def _patch_pyproject_text(original: str, *, override_name: str | None, deps_file: Path) -> str: - """Return *original* with the project name and ORT/GenAI version pins rewritten. - - * ``override_name`` — when set, replaces ``name = "foundry-local-sdk"``. - * ``deps_file`` — JSON file that supplies the ORT and GenAI version pins. - """ - patched = original - if override_name and override_name != "foundry-local-sdk": - patched, n = _NAME_PATTERN.subn(f'name = "{override_name}"', patched, count=1) - if n != 1: - raise RuntimeError( - f"Could not find canonical project name line in {_PYPROJECT} to patch " - f"for {_ENV_VAR}={override_name!r}." - ) - - ort_ver, genai_ver = _read_versions(deps_file) - patched = _ORT_PIN_PATTERN.sub(lambda m: f"{m.group(1)}{ort_ver}", patched) +def _patch_pyproject_text(original: str) -> str: + """Return *original* with the ORT/GenAI version pins rewritten from deps_versions.json.""" + ort_ver, genai_ver = _read_versions() + patched = _ORT_PIN_PATTERN.sub(lambda m: f"{m.group(1)}{ort_ver}", original) patched = _GENAI_PIN_PATTERN.sub(lambda m: f"{m.group(1)}{genai_ver}", patched) return patched @contextlib.contextmanager -def _maybe_patch_name() -> Generator[None, None, None]: - """Context manager that rewrites pyproject.toml during PEP 517 hook execution. - - Always rewrites ORT/GenAI version pins from deps_versions.json (single - source of truth). Conditionally rewrites the project name when - ``FL_PYTHON_PACKAGE_NAME`` selects the WinML variant. The WinML and - standard flavors share the same ORT/GenAI versions; only the - distribution name differs. - """ - override = os.environ.get(_ENV_VAR, "").strip() or None +def _rewrite_version_pins() -> Generator[None, None, None]: + """Rewrite pyproject.toml ORT/GenAI pins from deps_versions.json during build. + deps_versions.json is the single source of truth for the native dependency + versions; rewriting here keeps the wheel's declared pins in lockstep. + """ original = _PYPROJECT.read_text(encoding="utf-8") - patched = _patch_pyproject_text(original, override_name=override, deps_file=_DEPS_JSON_STD) + patched = _patch_pyproject_text(original) if patched == original: - # Nothing to rewrite (e.g. JSON already matches and no name override). + # Pins already match deps_versions.json — nothing to rewrite. yield return @@ -130,40 +97,40 @@ def _maybe_patch_name() -> Generator[None, None, None]: def get_requires_for_build_wheel(config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_get_requires_for_build_wheel(config_settings) def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_prepare_metadata_for_build_wheel(metadata_directory, config_settings) def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_build_wheel(wheel_directory, config_settings, metadata_directory) def get_requires_for_build_sdist(config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_get_requires_for_build_sdist(config_settings) def build_sdist(sdist_directory, config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_build_sdist(sdist_directory, config_settings) if _HAS_EDITABLE: def get_requires_for_build_editable(config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_get_requires_for_build_editable(config_settings) def prepare_metadata_for_build_editable(metadata_directory, config_settings=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_prepare_metadata_for_build_editable(metadata_directory, config_settings) def build_editable(wheel_directory, config_settings=None, metadata_directory=None): - with _maybe_patch_name(): + with _rewrite_version_pins(): return _orig_build_editable(wheel_directory, config_settings, metadata_directory) diff --git a/sdk_v2/python/pyproject.toml b/sdk_v2/python/pyproject.toml index 7b6e7478b..0c5bda0bd 100644 --- a/sdk_v2/python/pyproject.toml +++ b/sdk_v2/python/pyproject.toml @@ -1,8 +1,7 @@ [build-system] requires = ["setuptools>=68", "cffi>=1.16", "wheel"] -# Custom backend wraps setuptools.build_meta to honour the -# FL_PYTHON_PACKAGE_NAME env var (used by CI to produce the -# `foundry-local-sdk-winml` variant from the same source tree). +# Custom backend wraps setuptools.build_meta to rewrite the ORT/GenAI +# version pins from sdk_v2/deps_versions.json at wheel-build time. build-backend = "_build_backend" backend-path = ["."] @@ -40,8 +39,7 @@ classifiers = [ # ORT/GenAI version pins below use sentinel ``0.0.0``. The real versions # come from sdk_v2/deps_versions.json — the _build_backend rewrites these # pins from JSON at wheel-build time. JSON is the single source of truth; -# bumping ORT versions is a one-file edit. The WinML wheel variant shares -# the same ORT/GenAI versions and only differs in the distribution name. +# bumping ORT versions is a one-file edit. # # If a wheel ever ships with ``==0.0.0`` it means the backend wasn't # invoked (e.g. raw setuptools bypass) — pip install will fail loudly diff --git a/sdk_v2/python/src/foundry_local_sdk/_native/installer.py b/sdk_v2/python/src/foundry_local_sdk/_native/installer.py index f1d3094f3..51a2541ec 100644 --- a/sdk_v2/python/src/foundry_local_sdk/_native/installer.py +++ b/sdk_v2/python/src/foundry_local_sdk/_native/installer.py @@ -2,13 +2,12 @@ Usage:: - foundry-local-install [--winml] [--verbose] + foundry-local-install [--verbose] -Re-installs the SDK wheel (``foundry-local-sdk`` or ``foundry-local-sdk-winml``) -via pip — pip then resolves the ORT and GenAI runtime packages declared as -dependencies in ``pyproject.toml``. After install we materialise the platform's -DLL search path / symlink workarounds and verify ``onnxruntime`` and -``onnxruntime_genai`` import cleanly. +Re-installs the SDK wheel (``foundry-local-sdk``) via pip — pip then resolves the +ORT and GenAI runtime packages declared as dependencies in ``pyproject.toml``. +After install we materialise the platform's DLL search path / symlink workarounds +and verify ``onnxruntime`` and ``onnxruntime_genai`` import cleanly. This is the v2 equivalent of the legacy ``foundry-local-install`` command. v2 ships a single SDK wheel (rather than the legacy split between ``-sdk`` and @@ -57,14 +56,9 @@ def main(argv: list[str] | None = None) -> int: prog="foundry-local-install", description=( "(Re)install the Foundry Local SDK wheel and verify its ORT/GenAI " - "native dependencies are reachable. Use --winml for the WinML variant." + "native dependencies are reachable." ), ) - parser.add_argument( - "--winml", - action="store_true", - help="Install the WinML variant (foundry-local-sdk-winml) instead of the standard wheel.", - ) parser.add_argument( "--verbose", action="store_true", @@ -72,7 +66,7 @@ def main(argv: list[str] | None = None) -> int: ) args = parser.parse_args(argv) - package = "foundry-local-sdk-winml" if args.winml else "foundry-local-sdk" + package = "foundry-local-sdk" print(f"[foundry-local] Installing {package} (will pull ORT/GenAI runtime deps)...") try: subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", package]) diff --git a/www/src/lib/components/download-dropdown.svelte b/www/src/lib/components/download-dropdown.svelte index 811ba8ec9..ce7a37ef9 100644 --- a/www/src/lib/components/download-dropdown.svelte +++ b/www/src/lib/components/download-dropdown.svelte @@ -52,8 +52,6 @@ icon: string; crossPlatformId: string; crossPlatformCommand: string; - winmlId: string; - winmlCommand: string; }; const cliInstallOptions: CliInstallOption[] = [ @@ -83,36 +81,28 @@ label: 'Python SDK', icon: PythonIcon, crossPlatformId: 'python-cross-platform', - crossPlatformCommand: 'pip install foundry-local-sdk', - winmlId: 'python-winml', - winmlCommand: 'pip install foundry-local-sdk-winml' + crossPlatformCommand: 'pip install foundry-local-sdk' }, { id: 'javascript', label: 'JavaScript SDK', icon: JavaScriptIcon, crossPlatformId: 'javascript-cross-platform', - crossPlatformCommand: 'npm install foundry-local-sdk', - winmlId: 'javascript-winml', - winmlCommand: 'npm install foundry-local-sdk-winml' + crossPlatformCommand: 'npm install foundry-local-sdk' }, { id: 'csharp', label: 'C# SDK', icon: CSharpIcon, crossPlatformId: 'csharp-cross-platform', - crossPlatformCommand: 'dotnet add package Microsoft.AI.Foundry.Local', - winmlId: 'csharp-winml', - winmlCommand: 'dotnet add package Microsoft.AI.Foundry.Local.WinML' + crossPlatformCommand: 'dotnet add package Microsoft.AI.Foundry.Local' }, { id: 'rust', label: 'Rust SDK', icon: RustIcon, crossPlatformId: 'rust-cross-platform', - crossPlatformCommand: 'cargo add foundry-local-sdk', - winmlId: 'rust-winml', - winmlCommand: 'cargo add foundry-local-sdk --features winml' + crossPlatformCommand: 'cargo add foundry-local-sdk' } ]; @@ -163,17 +153,13 @@ {item.label} -
+
- -
{/each}