[SDKv2] Migrate WinML EP downloads to WinML 2.x (reg-free runtime)#788
[SDKv2] Migrate WinML EP downloads to WinML 2.x (reg-free runtime)#788bmehta001 wants to merge 27 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
44fb8d1 to
ae5ad90
Compare
ae5ad90 to
c240a00
Compare
There was a problem hiding this comment.
Pull request overview
This PR migrates the WinML EP download/discovery path from WinML 1.x (Windows App SDK bootstrap required) to WinML 2.x (Microsoft.Windows.AI.MachineLearning, reg-free runtime), and unifies ORT/GenAI pins across WinML and non-WinML flavors to simplify build + packaging across C++, C#, JS, Python, and CI.
Changes:
- Removes Windows App SDK bootstrap plumbing (
BootstrapadditionalSettings, bootstrap DLL staging, and native bootstrap helper) across all SDK bindings. - Switches WinML EP catalog acquisition to
Microsoft.Windows.AI.MachineLearning(NuGet + first-party CMake config) and updates packaging/staging to ship the reg-free runtime DLL where needed. - Unifies ORT/GenAI version pins (deletes WinML-specific deps JSON and removes WinML branching in CMake and the Python v2 build backend).
Reviewed changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk_v2/python/test/integration/test_configuration_native.py | Updates integration test to stop using the removed Bootstrap additional setting. |
| sdk_v2/python/README.md | Removes WinML-only Bootstrap=false example from docs. |
| sdk_v2/python/pyproject.toml | Updates comments to reflect single deps JSON source for both wheel flavors. |
| sdk_v2/python/_build_backend/init.py | Drops WinML-specific deps JSON selection; always rewrites from deps_versions.json. |
| sdk_v2/js/test/bootstrap-autodetect.test.ts | Deletes tests for bootstrap auto-detect (no longer relevant with WinML 2.x reg-free runtime). |
| sdk_v2/js/src/foundryLocalManager.ts | Removes bootstrap auto-detect logic from manager initialization. |
| sdk_v2/js/script/copy-native.mjs | Stops copying Bootstrap.dll; now copies Microsoft.Windows.AI.MachineLearning.dll. |
| sdk_v2/deps_versions_winml.json | Removes WinML-specific ORT pin file (pins are unified). |
| sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj | Lowers WinML package TFM to Windows 10 19H1-era baseline. |
| sdk_v2/cs/src/FoundryLocalManager.cs | Removes WinML bootstrap default injection into AdditionalSettings. |
| sdk_v2/cpp/test/sdk_api/ep_detection_test.cc | Updates WinML EP test comment to reflect Win10 19H1+ support. |
| sdk_v2/cpp/test/CMakeLists.txt | Increases gtest discovery timeout to accommodate WinML build startup costs. |
| sdk_v2/cpp/src/winml_bootstrap.h | Deletes WinAppSDK bootstrap helper header. |
| sdk_v2/cpp/src/winml_bootstrap.cc | Deletes WinAppSDK bootstrap helper implementation. |
| sdk_v2/cpp/src/manager.cc | Removes bootstrap lifecycle; enables WebGPU EP path for WinML builds now that ORT is unified. |
| sdk_v2/cpp/src/ep_detection/winml_ep_bootstrapper.cc | Removes OS-version gating; improves diagnostics when the WinML runtime DLL is absent. |
| sdk_v2/cpp/src/ep_detection/cuda_ep_bootstrapper.cc | Collapses CUDA EP download to a single ORT-aligned payload (no WinML branching). |
| sdk_v2/cpp/nuget/pack.py | Packages Microsoft.Windows.AI.MachineLearning.dll as the optional WinML sibling DLL. |
| sdk_v2/cpp/docs/MigrationPlan_20260410.md | Updates migration plan documentation for WinML 2.x acquisition and behavior. |
| sdk_v2/cpp/docs/EpDetectionPlan.md | Updates EP detection plan to WinML 2.x reg-free runtime and new package layout. |
| sdk_v2/cpp/docs/CppPortGuide.md | Updates port guide WinML requirements note to Win10 19H1+ reg-free runtime. |
| sdk_v2/cpp/CMakeLists.txt | Gates find_package(WinMLEpCatalog) behind FOUNDRY_LOCAL_USE_WINML; removes bootstrap linking/copy; ensures WinML DLL copy uses catalog DLL dir. |
| sdk_v2/cpp/cmake/FindWinMLEpCatalog.cmake | Switches acquisition to Microsoft.Windows.AI.MachineLearning and uses the package’s first-party CMake config to define targets and DLL dir. |
| sdk_v2/cpp/cmake/FindOnnxRuntimeGenAI.cmake | Removes WinML-vs-standard package branching; uses a single GenAI package. |
| sdk_v2/cpp/cmake/FindOnnxRuntime.cmake | Removes WinML-specific deps file selection; uses unified ORT pin. |
| sdk_v2/cpp/build.py | Updates --use_winml messaging and changes override define to WINML_EP_CATALOG_VERSION. |
| .pipelines/v2/templates/steps-prefetch-nuget.yml | Updates WinML prefetch to download the reg-free package directly (no transitive Foundation resolution). |
| .pipelines/v2/templates/steps-build-windows.yml | Updates native staging to include Microsoft.Windows.AI.MachineLearning.dll for WinML builds. |
| .pipelines/v2/templates/stages-sdk-v2.yml | Removes WinML-specific ORT version parameter wiring. |
| .pipelines/v2/templates/stages-build-native.yml | Unifies WinML and non-WinML builds on the same ORT version parameter. |
| .pipelines/v2/sdk_v2-pipeline-plan.md | Updates pipeline plan documentation for unified ORT pins and WinML 2.x package handling. |
| .pipelines/foundry-local-packaging.yml | Updates packaging pipeline variables to use WinML 2.x version and removes WinML-specific ORT version variable. |
Switch from `Microsoft.WindowsAppSDK.ML` 1.8.2192 (WinML 1.x, WinAppSDK
bootstrap required, Win11 24H2+) to `Microsoft.Windows.AI.MachineLearning`
2.1.6 (WinML 2.x, registration-free, Win10 19H1+). This brings Foundry-Local
in line with the same migration neutron-server completed.
Highlights:
* Remove WinAppSDK Bootstrap plumbing across all SDKs. The `Bootstrap`
`additionalSettings` key — which used to flip on WinAppSDK init in the
native layer — is gone in every binding (C++, C#, JS, Python). It was an
internal init knob; no public surface signaled it as a stable contract.
- C++: delete winml_bootstrap.{h,cc}, the WinAppSdkBootstrap link target,
the Bootstrap.dll post-build copy, and the additional_options key
handling in Manager::Create / Destroy.
- C#: drop the `IS_WINML` default that injected Bootstrap=true.
Lower TFM from net9.0-windows10.0.26100.0 to net9.0-windows10.0.18362.0.
- JS: delete applyBootstrapAutoDetect and its test; drop Bootstrap.dll
from copy-native script.
- Python: drop `Bootstrap=false` examples from README and integration
test.
* Unify ORT to 1.25.1 / OnnxRuntimeGenAI to 0.13.2 for both the WinML and
non-WinML flavors. Delete sdk_v2/deps_versions_winml.json and the
`FOUNDRY_LOCAL_USE_WINML` branch in FindOnnxRuntime.cmake /
FindOnnxRuntimeGenAI.cmake / the python build backend.
* Collapse cuda_ep_bootstrapper.cc to the single ORT-1.25.1 URL / binary
set (previously branched on WinML for the older 1.23.2 build).
* Drop the WebGPU-EP skip on WinML builds (both flavors now satisfy ORT
API >= 24).
* Drop the IsWindows11_24H2OrLater() runtime guard in
winml_ep_bootstrapper.cc; LoadLibraryW is a sufficient probe.
* Gate `find_package(WinMLEpCatalog)` behind `FOUNDRY_LOCAL_USE_WINML`
so non-WinML builds don't silently link the catalog DLL.
* CMake/cmake-modules: update DLL path
`runtimes-framework/<rid>/native/` -> `runtimes/<rid>/native/` and
bump default `WINML_EP_CATALOG_VERSION` to 2.1.6.
* Pipelines: bump `cppWinmlVersion` to 2.1.6, drop `cppOrtVersionWinml`
and the entire `Microsoft.WindowsAppSDK.Foundation` resolution +
Bootstrap.dll staging in steps-prefetch-nuget.yml /
steps-build-windows.yml.
* Nuget pack: replace `Microsoft.WindowsAppRuntime.Bootstrap.dll` with
`Microsoft.Windows.AI.MachineLearning.dll` in OPTIONAL_SIBLINGS.
* Bump gtest `DISCOVERY_TIMEOUT` to 60 for foundry_local_tests and
cache_only_tests; WinML's delay-loaded DLL resolution + static
initializers can exceed the 5 s default during test discovery.
Verified:
* `python sdk_v2/cpp/build.py --config RelWithDebInfo --skip_examples`
(non-WinML) builds clean; 820 unit/cache tests pass.
* `python sdk_v2/cpp/build.py --config RelWithDebInfo --skip_examples
--use_winml` builds clean; FindWinMLEpCatalog.cmake downloads
Microsoft.Windows.AI.MachineLearning 2.1.6 from nuget.org;
Microsoft.Windows.AI.MachineLearning.dll is co-located with
foundry_local.dll in the build output.
* Targeted ctest run on the WinML build:
`ctest -R "EpDetector|WinML|ManagerWebServiceTest|CacheOnlyTest"` ->
18/18 pass.
Out of scope (deferred): WebGPU manifest-based granular updates;
adopting WinML's bundled onnxruntime.dll.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The helper is only referenced from DiscoverProviders' EP-catalog code path. When FOUNDRY_LOCAL_HAS_EP_CATALOG=0 (non-WinML Windows build) the file is still compiled but the helper has no callers, which can trip MSVC C4505 (unreferenced local function has been removed) under /W4 builds. Moving the function definition inside the same preprocessor guard that gates its sole call site makes the compilation symmetric. No behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The find-module advertised WINML_EP_CATALOG_HEADER_DIR as a public output (set as CACHE PATH ... FORCE and printed via message(STATUS)), but no caller in the repo reads it. Headers reach consumers through the WindowsML::Api target's INTERFACE_INCLUDE_DIRECTORIES, propagated by the WinMLEpCatalog::WinMLEpCatalog alias. The companion WINML_EP_CATALOG_DLL_DIR is genuinely consumed by the post-build DLL-copy step in sdk_v2/cpp/CMakeLists.txt, so only the header variable is dead. Drop the cache var, its STATUS line, and the corresponding header-block doc entry. Verified zero remaining references via 'git grep' and a clean configure + build of foundry_local on Windows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The TODO from #758 anticipated needing a 'WinML-aware WebGPU path' when WinML 2.0 eventually adds WebGPU to its OS EP catalog. With WinML 2.x now bootstrapped (this PR), the design is clear: Foundry's WebGPU EP registers under 'Foundry.WebGPU' while WinML catalog EPs use their own names (e.g., 'OpenVINOExecutionProvider'), so the two paths coexist as separate registrations without conflict. No follow-up work is needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirrors the prior commit dropping the WinML/WebGPU coexistence TODO. Foundry's CUDA + WebGPU are pre-registered by SharedTestEnv, so the unregistered candidate is naturally an OS WinML catalog EP — no explicit name filter is needed. Verified: EpDetectionApiTest.DownloadAndRegister_WinMLEp_RegistersFromOsCatalog still picks OpenVINOExecutionProvider on Windows 11 24H2 and passes the full WinML 2.x register path end-to-end (7/7 EpDetectionApiTest tests green). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 3 conditional <TargetFrameworks> blocks with 2: a base 'net8.0;net9.0' and a single Windows-default-only override that adds net462. The previous third block (WinML SKU) redeclared the same 'net8.0;net9.0' the base already produces. Also hoist <UseWinML> and <IsWindows> to the top of the PropertyGroup so the TargetFrameworks condition can reference them directly. Verified: default SKU emits net462+net8.0+net9.0; WinML SKU emits net8.0+net9.0; both build clean (0 warnings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lper - .pipelines/v2: remove WinAppSDK 1.8 runtime install from C# and Python test steps for isWinML=true (WinML 2.x is reg-free, no WinAppSDK dep). - sdk_v2/DEVELOPMENT.md: drop _winml.json from build row (file was deleted earlier in this PR; deps_versions.json is the sole source now). - sdk_v2/js/src/detail/native.ts: delete orphaned getResolvedLibraryDir (sole caller applyBootstrapAutoDetect was removed; not re-exported). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- manager.cc: remove dead <windows.h>/WIN32_LEAN_AND_MEAN block — no Win32 symbols are used in this TU after MddBootstrap and winml_bootstrap.h were removed; <filesystem> comes transitively from ep_detection/ep_types.h. - copy-native.mjs, pack-prebuilds.mjs: drop WindowsAppRuntime from header comments — WindowsAppRuntime is no longer bundled or copied after the WinML 2.x reg-free migration. - EpDetectionPlan.md: fix v1.8.2141 -> 2.1.70 package version and rewrite the OS-version gate step to match actual implementation (build number is diagnostic only; gating is at WinMLEpCatalogCreate). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WinMLEpEnsureReady was synchronous, so the caller's progress callback only ever saw a 0->100% jump after the entire EP download finished. Switch to WinMLEpEnsureReadyAsync with a WinMLAsyncBlock that forwards the fractional progress (0.0-1.0) to the existing percent callback (scaled to 0-99 so 100% remains reserved for post-registration), and wait synchronously via WinMLAsyncGetStatus(..., TRUE). Caller-requested cancellation (progress callback returns false) routes through WinMLAsyncCancel. Also bring EpDetectionPlan.md into agreement with the as-built code: Name()/IsRegistered()/DownloadAndRegister() descriptions now match the actual implementation, and DiscoverProviders documents the EpRegistrationCallback parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- winml_ep_bootstrapper.cc: pass WinML progress through as a percent
directly (it reports 0-100, not 0-1, matching the WinRT projection's
IAsyncOperationWithProgress<_, double> convention used in
neutron-server). Drop the artificial 99% cap.
- winml_ep_bootstrapper: cache library_path_ captured during enumeration
and reuse it in DownloadAndRegister to skip the redundant
WinMLEpGetLibraryPath{Size,} pair when the EP was already Ready.
- winml_ep_bootstrapper.h: document that ep_handle_'s lifetime is owned
by catalog_ref_, so per-EP cleanup is intentionally absent.
- deps_versions.json: add windows-ai-machinelearning entry; expand the
_comment to call out that pipeline literals must match (validation
step below enforces).
- FindWinMLEpCatalog.cmake: read WINML_EP_CATALOG_VERSION from
deps_versions.json via string(JSON), matching FindOnnxRuntime.cmake.
- steps-prefetch-nuget.yml: add a 'Validate pinned versions' guard that
reads deps_versions.json and fails the pipeline if any of
cppOrtVersion / cppGenaiVersion / cppWinmlVersion drifts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- steps-prefetch-nuget.yml: drop -UseBasicParsing on Invoke-WebRequest for the WinML download; PowerShell Core always uses the basic parser, so the switch is a no-op (kept for the standard NuGet loop unchanged since both behaviors match). - _build_backend/__init__.py: remove dead _WINML_PKG_NAME constant; the WinML name override flows entirely through FL_PYTHON_PACKAGE_NAME. - EpDetectionPlan.md: add missing trailing period in Phase 1 validation note. Two other Copilot comments not actioned: the OSVERSIONINFOW concern was already addressed in an earlier commit (current code uses OSVERSIONINFOW from <windows.h>, which is layout-compatible with RTL_OSVERSIONINFOW and avoids needing <winternl.h>), and the "2.1.6 vs 2.1.70" PR-description mismatch isn't present (PR body consistently says 2.1.70). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Phase 3 design note still described the old fractional (0.0–1.0) progress contract that was removed when the artificial 99% cap was dropped. The current implementation in winml_ep_bootstrapper.cc treats WinML's progress value as a percentage (0–100) directly, matching the WinRT IAsyncOperationWithProgress<_, double> projection, and forwards it to the caller's percent callback after clamping — no scale conversion happens. Update the doc to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The EpDetector unions discovery results across CUDA / WebGPU / OpenVINO /
WinML bootstrappers; there is no caller-side fallback when WinML's
DiscoverProviders returns empty (Win10 19H1+ without 24H2, or DLL
missing) — the other bootstrappers were always going to run regardless.
- winml_ep_bootstrapper.h: drop the 'On earlier builds... caller falls back
to its other bootstrappers' sentences from the class doc. The preceding
two lines already state that EP discovery returns providers only on
Win11 24H2+, which is the only consumer-relevant fact.
- winml_ep_bootstrapper.cc: rewrite the LoadLibrary-failure comment to
drop 'older builds fall through to the other bootstrappers' for the
same reason; keep the OS-floor fact + diagnostics rationale.
- docs/EpDetectionPlan.md: rewrite the Risks/Mitigation entry the same
way ('returns empty when the DLL is missing — no WinML bootstrappers
join the discovery set').
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bindings that load foundry_local.dll from a non-default directory (JS prebuilds, Python wheels with FOUNDRY_LOCAL_LIB_DIR off, embedded C++ hosts that drop the runtime alongside their app binary) don't push that directory onto the process search path. A bare-name LoadLibraryW for Microsoft.Windows.AI.MachineLearning.dll then silently fails, the catalog returns zero providers, and DiscoverEps quietly returns only the always-on bootstrappers (CUDA + WebGPU) with no surfaced error. Resolve the sibling path explicitly via GetModuleHandleExW + GetModuleFileNameW + LOAD_WITH_ALTERED_SEARCH_PATH so the WinML DLL is found wherever foundry_local.dll lives. Fall back to the bare-name lookup so a system-installed copy still wins on the default search path when no sibling is present. Verified end-to-end against the dev build on Windows 11 24H2: cpp, cs, python, and js SDKs each return all 4 expected EPs (OpenVINO, NvTensorRTRTX, CUDA, WebGPU). The JS path previously returned only CUDA + WebGPU unless libraryPath was passed in FoundryLocalConfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| // Windows 11 24H2 = build 26100+ | ||
| if (osvi.dwMajorVersion > 10) { | ||
| return true; | ||
| wchar_t module_path[MAX_PATH]; |
There was a problem hiding this comment.
Does this length support potential values when the user has enabled long paths?
| <TargetFrameworks>net8.0;net9.0</TargetFrameworks> | ||
| <TargetFrameworks Condition="'$(IsWindows)' == 'true' And '$(UseWinML)' != 'true'">net462;net8.0;net9.0</TargetFrameworks> |
There was a problem hiding this comment.
Is there benefit to testing .net8 and .net9 or would picking just one be sufficient?
Summary
Migrates WinML EP-download path from
Microsoft.WindowsAppSDK.ML1.8.2192 (WinML 1.x, requires the Windows App SDK bootstrap and Win11 24H2+) toMicrosoft.Windows.AI.MachineLearning2.1.70 (WinML 2.x, registration-free, Win10 19H1+)Highlights
IsWindows11_24H2OrLater()runtime guard inwinml_ep_bootstrapper.cc, sinceLoadLibraryWis a sufficient probe.find_package(WinMLEpCatalog)behindFOUNDRY_LOCAL_USE_WINMLso non-WinML builds don't silently link the catalog DLL.runtimes-framework/<rid>/native/→runtimes/<rid>/native/and bump defaultWINML_EP_CATALOG_VERSIONto 2.1.70.cppWinmlVersionto 2.1.70, dropcppOrtVersionWinmland the entireMicrosoft.WindowsAppSDK.Foundationresolution +Bootstrap.dllstaging insteps-prefetch-nuget.yml/steps-build-windows.yml.Microsoft.WindowsAppRuntime.Bootstrap.dllwithMicrosoft.Windows.AI.MachineLearning.dllinOPTIONAL_SIBLINGS.DISCOVERY_TIMEOUTto 60 s forfoundry_local_testsandcache_only_tests— the WinML build's delay-loaded DLL resolution + static initializers can exceed the 5 s default.net9.0-windows10.0.26100.0lock and targetsnet8.0;net9.0instead. The WinML 2.x OS floor (Win10 19H1+) is enforced at runtime bywinml_ep_bootstrapper.ccand by the EP catalog NuGet, not by the C# csproj, so consumers no longer need the Win11 24H2 SDK to build againstMicrosoft.AI.Foundry.Local.WinML.netstandard2.0is intentionally not added to the WinML SKU — the standard SKU keeps it (and thenet462test lane) for .NET Framework consumers; the WinML SKU's audience is Win11-era + modern .NET, so thenet462test lane is gated off for the WinML test matrix.WinML 2.x EP semantics (informational)
WinMLEpEnsureReadyAsync(WinMLAsyncBlock*), but the OS-side MSIX/BITS work is largely serialized anyway. Left as a future optimization.WinMLEpCatalogHandle) is only created during EnsureReady at the beginning. The 2.x C API has no Refresh/Invalidate, so if new EPs are released mid-process, they will only be visible after process restart.AzureModelCatalogcache is correctly invalidated inManager::DownloadAndRegisterEps(manager.cc:547) after a successful register, so model-filter results reflect the new EP set immediately.Maybe the extra error-checking in @sdk_v2/cpp/src/ep_detection/winml_ep_bootstrapper.cc is unnecessary, but I have added it for diagnostics.