[Bugfix] Re-land MiniMax M3 default video processor (#50305)

Signed-off-by: Taneem Ibrahim <taneem.ibrahim@gmail.com> Signed-off-by: Isotr0py <Isotr0py@outlook.com> Co-authored-by: OpenAI Codex <codex@openai.com> Co-authored-by: Isotr0py <Isotr0py@outlook.com>

Taneem Ibrahim committed Jul 31, 2026 at 03:46 UTC f727951d3f0dbeb9acdb8a2f7ebfecaeb67090b3
3 files changed +42 -12
tests/multimodal/test_video.py
+7
@@ -14,6 +14,7 @@ from transformers import AutoVideoProcessor
14 from transformers.video_utils import VideoMetadata
15
16 from vllm.assets.base import get_vllm_public_assets
17 +from vllm.models.minimax_m3.common.mm_preprocess import MiniMaxM3VideoBackend
18 from vllm.multimodal.video import (
19 PYNVVIDEOCODEC_DECODER_CACHE_SIZE,
20 PYNVVIDEOCODEC_VIDEO_BACKEND,
@@ -388,6 +389,12 @@ def test_cosmos3_edge_uses_qwen3_vl_video_backend():
389 {"fps": 2},
390 id="qwen2_5_vl",
391 ),
392 + pytest.param(
393 + "MiniMaxAI/MiniMax-M3",
394 + MiniMaxM3VideoBackend,
395 + None,
396 + id="minimax_m3_vl",
397 + ),
398 ],
399 )
400 def test_video_processor_from_model_repo(
vllm/model_executor/layers/fused_allreduce_gemma_rms_norm.py
+1 -1
@@ -43,7 +43,7 @@ try:
43 if flashinfer_comm is not None
44 else None
45 )
46 -except ImportError:
46 +except (ImportError, AttributeError):
47 flashinfer_trtllm_fused_allreduce_norm = None # type: ignore[assignment]
48 get_fi_ar_workspace = None # type: ignore[assignment]
49 _AR_RESIDUAL_RMS_NORM = None
vllm/models/minimax_m3/common/mm_preprocess.py
+34 -11
@@ -3,8 +3,9 @@
3
4 import math
5 from collections.abc import Mapping, Sequence
6 -from typing import cast
6 +from typing import Any, Literal, cast
7
8 +import numpy.typing as npt
9 import torch
10 from transformers import BatchFeature
11 from transformers.video_utils import VideoMetadata
@@ -469,10 +470,39 @@ class MiniMaxM3VLMultiModalProcessor(
470 ]
471
472
472 -# TODO(Isotr0py): Tie with MinimaxVideoProcessor
473 -# after https://github.com/vllm-project/vllm/pull/44126
474 -@VIDEO_LOADER_REGISTRY.register("minimax_m3_vl")
473 +@VIDEO_LOADER_REGISTRY.register(
474 + name="minimax_m3_vl",
475 + video_processor="MiniMaxM3VLVideoProcessor",
476 +)
477 class MiniMaxM3VideoBackend(VideoBackend):
478 + @classmethod
479 + def load_bytes(
480 + cls,
481 + data: bytes,
482 + num_frames: int = -1,
483 + fps: int = 1,
484 + max_duration: int = 300,
485 + frame_recovery: bool = False,
486 + *,
487 + backend: Literal[
488 + "opencv",
489 + "pyav",
490 + "torchcodec",
491 + "pynvvideocodec",
492 + "deepstream",
493 + ] = "opencv",
494 + **kwargs,
495 + ) -> tuple[npt.NDArray, dict[str, Any]]:
496 + return super().load_bytes(
497 + data,
498 + num_frames=num_frames,
499 + fps=fps,
500 + max_duration=max_duration,
501 + frame_recovery=frame_recovery,
502 + backend=backend,
503 + **kwargs,
504 + )
505 +
506 @classmethod
507 def compute_frames_index_to_sample(
508 cls,
@@ -483,7 +513,6 @@ class MiniMaxM3VideoBackend(VideoBackend):
513 total_frames = source.total_frames_num
514 video_fps = source.original_fps
515 fps = target.fps
486 -
516 if total_frames <= 0 or video_fps <= 0 or fps <= 0:
517 return [0] if total_frames > 0 else []
518
@@ -503,12 +532,6 @@ class MiniMaxM3VideoBackend(VideoBackend):
532 break
533 indices.append(target_frame)
534 prev_kept_ts = target_frame / video_fps
506 -
507 - last_frame_idx = total_frames - 1
508 - last_ts = last_frame_idx / video_fps
509 - if indices and indices[-1] != last_frame_idx and last_ts - prev_kept_ts > eps:
510 - indices.append(last_frame_idx)
511 -
535 if not indices:
536 indices = [0]
537 return indices