Skip to content

Commit 7143ebf

Browse files
Addressing new inference updates in mcore
Signed-off-by: Onur Yilmaz <oyilmaz@nvidia.com>
1 parent da45b11 commit 7143ebf

7 files changed

Lines changed: 119 additions & 171 deletions

File tree

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

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,

tests/unit_tests/deploy/test_etp_sequence_parallel.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -389,19 +389,14 @@ class TestCreateMcoreEngineETPSequenceParallel(unittest.TestCase):
389389
"""Tests that create_mcore_engine handles ETP/SP defaults and passes them down."""
390390

391391
@patch("nemo_deploy.llm.inference.inference_base.setup_model_and_tokenizer_for_inference")
392-
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngine")
392+
@patch("nemo_deploy.llm.inference.inference_base.MegatronLLM")
393393
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngineWithCleanup")
394-
@patch("nemo_deploy.llm.inference.inference_base.GPTInferenceWrapper")
395-
@patch("nemo_deploy.llm.inference.inference_base.StaticInferenceContext")
396-
@patch("nemo_deploy.llm.inference.inference_base.TextGenerationController")
397-
def test_etp_defaults_to_1_when_none(
398-
self, mock_tgc, mock_ctx, mock_wrapper, mock_cleanup, mock_engine_cls, mock_setup
399-
):
394+
def test_etp_defaults_to_1_when_none(self, mock_cleanup, mock_llm_cls, mock_setup):
400395
"""expert_tensor_parallel_size=None is normalised to 1 before forwarding."""
401396
from nemo_deploy.llm.inference.inference_base import create_mcore_engine
402397

403398
mock_setup.return_value = ([MagicMock()], MagicMock())
404-
mock_engine_cls.return_value = MagicMock()
399+
mock_llm_cls.return_value = MagicMock()
405400
mock_cleanup.return_value = MagicMock()
406401

407402
create_mcore_engine(path=Path("/fake"), model_format="nemo", expert_tensor_parallel_size=None)
@@ -410,19 +405,14 @@ def test_etp_defaults_to_1_when_none(
410405
assert kwargs["expert_tensor_parallel_size"] == 1
411406

412407
@patch("nemo_deploy.llm.inference.inference_base.setup_model_and_tokenizer_for_inference")
413-
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngine")
408+
@patch("nemo_deploy.llm.inference.inference_base.MegatronLLM")
414409
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngineWithCleanup")
415-
@patch("nemo_deploy.llm.inference.inference_base.GPTInferenceWrapper")
416-
@patch("nemo_deploy.llm.inference.inference_base.StaticInferenceContext")
417-
@patch("nemo_deploy.llm.inference.inference_base.TextGenerationController")
418-
def test_sp_defaults_to_1_when_none(
419-
self, mock_tgc, mock_ctx, mock_wrapper, mock_cleanup, mock_engine_cls, mock_setup
420-
):
410+
def test_sp_defaults_to_1_when_none(self, mock_cleanup, mock_llm_cls, mock_setup):
421411
"""sequence_parallel=None is normalised to 1 before forwarding."""
422412
from nemo_deploy.llm.inference.inference_base import create_mcore_engine
423413

424414
mock_setup.return_value = ([MagicMock()], MagicMock())
425-
mock_engine_cls.return_value = MagicMock()
415+
mock_llm_cls.return_value = MagicMock()
426416
mock_cleanup.return_value = MagicMock()
427417

428418
create_mcore_engine(path=Path("/fake"), model_format="nemo", sequence_parallel=None)
@@ -431,19 +421,14 @@ def test_sp_defaults_to_1_when_none(
431421
assert kwargs["sequence_parallel"] == 1
432422

433423
@patch("nemo_deploy.llm.inference.inference_base.setup_model_and_tokenizer_for_inference")
434-
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngine")
424+
@patch("nemo_deploy.llm.inference.inference_base.MegatronLLM")
435425
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngineWithCleanup")
436-
@patch("nemo_deploy.llm.inference.inference_base.GPTInferenceWrapper")
437-
@patch("nemo_deploy.llm.inference.inference_base.StaticInferenceContext")
438-
@patch("nemo_deploy.llm.inference.inference_base.TextGenerationController")
439-
def test_explicit_etp_passed_through(
440-
self, mock_tgc, mock_ctx, mock_wrapper, mock_cleanup, mock_engine_cls, mock_setup
441-
):
426+
def test_explicit_etp_passed_through(self, mock_cleanup, mock_llm_cls, mock_setup):
442427
"""An explicit expert_tensor_parallel_size value is forwarded unchanged."""
443428
from nemo_deploy.llm.inference.inference_base import create_mcore_engine
444429

445430
mock_setup.return_value = ([MagicMock()], MagicMock())
446-
mock_engine_cls.return_value = MagicMock()
431+
mock_llm_cls.return_value = MagicMock()
447432
mock_cleanup.return_value = MagicMock()
448433

449434
create_mcore_engine(path=Path("/fake"), model_format="nemo", expert_tensor_parallel_size=4)
@@ -452,19 +437,14 @@ def test_explicit_etp_passed_through(
452437
assert kwargs["expert_tensor_parallel_size"] == 4
453438

454439
@patch("nemo_deploy.llm.inference.inference_base.setup_model_and_tokenizer_for_inference")
455-
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngine")
440+
@patch("nemo_deploy.llm.inference.inference_base.MegatronLLM")
456441
@patch("nemo_deploy.llm.inference.inference_base.MCoreEngineWithCleanup")
457-
@patch("nemo_deploy.llm.inference.inference_base.GPTInferenceWrapper")
458-
@patch("nemo_deploy.llm.inference.inference_base.StaticInferenceContext")
459-
@patch("nemo_deploy.llm.inference.inference_base.TextGenerationController")
460-
def test_explicit_sp_passed_through(
461-
self, mock_tgc, mock_ctx, mock_wrapper, mock_cleanup, mock_engine_cls, mock_setup
462-
):
442+
def test_explicit_sp_passed_through(self, mock_cleanup, mock_llm_cls, mock_setup):
463443
"""An explicit sequence_parallel=True value is forwarded unchanged."""
464444
from nemo_deploy.llm.inference.inference_base import create_mcore_engine
465445

466446
mock_setup.return_value = ([MagicMock()], MagicMock())
467-
mock_engine_cls.return_value = MagicMock()
447+
mock_llm_cls.return_value = MagicMock()
468448
mock_cleanup.return_value = MagicMock()
469449

470450
create_mcore_engine(path=Path("/fake"), model_format="nemo", sequence_parallel=True)
@@ -487,7 +467,7 @@ def test_expert_tensor_parallel_size_forwarded(self, mock_create):
487467
"""expert_tensor_parallel_size is forwarded to create_mcore_engine."""
488468
from nemo_deploy.llm.megatronllm_deployable import MegatronLLMDeployable
489469

490-
mock_create.return_value = (MagicMock(), MagicMock(), MagicMock())
470+
mock_create.return_value = (MagicMock(), MagicMock())
491471

492472
MegatronLLMDeployable(
493473
megatron_checkpoint_filepath="model.ckpt",
@@ -503,7 +483,7 @@ def test_sequence_parallel_forwarded(self, mock_create):
503483
"""sequence_parallel is forwarded to create_mcore_engine."""
504484
from nemo_deploy.llm.megatronllm_deployable import MegatronLLMDeployable
505485

506-
mock_create.return_value = (MagicMock(), MagicMock(), MagicMock())
486+
mock_create.return_value = (MagicMock(), MagicMock())
507487

508488
MegatronLLMDeployable(
509489
megatron_checkpoint_filepath="model.ckpt",
@@ -519,7 +499,7 @@ def test_defaults_etp_1_and_sp_false(self, mock_create):
519499
"""Defaults: expert_tensor_parallel_size=1, sequence_parallel=False."""
520500
from nemo_deploy.llm.megatronllm_deployable import MegatronLLMDeployable
521501

522-
mock_create.return_value = (MagicMock(), MagicMock(), MagicMock())
502+
mock_create.return_value = (MagicMock(), MagicMock())
523503

524504
MegatronLLMDeployable(megatron_checkpoint_filepath="model.ckpt")
525505

0 commit comments

Comments
 (0)