Summary
EPCatalog.is_compatible() decides EP↔hardware compatibility (the "L2" vendor check) using only GPU and NPU vendor strings. CPU is never considered.
OpenVINO EP supports Intel CPU as a valid device, so on a machine with an Intel CPU but no Intel GPU/NPU, the EP is incorrectly reported as [incompatible] and is excluded from EP/device selection.
Repro
Machine: Intel Core i9-10900X CPU + NVIDIA GeForce RTX 4080 (no Intel iGPU, no NPU).
winml sys output:
OpenVINOExecutionProvider [incompatible]
[incompatible] Catalog (catalog default)
Version: 1.3.0+b130ce1
Path: C:\Program Files\WindowsApps\MicrosoftCorporationII.WinML.Intel.OpenVINO.EP.1.8_1.8.69.0_x64__8wekyb3d8bbwe\ExecutionProvider\onnxruntime_providers_openvino_plugin.dll
The DLL is installed and registers successfully (it is not an L1 [failed] row) — it is purely the L2 vendor rule that rejects it.
Detected vendor strings on this machine:
>>> from winml.modelkit.ep_path import _get_detected_vendors
>>> sorted(_get_detected_vendors())
['NVIDIA', 'NVIDIA GeForce RTX 4080']
>>> from winml.modelkit.sysinfo.hardware import CPU
>>> [(c.manufacturer, c.name) for c in CPU.get_all()]
[('GenuineIntel', 'Intel(R) Core(TM) i9-10900X CPU @ 3.70GHz')]
"Intel" is present on the CPU, but the CPU is not scanned, so EP_CATALOG.is_compatible("OpenVINOExecutionProvider") returns False.
Root cause
src/winml/modelkit/ep_path.py — _get_detected_vendors() iterates only GPU and NPU:
strings: set[str] = set()
for cls in (GPU, NPU):
for hw in cls.get_all():
for attr in ("manufacturer", "name"):
...
EPCatalog.Row(name="OpenVINOExecutionProvider", ..., vendor_requirements=frozenset({"Intel"})) therefore never matches on a discrete-GPU-only Intel system.
winml.modelkit.sysinfo.hardware.CPU already exposes manufacturer ("GenuineIntel") and name ("Intel(R) Core(TM) i9-10900X ..."), so the data is available — it is simply not aggregated.
Impact
Beyond the misleading winml sys label, the same predicate gates real EP selection and validation:
src/winml/modelkit/commands/sys.py:893,896 — status shown as incompatible, and the device list is suppressed for the row.
src/winml/modelkit/config/build.py:355 — build config validation rejects the EP.
src/winml/modelkit/session/ep_device.py:496,612,692,764 — EP/device deduction skips the EP, so OpenVINOExecutionProvider with a CPU device cannot be auto-selected (and explicit selection is likely rejected).
Net effect: OpenVINO CPU inference is unreachable on Intel-CPU machines without an Intel GPU/NPU.
Expected behavior
An EP whose vendor requirement is satisfied by any detected device class — CPU included — should be treated as compatible. OpenVINO on an Intel CPU should show as [primary] (or [shadowed]), list its available devices, and be selectable for build/session.
Notes / open questions for design
- Adding
CPU to the _get_detected_vendors() scan is the minimal fix, but it makes the check coarser: matching is a plain substring test over a flat vendor set, so it cannot express "this EP needs an Intel NPU" vs "an Intel CPU is enough". If any future EP needs device-class-specific gating, vendor_requirements should become vendor+device-class pairs rather than bare strings.
- Vendor requirements are currently modeled per-EP, not per-device — worth confirming whether the other gated EPs (QNN/Qualcomm, VitisAI/MIGraphX/AMD) should also honor CPU vendor, or whether CPU should be opt-in per catalog row.
_get_detected_vendors() is functools.cached and raises RuntimeError on detection failure by design; a CPU addition must preserve that behavior (no silent empty-set fallback).
- Tests to extend:
tests/unit/ep_path/test_compat.py, tests/unit/session/test_is_compatible.py, tests/unit/session/test_ep_device.py.
Summary
EPCatalog.is_compatible()decides EP↔hardware compatibility (the "L2" vendor check) using only GPU and NPU vendor strings. CPU is never considered.OpenVINO EP supports Intel CPU as a valid device, so on a machine with an Intel CPU but no Intel GPU/NPU, the EP is incorrectly reported as
[incompatible]and is excluded from EP/device selection.Repro
Machine: Intel Core i9-10900X CPU + NVIDIA GeForce RTX 4080 (no Intel iGPU, no NPU).
winml sysoutput:The DLL is installed and registers successfully (it is not an L1
[failed]row) — it is purely the L2 vendor rule that rejects it.Detected vendor strings on this machine:
"Intel"is present on the CPU, but the CPU is not scanned, soEP_CATALOG.is_compatible("OpenVINOExecutionProvider")returnsFalse.Root cause
src/winml/modelkit/ep_path.py—_get_detected_vendors()iterates onlyGPUandNPU:EPCatalog.Row(name="OpenVINOExecutionProvider", ..., vendor_requirements=frozenset({"Intel"}))therefore never matches on a discrete-GPU-only Intel system.winml.modelkit.sysinfo.hardware.CPUalready exposesmanufacturer("GenuineIntel") andname("Intel(R) Core(TM) i9-10900X ..."), so the data is available — it is simply not aggregated.Impact
Beyond the misleading
winml syslabel, the same predicate gates real EP selection and validation:src/winml/modelkit/commands/sys.py:893,896— status shown asincompatible, and the device list is suppressed for the row.src/winml/modelkit/config/build.py:355— build config validation rejects the EP.src/winml/modelkit/session/ep_device.py:496,612,692,764— EP/device deduction skips the EP, soOpenVINOExecutionProviderwith a CPU device cannot be auto-selected (and explicit selection is likely rejected).Net effect: OpenVINO CPU inference is unreachable on Intel-CPU machines without an Intel GPU/NPU.
Expected behavior
An EP whose vendor requirement is satisfied by any detected device class — CPU included — should be treated as compatible. OpenVINO on an Intel CPU should show as
[primary](or[shadowed]), list its available devices, and be selectable for build/session.Notes / open questions for design
CPUto the_get_detected_vendors()scan is the minimal fix, but it makes the check coarser: matching is a plain substring test over a flat vendor set, so it cannot express "this EP needs an Intel NPU" vs "an Intel CPU is enough". If any future EP needs device-class-specific gating,vendor_requirementsshould become vendor+device-class pairs rather than bare strings._get_detected_vendors()isfunctools.cached and raisesRuntimeErroron detection failure by design; a CPU addition must preserve that behavior (no silent empty-set fallback).tests/unit/ep_path/test_compat.py,tests/unit/session/test_is_compatible.py,tests/unit/session/test_ep_device.py.