Skip to content

Bind args to the ModelOpt builder in the inference entry points - #8728

Closed
Connor-XY wants to merge 5 commits into
NVIDIA:mainfrom
Connor-XY:yx/pstate-d7-modelopt-builder-args
Closed

Connor-XY wants to merge 5 commits into
NVIDIA:mainfrom
Connor-XY:yx/pstate-d7-modelopt-builder-args

Conversation

@Connor-XY

@Connor-XY Connor-XY commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Why. With --modelopt-enabled, get_model_for_inference() in megatron/inference/utils.py and tools/run_text_generation_server.py pass modelopt_gpt_hybrid_builder straight to get_model(). The builder takes args as its first positional parameter, but get_model() calls its provider with keyword arguments only. Every ModelOpt run through these entry points fails with TypeError: modelopt_gpt_hybrid_builder() missing 1 required positional argument: 'args'. That includes tools/run_dynamic_text_generation_server.py, which reaches get_model_for_inference() through get_dynamic_inference_engine(). The regression came with #5169, which replaced partial(model_provider, builder) with the bare builder.

What.

  • Bind args with functools.partial at both call sites. That is what model_provider() did for this builder, and what examples/post_training/modelopt still do.
  • Drop the claim in the call-site comments that get_model() runs the ModelOpt checkpoint auto-detection. Both entry points take this branch only when --modelopt-enabled is already set, and maybe_enable_modelopt() returns at once in that case.

Tests. New tests/unit_tests/post_training/test_modelopt_inference_builder.py drives both entry points with a stand-in builder that has the real builder's signature and a stand-in get_model() that, like the real one, calls the provider with keyword arguments only and replaces a None collection with its own groups. The builder must receive the same args object and exactly the groups get_model() built with. A third test compares the stand-in's signature with the real builder where ModelOpt is installed. In a CPU run before the 2026-11-01 sync, the two binding tests fail on main with the TypeError above and pass here. The rest of tests/unit_tests/post_training gives the same results on both. The signature test skipped in that CPU run because ModelOpt is not installed there. In the integration run on 4 GPUs (main 77391e7 with all #6307 PRs merged), all 3 tests in the new file pass and none skip; main has no such file. In the integration run, this PR's test files show no regressions against main. No end-to-end run with --modelopt-enabled was done.

On main after #8391, the first commit's test fails without the third commit, so please squash on merge.

Compatibility. No API change and no new warnings. Not fixed here: since #5169 these entry points no longer pick the ModelOpt builder on their own for a ModelOpt checkpoint served without --modelopt-enabled. That behavior change needs its own PR.

Synced with main 77391e7 on 2026-11-01 by a merge commit with no conflicts, so existing commits and review links stay valid.

Part of #6307.

🤖 Generated with Claude Code

Connor-XY and others added 4 commits October 28, 2026 10:18
get_model_for_inference() and tools/run_text_generation_server.py pass
modelopt_gpt_hybrid_builder straight to get_model(). The builder takes
args as its first positional parameter, but get_model() calls its
provider with keyword arguments only (pre_process, post_process,
config, pg_collection), so with ModelOpt enabled both entry points
raise "missing 1 required positional argument: 'args'" before any
model is built. The regression came in with NVIDIA#5169, which replaced
partial(model_provider, builder) by the bare builder.

Bind args with functools.partial at both call sites, which is what
model_provider() did for this builder before. Add CPU tests that drive
both entry points with a stand-in builder of the same signature and a
stand-in get_model that calls the provider the way get_model() does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
The comments above the ModelOpt get_model() calls in
get_model_for_inference() and tools/run_text_generation_server.py say
that get_model() runs the ModelOpt-checkpoint auto-detection. It does
nothing there: both entry points take this branch only when
args.modelopt_enabled is already set, and maybe_enable_modelopt()
returns at once in that case. Remove the claim and fold the note about
binding args into the remaining comment.

Since NVIDIA#5169 moved the builder choice ahead of get_model(), these entry
points also no longer pick the ModelOpt builder for a ModelOpt
checkpoint served without --modelopt-enabled. Restoring that is a
separate behaviour change and is left to its own fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
On current main (NVIDIA#8391), get_model_for_inference() reads
get_run_config() before it picks a builder, so the first test fails
with "run config is not initialized" before it reaches the ModelOpt
branch. Use the run_config fixture, which sets a run config for the
test and exists on this base as well.

Both tests also asserted that the builder receives pg_collection=None.
Once the text generation server passes the registered process groups
to get_model(), that holds only while nothing is registered. Record
the groups the get_model() stand-in receives and check that the
builder gets exactly those.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
The get_model() stand-in passed pg_collection=None straight through,
while the real get_model() swaps None for the global groups first. On
main, a wrapper that dropped pg_collection would therefore still pass.
The stand-in now swaps None for a distinct object, so the builder must
receive exactly the groups get_model() built with.

The tests compared the bound args by value, which a copy of the
Namespace also satisfies; they now also check identity. The stand-in
builder copies the real builder's signature by hand, so add a test
that compares the two where ModelOpt is installed and the real module
imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026
The inference entry points that this branch makes pass the registered
collection to get_model() also call the ModelOpt builder, which needs
args bound because get_model() calls the provider with keyword
arguments only. Merge that change so both land together.

In megatron/inference/utils.py and tools/run_text_generation_server.py
the ModelOpt path keeps the merged change's comment and calls
get_model() with partial(modelopt_gpt_hybrid_builder, args),
wrap_with_ddp=False and the registered pg_collection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
@Connor-XY

Copy link
Copy Markdown
Contributor Author

Superseded by #8896, which carries this change as commit 9ee2dff under the same title, with "Originally #8728." in its message. Closing so review happens in one place. The branch stays on the fork.

@Connor-XY Connor-XY closed this Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant