Skip to content

Commit e0a20e4

Browse files
oyilmaz-nvidiako3n1gclaudesvcnemo-autobotnemo-autobot-origin[bot]
authored
Addressing new inference updates in mcore (#706)
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com> Signed-off-by: oliver könig <okoenig@nvidia.com> Co-authored-by: oliver könig <okoenig@nvidia.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: svcnemo-autobot <svcnemo-autobot@nvidia.com> Co-authored-by: nemo-autobot-origin[bot] <297562586+nemo-autobot-origin[bot]@users.noreply.github.com>
1 parent 6fd587c commit e0a20e4

10 files changed

Lines changed: 265 additions & 556 deletions

File tree

nemo_deploy/deploy_ray.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from nemo_deploy.multimodal.megatron_multimodal_deployable_ray import MegatronMultimodalRayDeployable
3131

3232
HAVE_RAY = True
33-
except (ImportError, ModuleNotFoundError):
33+
except (ImportError, ModuleNotFoundError, AssertionError):
3434
from unittest.mock import MagicMock
3535

3636
ray = MagicMock()

nemo_deploy/llm/inference/inference_base.py

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,8 @@
2727
get_default_load_sharded_strategy,
2828
)
2929
from megatron.core.dist_checkpointing.validation import StrictHandling
30-
from megatron.core.inference.contexts.static_context import StaticInferenceContext
31-
from megatron.core.inference.engines.mcore_engine import MCoreEngine
32-
from megatron.core.inference.model_inference_wrappers.gpt.gpt_inference_wrapper import (
33-
GPTInferenceWrapper,
34-
)
35-
from megatron.core.inference.text_generation_controllers.text_generation_controller import (
36-
TextGenerationController,
37-
)
30+
from megatron.core.inference.apis import MegatronLLM
31+
from megatron.core.inference.config import InferenceConfig
3832
from megatron.core.transformer.enums import AttnBackend
3933
from megatron.core.transformer.module import MegatronModule
4034
from megatron.core.transformer.transformer_config import MLATransformerConfig
@@ -390,27 +384,24 @@ def setup_model_and_tokenizer_for_inference(
390384

391385

392386
class MCoreEngineWithCleanup:
393-
"""Wrapper around MCoreEngine that ensures proper cleanup of distributed resources.
387+
"""Wrapper around MegatronLLM that ensures proper cleanup of distributed resources.
394388
395-
This class delegates all operations to the underlying MCoreEngine while ensuring that
396-
distributed resources are properly cleaned up when the engine is destroyed.
389+
This class delegates all operations to the underlying MegatronLLM engine while ensuring
390+
that distributed resources are properly cleaned up when the engine is destroyed.
397391
"""
398392

399393
def __init__(
400394
self,
401-
mcore_engine: MCoreEngine,
402-
model_inference_wrapper: GPTInferenceWrapper,
395+
llm: MegatronLLM,
403396
tokenizer: Union[MCoreTokenizerWrappper, MegatronTokenizer],
404397
):
405398
"""Initialize the MCoreEngineWithCleanup.
406399
407400
Args:
408-
mcore_engine (MCoreEngine): The underlying MCoreEngine instance
409-
model_inference_wrapper (GPTInferenceWrapper): The model inference wrapper
401+
llm (MegatronLLM): The underlying MegatronLLM instance
410402
tokenizer (Union[MCoreTokenizerWrappper, MegatronTokenizer]): The tokenizer instance
411403
"""
412-
self.mcore_engine = mcore_engine
413-
self.model_inference_wrapper = model_inference_wrapper
404+
self.mcore_engine = llm
414405
self.tokenizer = tokenizer
415406

416407
def __del__(self):
@@ -446,16 +437,16 @@ def create_mcore_engine(
446437
buffer_size_gb: float = 10.0,
447438
legacy_model_format: bool = False,
448439
**model_config_kwargs,
449-
) -> Tuple[MCoreEngineWithCleanup, GPTInferenceWrapper, Union[MCoreTokenizerWrappper, MegatronTokenizer]]:
450-
"""Set up the model, tokenizer and MCoreEngine for inference.
440+
) -> Tuple[MCoreEngineWithCleanup, Union[MCoreTokenizerWrappper, MegatronTokenizer]]:
441+
"""Set up the model, tokenizer and MegatronLLM engine for inference.
451442
452443
Args:
453444
path (Path): Path to the checkpoint file
454445
params_dtype (torch.dtype): Data type for model parameters (default: torch.bfloat16)
455446
inference_batch_times_seqlen_threshold (int): Threshold for batch size times sequence length
456447
inference_max_seq_length (int): Maximum sequence length for inference
457448
max_batch_size (int): Maximum batch size for inference
458-
random_seed (Optional[int]): Random seed for reproducibility
449+
random_seed (Optional[int]): Random seed for reproducibility (set globally during init)
459450
tensor_model_parallel_size (Optional[int]): Size of tensor model parallelism
460451
pipeline_model_parallel_size (Optional[int]): Size of pipeline model parallelism
461452
context_parallel_size (Optional[int]): Size of context parallelism
@@ -466,11 +457,10 @@ def create_mcore_engine(
466457
model_type (str): Type of model to load (default: "gpt")
467458
model_format (str): Format of model to load (default: "nemo")
468459
micro_batch_size (Optional[int]): Micro batch size for model execution
469-
legacy_model_format (bool): Whether to use the legacy StaticInferenceEngine path in MCoreEngine (default: False)
460+
legacy_model_format (bool): Deprecated; no longer used (DynamicInferenceEngine is always used)
470461
Returns:
471-
Tuple[MCoreEngineWithCleanup, GPTInferenceWrapper, Union[MCoreTokenizerWrappper, MegatronTokenizer]]: Tuple containing:
462+
Tuple[MCoreEngineWithCleanup, Union[MCoreTokenizerWrappper, MegatronTokenizer]]: Tuple containing:
472463
- MCoreEngineWithCleanup: Engine for text generation with proper cleanup
473-
- GPTInferenceWrapper: Inference-wrapped model
474464
- Union[MCoreTokenizerWrappper, MegatronTokenizer]: Tokenizer instance
475465
"""
476466
# Default to 1 for any parallelism dimension that's None
@@ -512,34 +502,32 @@ def create_mcore_engine(
512502
else:
513503
raise ValueError(f"Model format {model_format} not supported.")
514504

515-
# MLA models require block_size_tokens=64 for the dynamic engine, which is not
516-
# configurable in the current Megatron-LM version. Fall back to the legacy static
517-
# engine so MLA inference works correctly without touching Megatron-LM.
505+
model.eval()
506+
507+
# MLA models require block_size_tokens=64 for correct KV cache operation with the
508+
# dynamic inference engine. Set the attention backend to flash if not already set.
509+
block_size_tokens = 256
518510
model_config = getattr(model, "config", None)
519511
if isinstance(model_config, MLATransformerConfig):
520-
legacy_model_format = True
521-
# The legacy static engine requires an explicit attention backend.
522-
# MLA models use flash attention (attention_mask is handled internally).
512+
block_size_tokens = 64
523513
if not model_config.attention_backend:
524514
model_config.attention_backend = AttnBackend.flash
525515

526-
inference_context = StaticInferenceContext(
527-
max_batch_size=max_batch_size,
516+
inference_config = InferenceConfig(
528517
max_sequence_length=inference_max_seq_length,
518+
buffer_size_gb=int(buffer_size_gb),
519+
max_requests=max_batch_size,
520+
block_size_tokens=block_size_tokens,
521+
materialize_only_last_token_logits=True,
529522
)
530-
model_inference_wrapper = GPTInferenceWrapper(model, inference_context)
531-
text_generation_controller = TextGenerationController(
532-
inference_wrapped_model=model_inference_wrapper, tokenizer=tokenizer
533-
)
534-
mcore_engine = MCoreEngine(
535-
text_generation_controller=text_generation_controller,
536-
max_batch_size=max_batch_size,
537-
random_seed=random_seed,
538-
buffer_size_gb=buffer_size_gb,
539-
legacy=legacy_model_format,
523+
524+
llm = MegatronLLM(
525+
model=model,
526+
tokenizer=tokenizer,
527+
inference_config=inference_config,
540528
)
541529

542530
# Wrap the engine to ensure cleanup
543-
wrapped_engine = MCoreEngineWithCleanup(mcore_engine, model_inference_wrapper, tokenizer)
531+
wrapped_engine = MCoreEngineWithCleanup(llm, tokenizer)
544532

545-
return wrapped_engine, model_inference_wrapper, tokenizer
533+
return wrapped_engine, tokenizer

nemo_deploy/llm/megatronllm_deployable.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import torch
2222
import torch.distributed
2323
from jinja2 import Template
24-
from megatron.core.inference.common_inference_params import CommonInferenceParams
25-
from megatron.core.inference.inference_request import InferenceRequest
24+
from megatron.core.inference.inference_request import DynamicInferenceRequest
25+
from megatron.core.inference.sampling_params import SamplingParams
2626

2727
from nemo_deploy import ITritonDeployable
2828
from nemo_deploy.llm.inference.inference_base import create_mcore_engine
@@ -113,7 +113,7 @@ def __init__(
113113
if model_type not in ["gpt", "mamba"]:
114114
raise ValueError(f"Model type {model_type} not supported for Megatron models.")
115115

116-
self.mcore_engine, self.inference_wrapped_model, self.mcore_tokenizer = create_mcore_engine(
116+
self.mcore_engine, self.mcore_tokenizer = create_mcore_engine(
117117
num_devices=num_devices,
118118
num_nodes=num_nodes,
119119
path=Path(megatron_checkpoint_filepath),
@@ -144,18 +144,18 @@ def __init__(
144144
def generate(
145145
self,
146146
prompts: List[str],
147-
inference_params: Optional[CommonInferenceParams] = None,
148-
) -> List[InferenceRequest]:
147+
inference_params: Optional[SamplingParams] = None,
148+
) -> List[DynamicInferenceRequest]:
149149
"""Generates text based on the provided input prompts.
150150
151151
Args:
152152
prompts (List[str]): A list of input strings.
153-
inference_params (Optional[CommonInferenceParams]): Parameters for controlling the inference process.
153+
inference_params (Optional[SamplingParams]): Parameters for controlling the inference process.
154154
155155
Returns:
156-
List[InferenceRequest]: A list containing the generated results.
156+
List[DynamicInferenceRequest]: A list containing the generated results.
157157
"""
158-
inference_params = inference_params or CommonInferenceParams()
158+
inference_params = inference_params or SamplingParams()
159159

160160
# Store the original number of prompts
161161
orig_num_prompts = len(prompts)
@@ -173,17 +173,15 @@ def generate(
173173

174174
results = self.mcore_engine.generate(
175175
prompts=padded_prompts,
176-
add_BOS=False,
177-
common_inference_params=inference_params,
176+
sampling_params=inference_params,
178177
)
179178

180179
# Only return results for the original prompts
181180
return list(results)[:orig_num_prompts]
182181
else:
183182
results = self.mcore_engine.generate(
184183
prompts=prompts,
185-
add_BOS=False,
186-
common_inference_params=inference_params,
184+
sampling_params=inference_params,
187185
)
188186
return list(results)
189187

@@ -198,7 +196,7 @@ def generate_other_ranks(self):
198196
data=[None], src=0
199197
)
200198

201-
inference_params = CommonInferenceParams(
199+
inference_params = SamplingParams(
202200
temperature=temperature,
203201
top_k=int(top_k),
204202
top_p=float(top_p),
@@ -208,7 +206,7 @@ def generate_other_ranks(self):
208206
)
209207

210208
if log_probs:
211-
dynamic_engine = getattr(self.mcore_engine, "dynamic_engine", None)
209+
dynamic_engine = getattr(self.mcore_engine, "engine", None)
212210
if dynamic_engine is not None:
213211
dynamic_engine.materialize_only_last_token_logits = False
214212
dynamic_engine.context.config.materialize_only_last_token_logits = False
@@ -419,15 +417,15 @@ def _infer_fn(
419417
)
420418

421419
# cast top_k,top_p to native int, float since typecheck assert statements added in MCore0.13 error otherwise
422-
# return_prompt_top_n_logprobs returns top_logprobs for prompt tokens too when top_logprobs>0.
423-
inference_params = CommonInferenceParams(
420+
# skip_prompt_log_probs=False (default) includes prompt tokens in top-N logprobs when top_logprobs>0.
421+
inference_params = SamplingParams(
424422
temperature=temperature,
425423
top_k=int(top_k),
426424
top_p=float(top_p),
427425
num_tokens_to_generate=num_tokens_to_generate,
428426
return_log_probs=log_probs,
429427
top_n_logprobs=top_logprobs,
430-
return_prompt_top_n_logprobs=bool(top_logprobs),
428+
skip_prompt_log_probs=not bool(top_logprobs),
431429
stop_words=stop_words,
432430
)
433431

@@ -436,7 +434,7 @@ def _infer_fn(
436434
# (prompt log probs are required for logprob eval benchmarks).
437435
# Toggle it on both the engine and the context config (controls the
438436
# model forward pass and log prob calculations).
439-
dynamic_engine = getattr(self.mcore_engine, "dynamic_engine", None)
437+
dynamic_engine = getattr(self.mcore_engine, "engine", None)
440438
needs_all_logits = log_probs or bool(top_logprobs)
441439
if dynamic_engine is not None and needs_all_logits:
442440
dynamic_engine.materialize_only_last_token_logits = False

pyproject.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,11 @@ vllm = [
110110
{ index = "pytorch-cu130", marker = "python_version < '3.9' and platform_machine == 'x86_64'" },
111111
{ index = "pypi", marker = "platform_machine == 'aarch64'" },
112112
]
113-
megatron-bridge = { git = "https://github.com/NVIDIA-NeMo/Megatron-Bridge.git", rev = "859a18d001eca4423ad79e7771632d839f50fa77" }
114-
nvidia-resiliency-ext = { git = "https://github.com/NVIDIA/nvidia-resiliency-ext.git", rev = "b2bb3d728a18795807d9f76c535e005a609a1b01" }
113+
megatron-bridge = { git = "https://github.com/NVIDIA-NeMo/Megatron-Bridge.git", rev = "97e8dba4060ae74a110b9da6255eb0a66b016b2d" }
114+
# Use the released nvidia-resiliency-ext (>=0.6.0) rather than a git dev build:
115+
# megatron-core's runtime is_nvrx_min_version() asserts the installed package
116+
# version is >=0.6.0, and a git build reports 0.6.0.devN (< 0.6.0), which fails
117+
# the assertion at import time.
115118
# nemo-toolkit = { git = "https://github.com/NVIDIA/NeMo.git", rev = "main" }
116119

117120
[tool.uv]
@@ -123,7 +126,9 @@ no-build-isolation-package = [
123126
"mamba-ssm",
124127
"causal-conv1d",
125128
"nv-grouped-gemm",
129+
"fast-hadamard-transform",
126130
]
131+
127132
# Always apply the build group since dependencies like TE/mcore/nemo-run require build dependencies
128133
# and this lets us assume they are implicitly installed with a simply `uv sync`. Ideally, we'd
129134
# avoid including these in the default dependency set, but for now it's required.
@@ -149,7 +154,7 @@ override-dependencies = [
149154
"datasets>=3.3.0",
150155
"flash-linear-attention>=0.3.0,<0.4.dev0",
151156
"patchelf; sys_platform=='never'",
152-
"nvidia-resiliency-ext>=0.3.0,<0.6.0",
157+
"nvidia-resiliency-ext>=0.6.0",
153158
"transformer-engine[pytorch,core_cu13]>=2.14.0a0,<2.17.0; sys_platform != 'darwin'",
154159
"transformer-engine-cu13>=2.14.0a0,<2.17.0; sys_platform != 'darwin'",
155160
"transformer-engine-cu12; sys_platform == 'never'",
@@ -162,6 +167,10 @@ override-dependencies = [
162167
]
163168
prerelease = "allow"
164169

170+
[[tool.uv.dependency-metadata]]
171+
name = "nvidia-resiliency-ext"
172+
version = "0.6.0"
173+
165174
[[tool.uv.index]]
166175
name = "pypi"
167176
url = "https://pypi.org/simple"

tests/functional_tests/utils/run_nemo_export.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535

3636
in_framework_supported = True
3737
try:
38-
from megatron.core.inference.common_inference_params import CommonInferenceParams
38+
from megatron.core.inference.sampling_params import SamplingParams
3939

4040
from nemo_deploy.llm import NemoQueryLLMPyTorch
4141
from nemo_deploy.llm.megatronllm_deployable import MegatronLLMDeployable
4242
except Exception as e:
4343
LOGGER.warning(
44-
"Cannot import MegatronLLMDeployable class, or NemoQueryLLMPyTorch, or CommonInferenceParams, "
44+
"Cannot import MegatronLLMDeployable class, or NemoQueryLLMPyTorch, or SamplingParams, "
4545
f"in-framework inference will not be available. Reason: {type(e).__name__}: {e}"
4646
)
4747
in_framework_supported = False
@@ -98,7 +98,7 @@ def get_accuracy_with_lambada(model, nq, lora_uids, test_data_path, use_vllm: bo
9898
if in_framework_supported and isinstance(model, MegatronLLMDeployable):
9999
model_output = model.generate(
100100
prompts=[prompt],
101-
inference_params=CommonInferenceParams(
101+
inference_params=SamplingParams(
102102
temperature=0.1,
103103
top_k=1,
104104
top_p=0.0,

0 commit comments

Comments
 (0)