@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / f9582fd8f4
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 14, 05:30 UTC

[Model] Allow passing custom number of max tiles to Nano 2 VL (#26403)

Signed-off-by: Eugene Khvedchenia <ekhvedchenia@nvidia.com>

Eugene Khvedchenya committed Oct 8, 2025 at 14:19 UTC f9582fd8f4f0e03a1b0d5b959bbf15d297336775
1 file changed +19 -6
vllm/model_executor/models/nano_nemotron_vl.py
+19 -6
@@ -93,6 +93,7 @@ IMG_CONTEXT = "<image>"
93
94 # Profiling
95 MAX_FRAMES = 16
96 +DEFAULT_NUM_TILES = 12
97
98
99 class NanoNemotronVLImagePixelInputs(TypedDict):
@@ -227,6 +228,8 @@ def video_to_pixel_values(
228 max_num_tiles: int = 1,
229 use_thumbnail: bool,
230 ) -> torch.Tensor:
231 + assert max_num_tiles == 1, "Video modality always uses one tile"
232 +
233 # Convert each frame to a single resized tile tensor consistent
234 # with image path
235 frames_tensors: list[torch.Tensor] = []
@@ -255,13 +258,19 @@ class BaseNanoNemotronVLProcessor(ABC):
258 """
259
260 def __init__(
258 - self, config: PretrainedConfig, tokenizer: AnyTokenizer, *args, **kwargs
261 + self,
262 + config: PretrainedConfig,
263 + tokenizer: AnyTokenizer,
264 + *args,
265 + max_num_tiles: Optional[int] = None,
266 + **kwargs,
267 ) -> None:
268 super().__init__()
269
270 self.config = config
271 self.tokenizer = tokenizer
272
273 + self.max_num_tiles = max_num_tiles or DEFAULT_NUM_TILES
274 image_size: int = config.force_image_size
275 patch_size: int = config.patch_size
276
@@ -361,7 +370,7 @@ class BaseNanoNemotronVLProcessor(ABC):
370 ) -> BatchFeature:
371 # Use default if not provided
372 if max_num_tiles is None:
364 - max_num_tiles = 12
373 + max_num_tiles = self.max_num_tiles
374
375 text, images = [self._make_batch_input(x) for x in (text, images)]
376
@@ -390,6 +399,7 @@ class NanoNemotronVLProcessor(BaseNanoNemotronVLProcessor):
399 config: PretrainedConfig,
400 tokenizer: AnyTokenizer,
401 *,
402 + max_num_tiles: Optional[int] = None,
403 min_dynamic_patch: Optional[int] = None,
404 max_dynamic_patch: Optional[int] = None,
405 dynamic_image_size: Optional[bool] = None,
@@ -399,6 +409,7 @@ class NanoNemotronVLProcessor(BaseNanoNemotronVLProcessor):
409 super().__init__(
410 config=config,
411 tokenizer=tokenizer,
412 + max_num_tiles=max_num_tiles,
413 min_dynamic_patch=min_dynamic_patch,
414 max_dynamic_patch=max_dynamic_patch,
415 dynamic_image_size=dynamic_image_size,
@@ -506,7 +517,7 @@ class NanoNemotronVLProcessor(BaseNanoNemotronVLProcessor):
517 ) -> BatchFeature:
518 # Use default if not provided
519 if max_num_tiles is None:
509 - max_num_tiles = 12
520 + max_num_tiles = self.max_num_tiles
521
522 text, images, videos = [
523 self._make_batch_input(x) for x in (text, images, videos)
@@ -521,7 +532,7 @@ class NanoNemotronVLProcessor(BaseNanoNemotronVLProcessor):
532 text, video_inputs = self._preprocess_video(
533 text=text,
534 videos=videos,
524 - max_num_tiles=max_num_tiles,
535 + max_num_tiles=1,
536 dynamic_image_size=dynamic_image_size,
537 )
538
@@ -635,7 +646,7 @@ class BaseNanoNemotronVLProcessingInfo(BaseProcessingInfo):
646 def get_max_image_tokens(self) -> int:
647 processor = self.get_hf_processor()
648 # Use default max_num_tiles for max tokens calculation
638 - max_num_tiles = 12
649 + max_num_tiles = processor.max_num_tiles
650 target_width, target_height = self.get_image_size_with_most_features(
651 max_num_tiles
652 )
@@ -768,7 +779,9 @@ class NanoNemotronBaseVLMultiModalProcessor(BaseMultiModalProcessor[_I]):
779 else:
780 image_size = images.get_image_size(item_idx)
781 # Extract max_num_tiles from kwargs, default to 12
771 - max_num_tiles = hf_processor_mm_kwargs.get("max_num_tiles", 12)
782 + max_num_tiles = hf_processor_mm_kwargs.get(
783 + "max_num_tiles", hf_processor.max_num_tiles
784 + )
785 feature_size = self.info.get_num_image_tokens(
786 image_width=image_size.width,
787 image_height=image_size.height,