Skip to content

feat: MVP v2 — CLI standardization, lazy imports, Rich console redesign#335

Merged
tezheng merged 1 commit into
mainfrom
feat/mvp
Apr 14, 2026
Merged

feat: MVP v2 — CLI standardization, lazy imports, Rich console redesign#335
tezheng merged 1 commit into
mainfrom
feat/mvp

Conversation

@tezheng

@tezheng tezheng commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Port of mvp/main2 feature branch with improvements:

  • Lazy imports: _LAZY_IMPORTS dict pattern across 7 modules, CLI starts in 0.13s with zero heavy deps
  • LazyGroup CLI: Command discovery via filesystem scan + AST help parsing
  • Rich Live analyze: Real-time per-node stacked bar animation
  • Rich config output: Formatted CONFIG GENERATION header with I/O specs
  • Build StageLive: Per-stage progress with EP analyzer bars
  • Inspect redesign: Rich formatted model inspection with _inspect_model_v2
  • Perf ONNX direct path: Bypasses HF build for .onnx files via WinMLSession
  • Config registry short-circuit: Skips Optimum when input_tensors pre-registered
  • Circular import fix (onnx <-> compiler) via lazy detection module
  • Import cleanup: all commands use __init__.py public APIs
  • README rewrite with workflow comparison table

Verification

  • 2513 unit tests pass (28 skipped for hardware, 2 xfailed for upstream bugs)
  • All 12 CLI commands load without heavy imports
  • winml analyze, winml config, winml build verified with ConvNeXt model
  • Zero wmk references, zero legacy SupportLevel values
  • Ruff lint + format clean, CodeQL clean

@tezheng tezheng requested a review from a team as a code owner April 14, 2026 04:32
Comment thread src/winml/modelkit/build/__init__.py Fixed
Comment thread src/winml/modelkit/build/__init__.py Fixed
Comment thread src/winml/modelkit/optim/__init__.py Fixed
Comment thread src/winml/modelkit/export/io.py Fixed
Comment thread src/winml/modelkit/utils/console.py Fixed
Comment thread src/winml/modelkit/utils/console.py Fixed
@tezheng tezheng force-pushed the feat/mvp branch 2 times, most recently from 055f892 to 0681cd6 Compare April 14, 2026 07:02
Cherry-picked from feat/mvp-v2 (11 commits squashed):
- Lazy imports: _LAZY_IMPORTS dict pattern across 7 modules, 0.13s CLI startup
- LazyGroup CLI: filesystem scan + AST help parsing, no module imports
- Rich Live analyze: per-node animated stacked bars with callbacks
- Rich config output: CONFIG GENERATION header with I/O specs
- Build StageLive: per-stage progress with EP analyzer bars
- Inspect redesign: Rich formatted _inspect_model_v2
- Perf ONNX direct path: bypasses HF build for .onnx files
- Config registry short-circuit: skips Optimum when input_tensors registered
- Circular import fix (onnx <-> compiler) via lazy detection
- CodeQL alerts resolved, test adaptations, README rewrite
@tezheng tezheng merged commit d79e53d into main Apr 14, 2026
9 checks passed
@tezheng tezheng deleted the feat/mvp branch April 14, 2026 07:54
DingmaomaoBJTU added a commit that referenced this pull request Apr 29, 2026
## Summary

`winml --help` startup regressed from 0.13s → 6.1s in MVP v2 (#335).
`src/winml/modelkit/_warnings.py` was eagerly importing `torch.jit` at
module load via a `try:/except ImportError:` guard that was unreachable
(torch is a hard dependency in `pyproject.toml`), dragging ~1.7s of
torch into every CLI invocation.

This PR removes the eager import. Build-time `TracerWarning` suppression
is unaffected — `commands/build.py:1060-1061` already wraps
`export_onnx()` in a blanket `warnings.catch_warnings()` +
`filterwarnings("ignore")` that's strictly broader than the deleted
filter.

## Why CI didn't catch this

`tests/test_import_time.py` was a comprehensive regression test added in
MVP v2 — but it lived at `tests/` root, outside every enumerated path in
`.github/workflows/modelkit-ci.yml`'s test matrix. CI never ran it. The
37 failing tests it contained were invisible.

This PR moves it to `tests/cli/test_import_time.py` (a new top-level
CLI-surface test category), adds `tests/cli` to the CI matrix, and
updates `tests/CLAUDE.md` to require CI matrix sync for any new
top-level test directory.

## Verification

```text
$ uv run pytest tests/cli/ tests/unit/sysinfo/ tests/unit/commands/ --no-cov -q
526 passed in 159s

$ time uv run winml --help    # warm
real    0m0.365s

$ python -X importtime -m winml.modelkit --help → torch loaded: NO
```

**Bug-restore experiment** (proving the test is diagnostic):
- With fix applied: 91/91 `tests/cli/` pass
- With `_warnings.py` reverted to buggy state: 34 fail with `FAIL:
unexpected heavy modules: ['torch', 'torchgen']`
- After reverting back: 91/91 pass again

## Changes (10 files)

| File | Change |
|---|---|
| `src/winml/modelkit/_warnings.py` | Delete `try: from torch.jit import
TracerWarning` block (the root cause) |
| `src/winml/modelkit/onnx/__init__.py` | Standardize on `_LAZY_IMPORTS`
dict pattern (matches 6 other subpackages) |
| `src/winml/modelkit/sysinfo/device.py` | Add `@lru_cache(maxsize=1)`
to `_get_available_devices`; return `tuple[str, ...]` for cache safety |
| `.github/workflows/modelkit-ci.yml` | Add `tests/cli` to `remaining`
matrix group |
| `tests/CLAUDE.md` | Document `tests/cli/` category + CI-matrix-sync
rule; clarify "module dirs" rule |
| `tests/cli/__init__.py` | Standard pkg marker (matches sibling test
category dirs) |
| `tests/test_import_time.py` → `tests/cli/test_import_time.py` | Move +
fix path resolution + fix tuple unpacking in
`test_lazy_imports_all_resolvable` (was 0% coverage); remove
`TestCommandWithModel` (out-of-scope feature pipeline tests) |
| `tests/unit/commands/test_cli.py` → `tests/cli/test_main.py` | Pure
rename (verbatim move) |
| `tests/unit/sysinfo/conftest.py` | Autouse fixture: clear `lru_cache`
on device probes between tests |
| `tests/unit/sysinfo/test_device.py` | Update assertions for the new
`tuple[str, ...]` return type |

## Known caveat

`winml export` (direct CLI path, not via `winml build`) and
`winml.modelkit.build.hf:216` emit `TracerWarning` noise during ONNX
export. Functional behavior unchanged — UX-only regression. Optional
follow-up: wrap those call sites in `warnings.catch_warnings()`, or move
the wrapper inside `export_pytorch()` itself for symmetric coverage. Not
blocking this PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Zheng Te <te.zheng@microsoft.com>
Co-authored-by: Qiong Wu (qiowu) <qiowu@microsoft.com>
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.

2 participants