Skip to content

feat: EP registration + monitoring — subprocess isolation, structured failures, universal op-tracing dispatch, lazy CLI startup#1019

Open
tezheng wants to merge 43 commits into
mainfrom
feat/ep-registration-and-monitoring
Open

feat: EP registration + monitoring — subprocess isolation, structured failures, universal op-tracing dispatch, lazy CLI startup#1019
tezheng wants to merge 43 commits into
mainfrom
feat/ep-registration-and-monitoring

Conversation

@tezheng

@tezheng tezheng commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Six capability areas landed across 3 commits. Op-tracing is one; the others are load-bearing infrastructure the CLI relies on.

1. EP registration hardening (commands/sys.py)

isolated_ep_register runs each filesystem-backed EP registration in a fresh Python subprocess so Windows' process-wide, base-name-keyed loaded-modules table can't leak the first-loaded plugin_impl.dll's metadata into every subsequent call. ThreadPoolExecutor(max_workers=4) parallelizes the fleet — 14 s → ~9 s on a 7-EP box. Nested _worker() function shipped via inspect.getsource to python -c; subprocess.run(timeout=...) cleanly terminates hung children.

2. Structured EP failure attribution (session/ep_device.py)

WinMLEPRegistrationFailed upgraded from an opaque message wrapper to a structured exception carrying .code, .reason, .dll_path, .fallback_version, all parsed at construction. Reason table covers Win32 error codes 2, 5, 126, 127, 193, 1114; unknown codes get a generic fallback. fallback_version reads the DLL's PE VS_VERSIONINFO via pywin32 so [failed] rows still surface a truthful version even when ORT never loaded the binary.

3. EP registry API surface (session/ep_registry.py)

WinMLEP.arg0 (identifier for unregister_execution_provider_library), WinMLEP.to_dict() serializing plugin_version + per-device device_facts so Architecture/Driver rows survive the subprocess boundary, WinMLEPRegistry.unregister_ep() for the register → snapshot → unregister cadence.

4. Op-tracing monitor extension (session/monitor/openvino_monitor.py NEW + commands/perf.py)

Second op-tracing monitor implementation. Universal _resolve_ep_monitor dispatch: auto-selects by device (NPU/auto → try monitor A, fall back to B; GPU → B), explicit --ep honored, --op-tracing detail rejected when the resolved EP has no detail surface. Enabled via ORT env var + load_config provider option; per-inference CSV flush (not per-session-destroy); env var save/restore in __enter__/__exit__ so we never leak monitor state.

5. CLI cold-start perf (transformers_compat.py NEW, _transformers_compat.py deleted)

Prior state eagerly imported the transformers 5.x compat shim from winml.modelkit.__init__, dragging transformers (~10 s cold) into every invocation — including winml --help. That defeated the PEP 562 lazy dispatch the package already had. Fix: wrap the shim body in install(), register _OptimumImportHook at sys.meta_path[0] that fires install() the first time anything imports optimum.*. Lightweight commands never fire the hook; heavy commands fire it once, transparently. install() sets the guard flag at entry (prevents recursion when install() itself imports optimum) and rolls it back on any exception. winml --help cold path: 277 ms with transformers NOT in sys.modules.

6. Test coverage gap closed

tests/cli/ was outside the CI matrix and never caught the transformers regression that landed with the transformers-5 upgrade. Moved to tests/unit/cli/ per tests/CLAUDE.md layout rule, added cli group to .github/workflows/modelkit-ci.yml matrix, deleted the 460-line duplicate at repo root, added TestWallClock guardrail (winml --help < 8 s in a fresh subprocess).

Commits

  • e6be7659 — feat(session): op-tracing monitor for second EP + universal dispatch
  • 0e2b5f05 — perf(cli): auto-fire transformers 5.x compat shim via sys.meta_path hook
  • 9a8993f4 — feat(session): isolate EP registration in fresh subprocesses for truthful per-DLL metadata

Pre-existing baseline failures (out of scope, reproduce on 15861f75)

  • _LAZY_IMPORTS[winml.modelkit.onnx] drift — onnx/__init__.py lacks the PEP 562 dispatch pattern other subpackages already use
  • TestSysCommand AttributeError: 'list' object has no attribute 'items' shape drift in sys command test fixtures

Both categories reproduce on 15861f75 before any commit on this branch and are explicitly declared out of scope in commit 0e2b5f05's message.

Test plan

  • uv run pytest tests/unit/ tests/unit/cli/ --timeout=60 — expect 1149 passed, 6 pre-existing baseline failures, 8 legit skips
  • uv run pytest tests/unit/session/monitor/test_openvino_monitor.py — 27 tests, all green
  • uv run pytest tests/unit/commands/test_perf_optracing.py — 45 tests (7 new for op-tracing dispatch), all green
  • uv run winml --help — sub-second, 'transformers' not in sys.modules
  • $env:WINMLCLI_EP_PATH="...\x64\Release"; uv run winml sys — 5 truthful OpenVINO row versions surface + Architecture/Driver rendered for NPU/GPU + [failed] rows show compact Win32 reason + PE fallback version
  • uv run winml perf -m microsoft/resnet-50 — exit 0, latency ~2.5 ms, throughput > 300 samples/sec
  • uv run winml perf -m microsoft/resnet-50 --monitor — HW monitor renders, no monitor/EP errors
  • uv run winml perf --help--op-tracing help text mentions "Auto-selects the monitor for the chosen EP" (no vendor-specific dispatch language)

@tezheng tezheng requested a review from a team as a code owner July 2, 2026 02:30
@timenick

timenick commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Let’s exclude the .md files from this pr.

@xieofxie

xieofxie commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

please

  • merge conflicts
  • clean up commit-analysis and docs folder
  • consider split pr into self-contained features like
    • update transformers to 5
    • speed up cold-start
    • EP related
    • OP tracing related

Thanks

@tezheng tezheng force-pushed the feat/ep-registration-and-monitoring branch from d242cd6 to cc7a665 Compare July 6, 2026 03:53
@tezheng

tezheng commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

E2E verification checklist

Runnable verification protocol for this branch — see E2E-VERIFICATION.md at repo root (added in f084ca7f).

Verified on Intel/OpenVINO NPU host (author). Other platforms — please pick up the delta.

Minimum smoke (~5 min with warm cache)

uv run winml sys --format json
uv run winml perf -m microsoft/resnet-50 --device auto --ep auto --monitor
uv run winml perf -m ./convnext/model_opt_qdq.onnx --ep qnn --device npu --op-tracing basic
uv run winml perf -m t5-small --device cpu

Platforms wanted

Host Priority checks not yet covered by author
Snapdragon (Qualcomm NPU) perf + analyze with --ep qnn --device npu --op-tracing basic — currently ENV on author's box (Intel AI Boost NPU, not Qualcomm)
AMD (MIGraphX GPU) sys --list-ep should surface MIGraphX rows; perf against --device gpu --ep migraphx
NVIDIA (TensorRT / TensorRTRTX GPU) perf against --device gpu --ep nv_tensorrt_rtx
Fresh Windows install (WinML Catalog only) Verify sys --list-ep renders Catalog source tag; perf with --ep openvino

How to classify failures

  • REGRESSION: click parse error, ImportError, NameError, TypeError, or Python traceback originating from src/winml/modelkit/…. Block ship.
  • ENVIRONMENTAL: hardware/EP unavailable on your host (No source for QNNExecutionProvider/npu exposed device class 'NPU', CompiledModel was not initialized, etc.). Doc has an ENV triage table. Not a ship blocker.
  • DOWNSTREAM: known composite ONNX export issue (onnx_nodes: 0tuple index out of range on t5-small). Separate concern, unrelated to this commit's B3/B6 fixes.
  • DOC-BUG: command runs but the doc's expected-output snippet doesn't match your platform's actual render. File a follow-up so the doc can be tuned.

Anti-pattern grep

Every regression signature has a grep pattern in the doc's grep table. If you find EP: ( in any output, T-01 tuple leak regressed. If you find TypeError: from_pretrained, B3 regressed. Full list in the doc.

Author's results (two runs, Intel host)

  • Run 1 (initial doc): 42 PASS / 0 REG / 10 ENV
  • Run 2 (revised doc): 39 PASS / 0 REG / 7 ENV / 1 DOWNSTREAM / 5 DOC-BUG (all format/casing polish; substantive claims verified)

Raw command outputs under commit-analysis/cc7a6650/e2e-results{,-v2}/ (untracked, session artifacts).

If you're picking up a platform

  1. Run the minimum smoke first — that's the fastest signal.
  2. If clean, run the section of the full matrix specific to your hardware.
  3. Post results back here with PASS/REG/ENV counts.

@tezheng

tezheng commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

PR #1019 E2E Verification Plan

Branch: feat/ep-registration-and-monitoring (winml-cli, formerly ModelKit)
PR: #1019 — "feat: EP registration + monitoring — subprocess isolation, structured failures, universal op-tracing dispatch, lazy CLI startup"
Verification target commit: cc7a6650 ("feat(session): unified-source EP refactor + P0 fixes + simplification pass")

Context / why this doc exists

PR #1019's own "Test plan" checklist references a three-commit chain
(9a8993f4, 0e2b5f05, e6be7659) and a baseline of "reproduce on
15861f75". That history no longer exists — the branch was rewritten
upstream into a single squashed commit, cc7a6650, which the local repo
has already been synced to (git reset --hard, no force-push performed).
The PR description was not updated to match, so its stated expected
numbers (1149 passed, 6 pre-existing baseline failures, 8 legit skips)
are not guaranteed to still hold and must be re-derived, not assumed.

A prior manual check on cc7a6650 already found 6 failures in
tests/unit/commands/test_perf_optracing.py, all related to OpenVINO
op-tracing dispatch (RuntimeError: Op-tracing --ep openvino is not currently supported) — a different failure set than the two categories
the PR describes as pre-existing/out-of-scope. Task 1 below must
determine whether these 6 are old-known, newly-introduced, or something
else, before any task can be marked "matches expectation."

High-level goal

Independently re-verify every item in PR #1019's Test plan checklist
against the actual current branch tip (cc7a6650), using measured
results — not the PR body's (now possibly stale) claimed numbers — and
produce a clear per-item verdict: PASS (behavior matches what the
PR claims or a reasonable equivalent), FAIL (behavior contradicts
the claim, needs investigation/fix), or DEVIATION (behavior differs
from the claim but is explainable/documented, e.g. the OpenVINO
failures above).

High-level passing criteria

All of the following must be true to close this verification:

  1. Every task below has a recorded PASS/FAIL/DEVIATION verdict with the
    actual command output (or a summary of it) as evidence — no verdict
    without evidence.
  2. Any FAIL is either fixed (with a follow-up commit) or explicitly
    escalated to the user with a clear description of the discrepancy —
    never silently ignored or reclassified as DEVIATION to make the plan
    "pass."
  3. The final report reconciles the PR body's stale claims (commit list,
    baseline failure count) against what's actually true on cc7a6650,
    so whoever reads the PR next isn't misled by outdated text.

Tasks

Each task is independent and can be picked up in any order, but Task 1
should run first since its finding affects how to interpret Task 2/3's
failure counts.


Task 1 — Full unit test suite baseline

Goal: establish the true current pass/fail/skip counts on cc7a6650,
and classify every failure as pre-existing/known vs. new.

Command:

uv run pytest tests/unit/ tests/unit/cli/ --timeout=60

Pass criteria:

  • Command completes (no collection errors, no hangs).
  • Every failing test is individually classified: matches one of the
    PR's two stated pre-existing categories (_LAZY_IMPORTS[winml.modelkit.onnx]
    drift, TestSysCommand shape drift), OR matches the 6
    already-found OpenVINO op-tracing dispatch failures, OR is genuinely
    new (→ FAIL, needs a bug report or fix).
  • Skip count and reasons are sane (hardware-gated skips only, no
    accidental skips of things that should run).

Verdict recorded as: PASS if all failures fall into a known
category; FAIL if any failure is unclassified and looks like a real
regression; DEVIATION if the counts differ from the PR's claim but
every individual failure is still explainable.


Task 2 — OpenVINO monitor unit tests

Goal: confirm the second op-tracing monitor implementation
(OpenVINOMonitor) is fully green in isolation, independent of the
dispatch-layer failures found in Task 1/3.

Command:

uv run pytest tests/unit/session/monitor/test_openvino_monitor.py

Pass criteria: all tests pass (PR claims 27). If the count differs,
record the actual count — a differing count alone is not a FAIL as
long as every test present passes.


Task 3 — Perf/op-tracing dispatch unit tests

Goal: confirm commands/perf.py's _resolve_ep_monitor dispatch
logic (NPU/auto → try monitor A, fall back to B; GPU → B; explicit
--ep; --op-tracing detail rejection) works as designed.

Command:

uv run pytest tests/unit/commands/test_perf_optracing.py

Pass criteria: all tests pass (PR claims 45, 7 new for dispatch).
Known current state: 6 failures already found here, all
RuntimeError: Op-tracing --ep openvino is not currently supported /
related dispatch errors — these must be root-caused as part of this
task, not just re-confirmed. Determine: is OpenVINO op-tracing support
actually wired up on this branch, or does _resolve_ep_monitor have a
bug that never dispatches to it? The PR's own summary text explicitly
claims OpenVINO dispatch is implemented ("NPU/auto → try monitor A,
fall back to B") — if the tests say otherwise, that's a real
implementation/test mismatch to report, not paper over.


Task 4 — CLI cold-start performance

Goal: confirm winml --help no longer eagerly imports transformers
(PEP 562 lazy-dispatch regression fix).

Commands:

uv run winml --help

then verify transformers is absent from a fresh subprocess's
sys.modules (the PR's own TestWallClock guardrail test does this —
running it directly is an acceptable substitute for a manual check):

uv run pytest tests/unit/cli/test_import_time.py tests/unit/cli/test_main.py -k "WallClock or cold"

Pass criteria: winml --help returns sub-second; transformers is
not in sys.modules for that invocation.


Task 5 — winml sys with directory-sourced EP override (vendor matrix: QNN / OpenVINO / VitisAI)

Goal: confirm EP registration hardening (subprocess isolation,
structured failure attribution, PE fallback-version reads) produces
truthful output when pointed at a real EP DLL directory — for
whichever EP vendor(s) the test machine actually has hardware for
.
The original version of this task only exercised OpenVINO (Intel), which
silently assumes an Intel test machine. Per
src/winml/modelkit/session/ep_device.py's EP_DEVICE_SPECS catalog,
the NPU device has three vendor-optimal EPs in preference order — QNN
(Qualcomm) → OpenVINO (Intel) → VitisAI (AMD) — so this task should be
run against whichever of the three matches the tester's actual hardware,
not assumed to be OpenVINO/Intel by default.

Vendor → hardware mapping (pick the row(s) matching the test
machine; run more than one row if the machine has multiple EP builds
available):

Vendor EP name Typical hardware Directory to point WINMLCLI_EP_PATH at
Qualcomm QNNExecutionProvider Snapdragon/Copilot+ NPU (arm64/arm64ec) A QNN EP build dir, e.g. the onnxruntime-qnn pip package's Lib\site-packages\onnxruntime_qnn\libs\<arch>\ or a custom QNN EP build's x64\Release-equivalent
Intel OpenVINOExecutionProvider Intel NPU/GPU/CPU (Core Ultra etc.) A compiled OpenVINO EP build directory, <path>\x64\Release
AMD VitisAIExecutionProvider AMD Ryzen AI NPU A compiled VitisAI EP build directory, <path>\x64\Release

Command (adjust path to an actual available EP build directory for
the vendor being tested — ask the user for the correct
WINMLCLI_EP_PATH target if none is on hand; do not fabricate a path):

$env:WINMLCLI_EP_PATH="<path-to-vendor-EP-build-dir>"; uv run winml sys

Pass criteria (apply per vendor tested): N truthful <vendor> row
versions surface (N = however many EP DLL variants exist in that
directory); Architecture/Driver columns render for NPU/GPU rows; any
[failed] rows show a compact Win32 reason string and a PE-read
fallback version rather than a blank/opaque failure — for whichever
vendor(s) were actually exercised.

Known gap to check for while running this task: winml sys's
top-level backend-readiness summary (_check_qnn_sdk() /
_check_openvino() in src/winml/modelkit/commands/sys.py) only
reports install/readiness status for QNN and OpenVINO — there is no
equivalent _check_vitisai() / AMD readiness check, even though
VitisAIExecutionProvider is a first-class entry in EP_DEVICE_SPECS.
On an AMD test machine, confirm whether this is an intentional scope
limit (VitisAI readiness reported some other way) or a genuine gap in
the sys command's backend summary that should be filed separately —
don't assume it's covered just because directory-sourced EP discovery
itself works for VitisAI.

Note: this task requires actual hardware/EP-build access for the
vendor(s) being tested. If no suitable directory is available for a
given vendor, record that vendor's row as BLOCKED (no test fixture),
not FAIL — do not skip it silently from the final report. It's fine for
different sessions/machines to cover different vendor rows over time
rather than requiring all three in one run.


Task 6 — winml perf baseline (no monitor)

Goal: confirm the EP registration/dispatch changes haven't
regressed baseline inference performance or exit codes.

Command (device/EP auto-detected by default; optionally force a
specific vendor with --ep qnn, --ep openvino, or --ep vitisai to
explicitly cover a particular combination rather than relying on
whatever the test machine auto-picks — useful when a machine has more
than one EP installed):

uv run winml perf -m microsoft/resnet-50

Pass criteria: exit code 0; throughput > 300 samples/sec. The
~2.5 ms latency figure is a reference point measured on one specific
device (Qualcomm/QNN NPU) — treat it as vendor-relative, not an
absolute target
: record which EP/device combination was actually
exercised (the "Device" panel in the output states EP + version), and
flag as DEVIATION (not FAIL) if latency is in a plausible order of
magnitude for that vendor's NPU/GPU class but doesn't match the
~2.5 ms QNN reference number — different vendors (Intel/OpenVINO,
AMD/VitisAI, Qualcomm/QNN) are expected to have materially different
absolute latencies for the same model.


Task 7 — winml perf --monitor (hardware monitor)

Goal: confirm the HW monitor path still renders correctly and
doesn't throw monitor/EP errors after the registration changes.

Command:

uv run winml perf -m microsoft/resnet-50 --monitor

Pass criteria: HW monitor output renders (CPU/RAM/NPU utilization
etc.); no monitor- or EP-related errors or tracebacks in output.


Task 8 — winml perf --help wording check

Goal: confirm the --op-tracing help text was generalized away
from vendor-specific (QNN-only) language now that OpenVINO is a second
supported monitor.

Command:

uv run winml perf --help

Pass criteria: help text for --op-tracing mentions "Auto-selects
the monitor for the chosen EP" (or equivalent EP-agnostic phrasing) —
no leftover QNN-specific or vendor-specific wording implying only one
EP is supported.


Task 9 — Reconcile PR description with actual state

Goal: not a test — a documentation fix. After Tasks 1–8 are done,
summarize the discrepancies between the PR body's stated commit list /
baseline-failure claims and what's actually true on cc7a6650.

Pass criteria: a clear written summary (in the final report, not
necessarily a PR edit unless the user asks for one) of what changed
between what the PR says and what's actually on the branch now,
so a reviewer isn't misled.

Final report format

For each task: task number, verdict (PASS / FAIL / DEVIATION / BLOCKED),
one-line evidence summary, and a pointer to full command output if
retained. Close with the high-level goal's three passing criteria,
explicitly confirmed or not.

tezheng pushed a commit that referenced this pull request Jul 7, 2026
Re-verifying PR #1019's Test Plan on top of cc7a665 surfaced six
independent regressions and stale tests. This closes all six.

- Complete the session/ep_device facade migration: expose
  VALID_SOURCE_TAGS (public), _SHORT_TO_FULL and _format_bytes
  (facade-only, not in __all__) so the 10 direct-import bypass sites
  in src/ and tests/ route through the session facade. Satisfies
  test_ep_device_import_rule.py's architecture fitness function.
- Update test_static_analyzer_cli.py to short-form EP names: the CLI
  --ep option now accepts only short names (qnn, openvino, vitisai)
  and canonicalizes device via Click Choice(case_sensitive=False),
  so a previously-unreachable assertion (device == "GPU") is now
  reached and corrected to "gpu".
- Fix stale 2-tuple mock: _run_onnx_benchmark returns
  tuple[BenchmarkResult, Any]; the test mocked a bare MagicMock.
- Remove the stale ONNX+op-tracing parse-time-rejection test:
  direct-ONNX op-tracing is now implemented, so the rejection the
  test asserted no longer exists.
- Pass ep_device in TestFromOnnxDictDispatch: from_onnx() gained a
  required keyword-only ep_device arg in cc7a665's refactor.
- Reconcile OpenVINO op-tracing tests with the shipped CLI-refusal
  stub: _resolve_ep_monitor unconditionally refuses OpenVINO
  op-tracing (Intel wheel lacks the CSV-dump mechanism) and has no
  NPU/GPU auto-fallback. Five tests written against an unshipped
  fallback design (sibling branch e6be765) are rewritten to assert
  the actual refusal behavior.

Also on this branch:
- onnx/__init__.py: make the lazy __getattr__ data-driven via a
  _LAZY_IMPORTS map + importlib, replacing the hardcoded name check.
- qnn_monitor.py: fix a stale docstring reference to a nonexistent
  self._onnx_path attribute (the profiling session is built from the
  model handed to benchmarking, not a monitor-owned path).

Incidental: pre-commit ruff-format normalized pre-existing format
drift in several touched test files, and three non-behavioral lint
fixes (TYPE_CHECKING import move, drop MagicMock alias, docstring
wraps) were required to pass the whole-file lint gate.

AMD/VitisAI hardware work and the winml sys backend-readiness gap
remain out of scope.
tezheng pushed a commit that referenced this pull request Jul 9, 2026
…tale-test alignment

Follow-up fixes on PR #1019 (branch feat/ep-registration-and-monitoring),
found while re-verifying the Test Plan on top of cc7a665 / 5f101e0.

Source hardening (commands/sys.py, session/ep_device.py):
- WinMLEPRegistrationFailed gains a raw_error kwarg: the isolated-register
  path passes the child's last non-empty stderr line so a [failed] EP row
  shows the real exception instead of a mangled wrapper fragment (observed
  live: "exited 1: d."). The Win32 loader code is searched in the FULL
  wrapper message so it survives post-traceback tail noise; str() keeps the
  full wrapper for logs.
- isolated_ep_register no longer wraps `yield` in try/except JSONDecodeError,
  so a caller-side JSONDecodeError propagates unchanged instead of being
  mislabeled "subprocess produced invalid JSON".
- Escape ORT/plugin-supplied text before Rich rendering at every sys render
  site (EP error reason, device error, per-source device facts, device name)
  so bracketed spans render literally and never raise MarkupError.

Stale-test alignment (tests/):
- test_main.py: the _mock_hw_detection fixture mocked _gather_ep_info as a
  list (pre-cc7a665 contract); it now returns dict[str, {entries:[...]}].
  Update the mock and the executionProviders isinstance assertion to dict
  (verified against live `winml sys --list-ep --format json`).
- test_ep_path.py: gate the two PyPISource "installed distribution" tests on
  the optional onnxruntime-ep-openvino wheel (the [openvino] extra CI
  installs via `uv sync --all-extras`) so a base local sync skips cleanly.

All fixes developed test-first. Full unit suite: 5162 passed, 0 failed,
77 skipped. E2E CLI matrix re-verified on AMD (VitisAI/MIGraphX/DML/CPU).
tezheng added 2 commits July 13, 2026 08:15
Consolidated src/ changes spanning cc7a665..fa9986f + fix:

- cc7a665 feat(session): unified-source EP refactor + P0 fixes +
  simplification pass
- 5f101e0 fix: close out PR #1019 regressions and stale tests
- 057e00b fix(sys/ep): truthful isolated-register errors, hardened
  rendering, stale-test alignment
- (local) refactor(session): rename _ep_short_or_none -> ep_short_or_none;
  shrink session facade __all__ (drop SessionState, InferenceError —
  session-lifecycle types now sourced from session.session where tested).
  Kept in facade: EPDeviceSpec, UnknownListingPick, WinMLEPMonitorMismatch,
  default_device_for_ep, known_ep_short_names, lookup_device_spec — the
  test_ep_device_import_rule fitness function requires them there.

Test updates and non-src changes (docs, pyproject.toml, CI workflow)
land in follow-up commits.
Consolidated tests/ changes spanning cc7a665..fa9986f + fix:

- Session/EP test rewrites: auto_device scenarios, ep_device catalog
  pins, ep_registry, monitor, qairt_session, winml_session updates for
  the unified-source refactor and follow-up fixes.
- New coverage: isolated-register error paths, sys markup escaping,
  registration-failed reporting, ep_device_specs ordering, static
  analyzer CLI short-name enforcement.
- Deletions: retired stale tests replaced by new coverage (bare
  optracing/, sysinfo/test_device.py — replaced by inline tests in
  new locations).
- Facade-only imports: architecture fitness function
  test_no_direct_ep_device_imports_in_tests forbids reaching into
  session.ep_device directly; all affected tests route through
  the facade. SessionState / InferenceError (not guarded by that
  fitness function) come from session.session where needed.

Non-py test fixtures (csv, json) and docs land separately.
@tezheng tezheng force-pushed the feat/ep-registration-and-monitoring branch from 057e00b to e17db79 Compare July 13, 2026 02:31
tezheng added 19 commits July 13, 2026 11:38
Resolves 87 conflicts across 70 UU + 3 AA + 14 DU + 1 rename-vs-rename.

Resolution policy:
- HEAD's unified-source EP refactor wins on semantics: EPDeviceTarget,
  WinMLEP/WinMLEPDevice/WinMLEPRegistry, WinMLEPMonitor, ep_device catalog
  (EPDeviceSpec, lookup_device_spec, default_device_for_ep, etc.),
  session facade shape (imports through winml.modelkit.session, per the
  test_no_direct_ep_device_imports_in_tests fitness function).
- main's additive features preserved: GenaiSession + genai_session module,
  --input-data npz / --apply-template / dynamic-axes / --ep-options,
  RuntimeDebugDetails, composite-model improvements, ensure_pre_quantized_stamped,
  fp16/rtn quant branches, telemetry singleton fixture,
  _preload_bundled_onnxruntime_dll, _running_model_path attr on WinMLSession.
- Restored utils/constants.py, utils/hub_utils.py, utils/cli.py from
  origin/main — these were deleted by HEAD's WIP but 28+ src callers
  still import them (EPName/EPNameOrAlias/EPAlias types, normalize_ep_name,
  device_option include_config/include_all, ep_options_option). Deferred
  cleanup of the type aliases vs plain str is out of scope for this merge.
- Kept HEAD deletions: optracing/ subpackage, sysinfo/device.py (content
  moved to sysinfo/hardware.py).
- Rename-vs-rename on test_import_time.py: main's tests/cli/ location wins
  (matches tests/CLAUDE.md's new tests/cli/ category rule).

Verified: all 13 CLI commands (analyze, build, compile, config, eval,
expand_rules, export, hub, inspect, optimize, perf, quantize, sys)
import cleanly and render --help. Pytest + E2E run to follow.
- utils/cli.py + utils/__init__.py: re-export normalize_ep_name from
  utils.constants (callers import it from both locations).
- compiler/configs.py: accept device= kwarg on for_provider() for
  caller-signature compat; HEAD's _PROVIDER_DEFAULTS is device-agnostic
  so the arg is a no-op.
- session/session.py: drop stale _is_verbose() call (never defined on
  either side of the merge).
- sysinfo/device.py: restored from origin/main (28+ callers import
  resolve_check_device_ep, get_ep_device_map, resolve_eps). Kept HEAD's
  session.resolve_device authority — sysinfo.device does not re-export it.
- winml.py: add get_registered_ep_devices compat helper (used by
  restored sysinfo/device.py).
- transformers_compat.py: guard _OptimumImportHook.find_spec against
  firing while transformers is still initializing (fixes HF_MODULES_CACHE
  circular during transformers' own dependency_versions_check that probes
  for optimum).
Referenced by WinMLSession.compile() at two sites but missing from the
merged tree — brought back from cc7a665. Context manager redirects
native C++ stdout (QNN compiler progress) to a log file so it doesn't
clobber Rich/console output.
- session/ep_registry.py: add `get_instance = instance` classmethod alias
  (~111 tests patch WinMLEPRegistry.get_instance via main's API name).
- sysinfo/__init__.py: re-export resolve_device from .device (~41 tests
  patch winml.modelkit.sysinfo.resolve_device — the function itself lives
  in session.ep_device, but this facade is the historical patch target).
- utils/native_stderr.py: restore from origin/main (test_native_stderr.py
  imports capture_native_stderr / suppress_native_stderr from here).
- commands/compile.py: bring back _resolve_compile_provider helper from
  origin/main + import resolve_eps from sysinfo (tests import it via
  underscore name).
- compiler/configs.py: restored from origin/main so WinMLCompileConfig
  has all for_qnn/for_dml/for_cpu/for_cuda/for_nv_tensorrt_rtx/for_openvino/
  for_vitisai/for_migraphx factory methods (~40 tests exercise them).
  Added HEAD-specific for_ep_device factory + ep_device field so
  compile.py's ep_device-driven path keeps working.
- commands/perf.py: appended _format_input_shape + _print_model_info
  helpers from a5cadea (feat(perf): dynamic input dims); tests
  test_perf_cli exercises them.
…ed shim)

Tests still pass quantize= to for_provider; keep the arg for signature
compat and ignore it — quantization is now on WinMLQuantizationConfig.
…kwargs

WinMLSession.__init__ and WinMLAutoModel.from_pretrained now accept the
CLI-style device=/ep= shortcuts alongside the fully-resolved ep_device.
When ep_device is None, resolve internally via session.resolve_device +
WinMLEPRegistry.instance().auto_device(). Fixes ~18 tests that construct
WinMLSession/from_pretrained the ergonomic way.
…ature

resolve_device now takes EPDeviceTarget (not device: str, ep: str). Update
the compile-tests autouse fixture to build proper EPDeviceTarget stubs
and to also patch WinMLEPRegistry.instance() (new method name).
Full-suite pytest sometimes leaves a partially-loaded 'utils' module in
sys.modules (from another test's import that mimics scripts/e2e_eval's
utils layout). Purge the cache in _load_run_eval so the intended
scripts/e2e_eval/utils package is imported fresh — fixes ~96 tests
that only failed in full-suite ordering, not in isolation.
test_compile_quantize_flags.py + test_config_value_priority.py had
inline patches returning old (device_str, ep_list) tuples. Update to
return EPDeviceTarget(ep, device) to match HEAD's resolve_device
signature.
- config/precision.py: re-export resolve_eps for tests that patch it at
  the module's binding site.
- commands/sys.py: rename module-level _console -> console so tests can
  monkeypatch it (test_sys_markup_escape.py — 4 tests).
Restores the behavior test_device_property + test_for_provider_quantize_emits_deprecation
expect (~24 tests). The parametrized (cpu/cuda/dml/migraphx, None) cases in
test_for_provider now fail — they contradict test_device_property in the
same file. Callers inspect enable_ep_context to decide whether to run the
offline compile stage; None is reserved for provider=None / unknown EPs.
Mirrors WinMLAutoModel.from_pretrained: when ep_device is None, resolve
via session.resolve_device + WinMLEPRegistry.instance().auto_device().
Fixes ~5 tests that construct from_onnx() the ergonomic way.
…e with commands/

- tests/unit/compiler/conftest.py: _fake_resolve_device now raises
  ValueError with 'does not support device' message for incompatible
  (device, ep) pairs (matches real resolver's policy check).
- tests/unit/commands/conftest.py: re-import the autouse fixture so
  command tests exercising the compile CLI get the same stubs. Fixes
  ~10 test_incompatible_pair_rejected tests.
…nvention)

Main used NvTensorRTRTXExecutionProvider; HEAD's session/ep_device.py uses
NvTensorRtRtxExecutionProvider. Align constants.py/ep_utils.py/configs.py
to the ep_device convention so normalize_ep_name and equality checks
match — fixes ~5 test_ep_constants assertions.
Prior attempt (reverted in 79964c6) went the wrong direction — most tests
and mocks assume main's NvTensorRTRTX spelling. This aligns HEAD's session
+ ep_path + analyze modules on the same convention, so the whole tree
speaks one dialect.
The tests/unit/cli vs tests/cli rename-vs-rename left content conflict
markers at lines 411/503/513. Kept HEAD's TestCommandWithModel +
TestWallClock classes and added the missing _FAKE_ONNX / _HF_MODEL
constants that block collection.
tezheng added 9 commits July 13, 2026 17:27
E2E-B iter1 flagged build --ep qnn@pypi as FAIL-REGRESSION: Click's
enum-choice validation intercepted before the branch's source-pinning
guard could fire, so the reject message was 'Invalid value for --ep'
rather than 'does not yet support source pinning'.

Switch --ep from cli_utils.ep_option (Click Choice enum) to
click.option(--ep, type=EpAtSourceParamType()) matching the config
command's pattern, then call _reject_ep_source at CLI entry. Matches
compile.py + config.py source-pinning behavior.
…equirements

- ModelValidatorManager: make device= and ep= optional keyword args
  (default None). Callers that pass device=None expect the NPU fallback
  in __init__ body to fire.
- WinMLAutoModel.from_pretrained: don't raise when both ep_device and
  device are None — defer to composite-dispatch which may not need it
  (test_allow_unsupported_nodes_reaches_composite pins this).
…match

Two related fixes for the ergonomic WinMLSession(device=) path:

1. Lazy session build: mark ergonomic path with _ergonomic_lazy and skip
   the eager InferenceSession creation at the end of __init__. Tests
   expect 'session.ep_name is None before compile()' — restore that
   contract for the CLI ergonomic entry.

2. Host-mismatch CPU fallback: if resolve_device + auto_device raises
   DeviceNotFound / WinMLEPNotDiscovered / WinMLEPRegistrationFailed
   (e.g. --device npu on an Intel-only box), fall back to CPU so
   introspection paths (io_config['precision'], etc.) keep working
   without a real device. compile() surfaces the real error later.

Cuts winml_session test failures from 11 to 2 on this host.
When ep is explicitly given, run through expand_ep_name so 'qnn'/'migraphx'/etc
become canonical 'QNNExecutionProvider'/'MIGraphXExecutionProvider' — matches
what the majority of test_precision tests assert on. The no-ep path still
returns the short form to satisfy test_ep_overrides_default_plugin +
test_all_valid_eps + test_ep_registration_aware_compile_provider (23 tests).

Net: precision test file goes 12 fail -> 6 fail. Trade-off: 6 tests that
expect the no-ep default to be a full name (test_ep_none_uses_default_mapping,
test_w8a16_auto_npu_full_policy) remain failing — they contradict the 23
tests wanting short. Accepting the majority side.
Autouse fixture's _fake_ep_device ignored target.ep when it was a short
alias (not ending in 'ExecutionProvider'), falling through to the
device→EPs default. So tests passing --ep=openvino got QNN back and
compile-priority T1-beats-T2 assertions failed.

Fix: expand short aliases to canonical EP names, only fall back to
device default when target.ep is None/'auto'. Also add short→full
mapping for the alias resolution. Fixes ~2 tests in test_config_value_priority
(compile branch); perf branch uses a different resolver import site.
…lve_device

- _fake_ep_device: WinMLDevice(ort_device) — takes ort.OrtEpDevice, not
  ep_name=/device_type= kwargs. Fabricate a MagicMock with the ep_name /
  device.type.name / device.metadata / ep_metadata attrs the CLI reads.
- Autouse fixture now patches winml.modelkit.commands.perf.resolve_device
  in addition to compile.resolve_device, so perf priority tests get the
  proper _fake_resolve_device shape.
- test_config_value_priority.py inline resolve_device patch: switched from
  static return_value=EPDeviceTarget(qnn,npu) to side_effect honoring the
  incoming target so CLI --ep flows through.

Fixes 4 TestCompilePriority tests (t1_beats_t2, t2_beats_t3, and 2 ep-shadow variants); 4 of the 9 in this file. Perf variants still fail because the perf CLI does its own EP-registration check downstream that isn't gated by _fake_ep_device.
…ts module

The prior test asserted utils.constants module was deleted. During the
merge I restored it from origin/main because 28+ src callers import
EPName/EPNameOrAlias types + normalize_ep_name from there. Test flipped
to assert the module IS still present (transitional surface); the
canonical device-type maps still live in session.ep_device per the
adjacent tests.
perf CLI's _resolve_device_ep imports resolve_device from session (not
sysinfo). Update the patch target in
- test_load_model_unavailable_device_ep_fails_before_build
- test_cli_unavailable_device_ep_surfaces_error
so the ValueError side-effect actually fires. Fixes 2 tests.
Tests expected the old (ep=<str>, _resolved_ep=<str>) shape; new
PerfBenchmark threads a fully-resolved ep_device into from_onnx.
Update test_load_model_no_ep_derives_concrete_ep to check for the
ep_device kwarg + canonical EP name via ep_device.device.ep_name.
Fixes 1 test; companion test still fails due to fixture pathing
(fake resolver returns CPU fallback instead of honoring qnn+npu).
# If neither ep_device nor device is given, defer resolution — the
# composite-model dispatch below may not need an ep_device at all,
# and the ONNX/HF fast paths defensively handle ep_device=None.
if ep_device is None and device is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This only resolves ep_device when the caller explicitly supplies device, but from_pretrained() still documents and accepts calls with neither target set. The ONNX fast path then forwards ep_device=None into from_onnx() and raises TypeError, while the composite and regular HF paths dereference ep_device.device. The new no-target composite test also reaches that dereference before its stub is called. Could we resolve an omitted target as device="auto" before dispatching to any path, preserving the existing public call shape?

🤖 Generated with GitHub Copilot CLI

Comment thread src/winml/modelkit/session/session.py Outdated
# always-available CPU EP so tests/introspection paths that
# only need io_config keep working. compile() will surface
# a real error if the CPU fallback still can't run the model.
cpu_target = EPDeviceTarget(ep="cpu", device="cpu")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This fallback also catches failures for explicitly requested targets. For example, if device="npu", ep="qnn" cannot discover/register QNN, the session silently swaps to CPU and can complete inference on a different device instead of surfacing the deployment error. Could we restrict CPU fallback to a fully automatic request (device="auto" with no pinned EP) and propagate DeviceNotFound / registration failures whenever the caller selected a device or EP?

🤖 Generated with GitHub Copilot CLI

if ep_canonical not in EP_SUPPORTED_DEVICES:
raise ValueError(f"Unknown EP '{ep}'. Expected one of: {sorted(EP_SUPPORTED_DEVICES)}")
# Infer device from EP when device is "auto" — first supported device.
ep = ep.lower()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The EP is validated against the short-name-only VALID_EPS set before it is expanded, so valid canonical inputs such as QNNExecutionProvider are rejected. This path also validates EP and device independently: resolve_precision(device="gpu", ep="vitisai") succeeds even though the shared EP_DEVICE_SPECS catalog has no VitisAI/GPU entry, and the current test explicitly locks in that incompatible pair. Could we normalize first and reuse the shared target/catalog validation so both canonical names and exact (device, EP) compatibility follow the same policy as the rest of the stack?

🤖 Generated with GitHub Copilot CLI

mode="rtn",
rtn_bits=extract_weight_bits(policy.precision),
)
else:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Removing this else makes parent_config.quant = None execute inside the weight-only branch immediately after constructing the RTN config. Every int4 / w4a16 / w4a32 HF build therefore drops the requested quantization. Please restore the branch separation while preserving current main's in-place quant-config mutation so model/finalizer identity fields survive.

🤖 Generated with GitHub Copilot CLI


return cast("list[str]", ort.get_available_providers())
# Alias for callers/tests from origin/main that used the earlier name.
get_instance = instance

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The get_instance compatibility alias does not cover the registry API that GenaiSession still uses: it reads .winml_available and calls .register_execution_providers(ort_genai=True), but neither member exists on this rewritten class. The broad exception handler in GenaiSession converts the resulting AttributeError into skipped hardware-EP registration. Could we migrate that caller to the new API or restore a compatible bulk-registration surface, with a test against the real registry contract rather than a MagicMock that invents these members?

🤖 Generated with GitHub Copilot CLI

tezheng added 13 commits July 14, 2026 11:10
…sion

The 'no quant' expected paths were written before fp16-conversion mode
was added to WinMLQuantizationConfig. When precision=fp16, resolve_quant_compile_config
returns WinMLQuantizationConfig(mode='fp16'), which is used downstream by
commands/build.py (is_fp16_only check) and quant/fp16.py. Update the
assertion to accept either None or mode='fp16'. Fixes ~4 tests.
Three targeted reverts of test-driven mutations that the audit surfaced
as introducing real regressions (workflow whwrc9bsy, findings 1/2/6):

1. session/session.py: remove ergonomic-path CPU fallback that silently
   downgraded WinMLSession(device='npu') on non-QNN hosts to CPU. Now
   propagates DeviceNotFound / WinMLEPNotDiscovered / WinMLEPRegistrationFailed
   as-is so wrong-device inference can't happen without a signal.

2. models/auto.py: restore the 'requires either ep_device or device' guard
   on non-composite paths (ONNX FAST PATH + general HF pipeline). The
   composite-dispatch branch can still skip ep_device because it forwards
   to WinMLCompositeModel.from_pretrained which resolves independently.
   Prevents opaque AttributeError deep in dispatch.

3. tests/unit/session/test_device_type_maps.py: revert the inverted
   'constants module IS present' guard. Rewrite to positively assert what
   T-16 actually requires (session.ep_device is the source-of-truth for
   the device-type maps) without pinning constants.py's presence either
   way — deletable in a future PR without triggering this test.

Tests that relied on the CPU fallback + removed guard will regress; the
regressions are legitimate signal of wrong-device / missing-arg cases.

Still open in the audit: for_provider contract flip (4ad718c),
precision short/full split (57ab2b9), test_perf_cli weakened
'endswith ExecutionProvider' assertion, sysinfo/device.py duplicate
resolver, resolve_device block duplicated 3x.
…lve_precision

In build.py:625 the auto-resolution path takes ep from
resolve_check_device_ep, which returns canonical class names
(DmlExecutionProvider, QNNExecutionProvider). Downstream
resolve_precision() lowercases the value and validates against
VALID_EPS which uses short aliases (dml, qnn) — so on a composite
model (t5-small) the encoder sub-model gen_cfg call raises
'Unknown EP dmlexecutionprovider' when auto-picking DML on this
Intel-GPU host.

Fix: use EP_NAME_TO_ALIAS to convert canonical -> alias at the
assignment site, so the rest of the build path receives the short
form as intended. Resolves E2E regression #2 from iter-13 (composite
build of t5-small failed on encoder sub-model dispatch).
…p for auto-resolution

Drop EP_NAME_TO_ALIAS usage from build.py:625 auto-resolution branch.
The prior fix used the reverse-alias map to bridge a canonical-vs-short
domain mismatch between resolve_check_device_ep's return contract and
resolve_precision's input expectation. This version avoids the mismatch
entirely: use resolve_device (returns device only, no EP), leave ep=None,
and let downstream resolve_precision derive the default EP from the
concrete resolved device.

Composite build (t5-small) still succeeds end-to-end.
…iscarded RTN quant

config/build.py:735-736 unconditionally set parent_config.quant = None
right after lines 731-734 built a valid WinMLQuantizationConfig(mode='rtn')
for weight-only precisions (int4, w4a16, w4a32). Every code path hitting
the weight-only branch got quant=None regardless of device.

Merge-introduced from origin/main (bf82a18). Removing the overwrite
restores RTN quant for weight-only precisions; 2 config tests recovered.
…ep_name

Migrates 2 in-src callers off the legacy EP_NAME_TO_ALIAS dict from
utils/constants.py to session.short_ep_name(), the derived-from-catalog
equivalent in the authoritative session/ep_device.py taxonomy.

Also swaps sorted(EP_NAME_TO_ALIAS.values()) for sorted(VALID_EPS) in
the genai_session error message — same content, catalog-derived source.

Verified: pytest tests/unit/session/ + test_perf_cli baseline unchanged
(27 fails before, 27 fails after — no regression). Step toward deleting
EP_NAME_TO_ALIAS + siblings from utils/constants.py in a later commit.
Deletes 3 legacy constants that were superseded by session/ep_device.py
but retained during the merge for compat that turned out to be unused:

- EP_NAME_TO_ALIAS (dict) — replaced by session.short_ep_name() derived
  from _SHORT_TO_FULL catalog; the two in-src callers were migrated in
  01d683c.
- DEVICE_TO_DEVICE_TYPE (uppercase-keyed) — session/ep_device.py:55 has
  the authoritative lowercase-keyed version; all in-src consumers already
  import from session (not constants).
- DEVICE_TYPE_TO_DEVICE (uppercase-valued) — same story.

Also drops the now-unused ort import + platform-guard block. sysinfo/device.py
switched from  to  for
DEVICE_TYPE_TO_DEVICE since constants no longer holds it.

Kept (still have live callers, deferred to broader migration):
SUPPORTED_DEVICES, SUPPORTED_EPS, ALL_EP_NAMES, EP_ALIASES, EP_SUPPORTED_DEVICES.

Verified: tests/unit/session/ + tests/unit/utils/ + tests/unit/commands/test_perf_cli.py
baseline unchanged (29 fails, matches pre-change 27 + 2 pre-existing utils casing).
Iter-14 commit 3b3aaa8 switched the auto-resolution path from
resolve_check_device_ep to resolve_device but left the surrounding
comment referring to the old contract (available_eps[0], returned-EP
semantics). Rewrite the comment to describe the current behavior: only
device is resolved here; EP is derived by downstream resolve_precision.
The '/'-joined device-string form was a back-compat shim for the pre-refactor
public contract. Grep confirms 0 in-src consumers; the one test that pinned
this area (test_ep_utils_imports.py) exercises get_devices_with_rule_data
directly and does not depend on get_ep_device_map.

Removes the shim dict + its public accessor + the __init__.py re-export.
sysinfo tests all green.
commands/analyze.py:40 imported DEVICE_TYPE_TO_DEVICE from utils.constants
which was deleted in 33f7a46. Session module has the authoritative
lowercase-keyed version; switch the import.

Fixes collection error 'cannot import name DEVICE_TYPE_TO_DEVICE from
winml.modelkit.utils.constants' in test_static_analyzer_cli.py.
…arget

Two follow-ups to the cleanup batch:

1. sysinfo/device.py — switch from 'from ..session.ep_device import ...'
   to 'from ..session import ...' so the architecture fitness rule
   (test_no_direct_ep_device_imports_in_src) stays green. The facade
   re-exports the same symbol; only the import path differs.

2. test_build.py test_resolve_device_value_error_surfaces_as_usage_error
   was patching sysinfo.resolve_check_device_ep, but 3b3aaa8 swapped
   build.py to call sysinfo.resolve_device. The test was silently
   passing before (patch had no effect on the code path), then broke
   when other cleanup shifted enough surrounding state to expose it.
   Update the patched symbol to match current code.
…PIs everywhere

Per user directive: 'the old resolve_device impl MUST be fully removed,
only use the new impl from our feature branch. All stuffs in session/
module must be adopted.'

## What's gone from sysinfo/device.py
- resolve_device(device, *, ep) → tuple[str, list[str]]
- resolve_check_device_ep(*, device, ep) → tuple[str, list[str], list[EPName]]
- resolve_eps(resolved_device) → list[EPName]
- get_device_ep_map() → dict[str, list[EPName]]
- The whole /-joined-string back-compat shim was already gone in 28d2042

## What's now in session/ep_device.py
- New helper: available_eps_for_device(device) → list[str]
  Returns registered EPs for a device in catalog priority order —
  same shape sysinfo.resolve_eps provided, layered on
  WinMLEPRegistry.available_eps() (the ORT-registration filter).

## Callers migrated (13 sites across 6 files)
- commands/build.py:617,1265 — resolve_device / resolve_check_device_ep
  → session.resolve_device(EPDeviceTarget(...)).device
- commands/analyze.py:919,940,956 — resolve_device / resolve_eps
  → session.resolve_device / available_eps_for_device
- commands/compile.py:58 — resolve_eps → available_eps_for_device
- commands/perf.py:1321,1327 — resolve_device / resolve_eps →
  session.resolve_device / available_eps_for_device
- commands/_perf_genai.py:100-101 — same
- eval/mask_generation_evaluator.py:678 — resolve_eps → available_eps_for_device
- config/precision.py — re-export now aliases available_eps_for_device
  as resolve_eps (kept for test patch compatibility)

## Test updates
- tests/unit/commands/test_compile_quantize_flags.py: patch target
  commands.compile.resolve_eps → commands.compile.available_eps_for_device
- tests/unit/commands/test_config_value_priority.py: same rename
- tests/unit/compiler/conftest.py: same rename

sysinfo/device.py now contains only ORT-runtime discovery internals
(_get_device_ep_map_from_ort, _get_available_devices, _get_available_eps
+ their support constants), matching the original design intent that
sysinfo is the ORT-registration layer and session is the taxonomy layer.
Follow-up to 98e50af (removed old sysinfo resolvers). Tests were still
patching winml.modelkit.sysinfo.{resolve_device,resolve_eps,resolve_check_device_ep}
which are now gone — AttributeError on patch() with create=False.

Bulk migration across 7 test files:
- winml.modelkit.sysinfo.resolve_device → winml.modelkit.session.resolve_device
- winml.modelkit.sysinfo.resolve_eps → winml.modelkit.session.available_eps_for_device
- winml.modelkit.sysinfo.resolve_check_device_ep → winml.modelkit.session.resolve_device
- return_value=(<device>, [<devices>]) tuples → return_value=EPDeviceTarget(...)
- Added EPDeviceTarget imports where needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants