Skip to content

fix: apply_mode accepts ModeDescriptor instances and an empty mode list with a ModelLike#1918

Open
arham766 wants to merge 2 commits into
NVIDIA:mainfrom
arham766:fix/apply-mode-contract
Open

fix: apply_mode accepts ModeDescriptor instances and an empty mode list with a ModelLike#1918
arham766 wants to merge 2 commits into
NVIDIA:mainfrom
arham766:fix/apply-mode-contract

Conversation

@arham766

@arham766 arham766 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: Bug fix

Two API-contract bugs in the opt mode framework (found writing #1915): apply_mode(model, [descriptor_instance]) raised TypeError despite the docstring and the ModeType = ModeDescriptor | str annotation — descriptors are now normalized to their name up front (registry descriptors are per-name singletons, so resolution round-trips; get_mode_config's only caller is apply_mode, audited); and apply_mode((cls, args, kwargs), []) — the documented vanilla initialize+return case — crashed in init_modellike's unconditional state transfer, now guarded by is_converted so both vanilla paths consistently return a plain initialized model. Existing ModelLike-with-state coverage stays green; regression tests appended to test_chaining.py, both verified to fail pre-fix with the exact reported signatures.

Usage

N/A

Testing

Regression tests included, each verified to FAIL with the fix reverted. Combined battery with all sibling wave fixes: 381 passed across tests/unit/torch/opt, tests/unit/torch/utils, tests/unit/recipe, and quantization test_mode. Note: the unmerged suite PRs #1913-#1916 contain behavior-documenting NOTE tests pinning the OLD behavior fixed here — whichever lands second gets the NOTE tests flipped (happy to rebase either way).

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: N/A
  • Did you get Claude approval on this PR?: N/A (external contributor)

Additional Information

Issue: #1902 (wave-2 findings)

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of mode configuration inputs so descriptor-based and string-based modes behave consistently.
    • Fixed model initialization so prior optimization state is only restored when the model is actually recognized as converted.
    • Ensured applying an empty set of modes to a wrapped model returns a standard, forward-executable module.
  • Tests
    • Added coverage for mode descriptor equivalence and empty-mode behavior on wrapped models.

…st with a ModelLike

get_mode_config only handled str/tuple entries, so
apply_mode(model, [descriptor_instance]) raised TypeError - despite
the docstring and the ModeType = ModeDescriptor | str annotation.
Descriptors are now normalized to their name up front (registry
descriptors are per-name singletons, so name resolution returns the
same instance); str/tuple paths are byte-identical, and
get_mode_config's only caller is apply_mode.

apply_mode((cls, args, kwargs), []) - the documented 'vanilla case
(just initialize+return)' - crashed in init_modellike, which
unconditionally transferred never-initialized state. The transfer is
now guarded by is_converted, keeping the two vanilla paths consistent:
both return a plain initialized model without modelopt state. The
state-carrying ModelLike path is unchanged (the manager is constructed
before init_modellike runs in normal flows).

Part of the findings in NVIDIA#1902 (wave 2).

Signed-off-by: arham766 <arhamislam766@yahoo.com>
@arham766 arham766 requested review from a team as code owners July 6, 2026 01:49
@arham766 arham766 requested a review from realAsma July 6, 2026 01:49
@copy-pr-bot

copy-pr-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3ba30d67-3392-4e9f-9870-79ebc6a8e90c

📥 Commits

Reviewing files that changed from the base of the PR and between 8283d00 and 54b9a03.

📒 Files selected for processing (1)
  • modelopt/torch/opt/mode.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • modelopt/torch/opt/mode.py

📝 Walkthrough

Walkthrough

This PR makes init_modellike transfer state only for converted modules, extends get_mode_config to accept ModeDescriptor inputs, and adds unit tests covering descriptor-based mode application and empty-mode ModelLike initialization.

Changes

ModeDescriptor and ModelLike fixes

Layer / File(s) Summary
Conditional state transfer and ModeDescriptor handling
modelopt/torch/opt/conversion.py, modelopt/torch/opt/mode.py
init_modellike now transfers modelopt state only when ModeloptStateManager.is_converted returns true; get_mode_config adds explicit handling for ModeDescriptor inputs before string and tuple normalization.
Unit tests for new behavior
tests/unit/torch/opt/test_chaining.py
New imports of ModelLikeModule and _ModeRegistryCls support tests that compare string and ModeDescriptor application results and verify empty-mode ModelLike initialization returns a plain, usable module.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main fixes: ModeDescriptor support and empty mode-list handling for ModelLike.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed Changed Python files only adjust mode normalization and model-like init; no unsafe torch/numpy loads, trust_remote_code, eval/exec on external input, nosec, or new deps found.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
modelopt/torch/opt/mode.py (1)

357-370: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nested ternary is functionally correct but a bit dense.

Logic correctly standardizes ModeDescriptor/str/tuple inputs, matching the new descriptor-instance test. Consider an explicit if/elif/else for readability, though this is optional.

♻️ Optional readability refactor
 def get_mode_config(mode_like: ModeLike) -> ModeConfigList:
     """Standardize mode to ModeConfigDict and return."""
-    mode_and_config = [
-        (
-            (m.name, {})
-            if isinstance(m, ModeDescriptor)
-            else (m, {})
-            if isinstance(m, str)
-            else (m[0], m[1] or {})
-        )
-        for m in val2list(mode_like)
-    ]
-
-    return mode_and_config
+    def _to_pair(m):
+        if isinstance(m, ModeDescriptor):
+            return (m.name, {})
+        if isinstance(m, str):
+            return (m, {})
+        return (m[0], m[1] or {})
+
+    return [_to_pair(m) for m in val2list(mode_like)]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelopt/torch/opt/mode.py` around lines 357 - 370, The logic in
get_mode_config is correct, but the nested ternary used to normalize
ModeDescriptor, str, and tuple inputs is hard to read. Refactor the list
construction in get_mode_config into an explicit if/elif/else structure inside
the loop over val2list(mode_like), preserving the same output shape and default
{} handling for each input type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@modelopt/torch/opt/mode.py`:
- Around line 357-370: The logic in get_mode_config is correct, but the nested
ternary used to normalize ModeDescriptor, str, and tuple inputs is hard to read.
Refactor the list construction in get_mode_config into an explicit if/elif/else
structure inside the loop over val2list(mode_like), preserving the same output
shape and default {} handling for each input type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 51da449d-b75e-4d01-ab1f-2de1af577fd1

📥 Commits

Reviewing files that changed from the base of the PR and between b96a785 and 8283d00.

📒 Files selected for processing (3)
  • modelopt/torch/opt/conversion.py
  • modelopt/torch/opt/mode.py
  • tests/unit/torch/opt/test_chaining.py

Signed-off-by: arham766 <arhamislam766@yahoo.com>
@arham766 arham766 force-pushed the fix/apply-mode-contract branch from 9451fad to 54b9a03 Compare July 6, 2026 02:15
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.

1 participant