Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/models/supported_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ th {
| `HunYuanMoEV1ForCausalLM` | Hunyuan-A13B | `tencent/Hunyuan-A13B-Instruct`, `tencent/Hunyuan-A13B-Pretrain`, `tencent/Hunyuan-A13B-Instruct-FP8`, etc. | ✅︎ | ✅︎ |
| `HYV3ForCausalLM` | HY3 | `tencent/Hy3-preview-Base`, `tencent/Hy3-preview` | ✅︎ | ✅︎ |
| `HyperCLOVAXForCausalLM` | HyperCLOVAX-SEED-Think-14B | `naver-hyperclovax/HyperCLOVAX-SEED-Think-14B` | ✅︎ | ✅︎ |
| `InklingForCausalLM` | Inkling | `thinkingmachines/Inkling-NVFP4`, `thinkingmachines/Inkling-Small-NVFP4`, etc. | | ✅︎ |
| `InternLM2ForCausalLM` | InternLM2 | `internlm/internlm2-7b`, `internlm/internlm2-chat-7b`, etc. | ✅︎ | ✅︎ |
| `InternLM3ForCausalLM` | InternLM3 | `internlm/internlm3-8b-instruct`, etc. | ✅︎ | ✅︎ |
| `IQuestCoderForCausalLM` | IQuestCoderV1 | `IQuestLab/IQuest-Coder-V1-40B-Instruct`, etc. | | |
Expand Down Expand Up @@ -538,6 +539,7 @@ These models primarily accept the [`LLM.generate`](./generative_models.md#llmgen
| `HunYuanVLForConditionalGeneration` | HunyuanOCR | T + I<sup>E+</sup> | `tencent/HunyuanOCR`, etc. | ✅︎ | ✅︎ |
| `Idefics3ForConditionalGeneration` | Idefics3 | T + I | `HuggingFaceM4/Idefics3-8B-Llama3`, etc. | ✅︎ | |
| `IsaacForConditionalGeneration` | Isaac | T + I<sup>+</sup> | `PerceptronAI/Isaac-0.1` | ✅︎ | ✅︎ |
| `InklingForConditionalGeneration` | Inkling | T + I<sup>+</sup> + A<sup>*+</sup> | `thinkingmachines/Inkling-NVFP4`, `thinkingmachines/Inkling-Small-NVFP4`, etc. | | ✅︎ |
| `InternS1ForConditionalGeneration` | Intern-S1 | T + I<sup>E+</sup> + V<sup>E+</sup> | `internlm/Intern-S1`, `internlm/Intern-S1-mini`, etc. | ✅︎ | ✅︎ |
| `InternS1ProForConditionalGeneration` | Intern-S1-Pro | T + I<sup>E+</sup> + V<sup>E+</sup> | `internlm/Intern-S1-Pro`, etc. | ✅︎ | ✅︎ |
| `InternS2MobiusForConditionalGeneration` | Intern-S2-Mobius | T + I<sup>E+</sup> + V<sup>E+</sup> | `internlm/Intern-S2-Mobius` | ✅︎ | ✅︎ |
Expand Down Expand Up @@ -650,6 +652,19 @@ Some models are supported only via the [Transformers modeling backend](#transfor
- All modalities (image, video, audio) are supported.
- Gemma 4 Unified assistant checkpoints (`model_type: gemma4_unified_assistant`) use the same MTP path as the tower-based variant. See the [Gemma 4 assistant model MTP example](../features/speculative_decoding/mtp.md#gemma-4-assistant-models).

!!! note
`InklingForCausalLM` and `InklingForConditionalGeneration` are not supported
on SM120/SM121 NVIDIA parts (RTX PRO 6000 Blackwell, GeForce Blackwell, DGX
Spark). The FA4 relative-attention kernel has no paged-KV forward there and
vLLM always attends over the paged KV cache, so those devices are rejected
at startup even though the NVFP4 MoE path itself runs on them — see
[vllm-project/vllm#51405](https://github.com/vllm-project/vllm/issues/51405).
The FA4 path is used on compute capability 9.x, 10.x and 11.x. ROCm uses a
separate Triton implementation and is not subject to this limit.

Image and audio inputs each require the corresponding tower in the
checkpoint config; variants without one expose no such modality.

!!! note
For `InternVLChatModel`, only InternVL2.5 with Qwen2.5 text backbone (`OpenGVLab/InternVL2.5-1B` etc.), InternVL3 and InternVL3.5 have video inputs support currently.

Expand Down
98 changes: 98 additions & 0 deletions tests/models/inkling/test_fa4_rel_attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import importlib

import pytest
import regex as re
import torch

from vllm.models.inkling.nvidia.attention import (
Expand All @@ -25,6 +26,7 @@
from vllm.models.inkling.nvidia.ops.fa4_rel_attention import (
INKLING_FA4_REL_ATTENTION_KERNEL,
_use_sheared_bias,
check_inkling_fa4_support,
inkling_fa4_num_splits,
)
from vllm.platforms import current_platform
Expand Down Expand Up @@ -98,6 +100,102 @@ def test_sheared_bias_architecture_selection(monkeypatch, major, expected):
_use_sheared_bias.cache_clear()


@pytest.mark.parametrize("major", [8, 9, 10, 11])
def test_fa4_support_accepts_paged_kv_architectures(monkeypatch, major):
# SM8x is not rejected: it has no paged-KV FA4 forward either, but a
# FlexAttention fallback can serve Inkling there, so the guard leaves it
# open rather than blocking that path.
monkeypatch.setattr(
current_platform,
"get_device_capability",
lambda: DeviceCapability(major=major, minor=0),
)
check_inkling_fa4_support()


def test_fa4_support_allows_unknown_capability(monkeypatch):
# NVML can fail to report a capability; that must not block startup.
monkeypatch.setattr(current_platform, "get_device_capability", lambda: None)
check_inkling_fa4_support()


def test_fa4_support_rejects_architectures_without_paged_kv(monkeypatch):
# SM12x has no paged-KV path at all, so it must be rejected while the model
# is being built, not by an assert inside the first forward pass.
monkeypatch.setattr(
current_platform,
"get_device_capability",
lambda: DeviceCapability(major=12, minor=1),
)
monkeypatch.setattr(current_platform, "get_device_name", lambda: "TestGPU")
with pytest.raises(ValueError, match=re.escape("capability 12.1")):
check_inkling_fa4_support()


def test_attention_layer_checks_fa4_support(monkeypatch):
# The guard is only useful if the attention layer actually invokes it, and
# the constructor is the single chokepoint for backbone and MTP layers.
def _reject():
raise RuntimeError("guard invoked")

monkeypatch.setattr(
"vllm.models.inkling.nvidia.attention.check_inkling_fa4_support", _reject
)
# config=None: reaching any attribute of it raises AttributeError instead,
# so RuntimeError also proves the guard runs before the rest of __init__.
with pytest.raises(RuntimeError, match="guard invoked"):
InklingAttention(
None,
num_heads=1,
num_kv_heads=1,
head_dim=HEAD_DIM,
rel_extent=128,
local_extent=128,
is_local=False,
prefix="test",
conv_owner=None,
)


@pytest.mark.parametrize(
"model_arch",
[
"InklingForCausalLM",
"InklingForConditionalGeneration",
"InklingMTPModel",
],
)
@pytest.mark.parametrize(("major", "rejected"), [(8, False), (9, False), (12, True)])
def test_platform_rejects_inkling_before_engine_start(
monkeypatch, model_arch, major, rejected
):
# verify_model_arch runs during architecture resolution in the front end,
# so an unsupported GPU never reaches worker startup or weight loading.
from vllm.platforms.cuda import CudaPlatformBase

monkeypatch.setattr(
CudaPlatformBase,
"get_device_capability",
classmethod(lambda cls, device_id=0: DeviceCapability(major=major, minor=0)),
)
if rejected:
with pytest.raises(ValueError, match=re.escape(f"'{model_arch}'")):
CudaPlatformBase.verify_model_arch(model_arch)
else:
CudaPlatformBase.verify_model_arch(model_arch)


def test_platform_ignores_unrestricted_architectures(monkeypatch):
from vllm.platforms.cuda import CudaPlatformBase

monkeypatch.setattr(
CudaPlatformBase,
"get_device_capability",
classmethod(lambda cls, device_id=0: DeviceCapability(major=12, minor=0)),
)
CudaPlatformBase.verify_model_arch("LlamaForCausalLM")


@pytest.fixture
def blackwell_platform(monkeypatch):
monkeypatch.setattr(
Expand Down
8 changes: 5 additions & 3 deletions tests/models/test_initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
HF_EXAMPLE_MODELS,
HfExampleModels,
)
from .utils import dummy_hf_overrides
from .utils import dummy_hf_overrides, skip_if_capability_restricted

# This minimal list of model architectures is smaller than the total list of
# supported models. The intention is that in the "typical" regression testing
Expand Down Expand Up @@ -114,9 +114,9 @@ def _initialize_kv_caches_v1(self, vllm_config):
"(see #41376)"
)

if model_arch in ["DeepseekV32ForCausalLM", "GlmMoeDsaForCausalLM"]:
from vllm.platforms import current_platform
from vllm.platforms import current_platform

if model_arch in ["DeepseekV32ForCausalLM", "GlmMoeDsaForCausalLM"]:
capability = current_platform.get_device_capability()
if capability and capability.major < 9:
pytest.skip(
Expand All @@ -125,6 +125,8 @@ def _initialize_kv_caches_v1(self, vllm_config):
f"capability {capability.major}.{capability.minor}"
)

skip_if_capability_restricted(model_arch)

with (
patch.object(V1EngineCore, "_initialize_kv_caches", _initialize_kv_caches_v1),
monkeypatch.context() as m,
Expand Down
5 changes: 5 additions & 0 deletions tests/models/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from ..utils import create_new_process_for_each_test
from .registry import HF_EXAMPLE_MODELS
from .utils import skip_if_capability_restricted


@pytest.mark.parametrize("model_arch", ModelRegistry.get_supported_archs())
Expand Down Expand Up @@ -59,6 +60,10 @@ def test_registry_imports(model_arch):
):
pytest.skip("Dots3 NOTE is only supported on CUDA")

# _try_load_model_cls runs verify_model_arch, which rejects architectures
# whose kernels are missing for this GPU's compute capability.
skip_if_capability_restricted(model_arch)

# Ensure all model classes can be imported successfully
model_cls = ModelRegistry._try_load_model_cls(model_arch)
assert model_cls is not None
Expand Down
28 changes: 28 additions & 0 deletions tests/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,3 +614,31 @@ def check_transformers_version(
min_transformers_version=min_transformers_version,
max_transformers_version=max_transformers_version,
).check_transformers_version(on_fail="skip")


def skip_if_capability_restricted(model_arch: str) -> None:
"""Skip when this GPU lacks the kernels an architecture requires.

`CudaPlatform.verify_model_arch` rejects these architectures during model
resolution, so any test that resolves one would error instead of skipping.
"""
import pytest

from vllm.platforms import current_platform

if not current_platform.is_cuda():
return
# Imported lazily: vllm.platforms.cuda assumes a CUDA platform at import.
from vllm.platforms.cuda import _CAPABILITY_RESTRICTED_MODELS

unsupported_majors = _CAPABILITY_RESTRICTED_MODELS.get(model_arch)
if unsupported_majors is None:
return
capability = current_platform.get_device_capability()
if capability and capability.major in unsupported_majors:
families = " / ".join(f"{major}.x" for major in unsupported_majors)
pytest.skip(
f"{model_arch} has no working kernel on compute capability "
f"{families}. Current device has compute capability "
f"{capability.major}.{capability.minor}"
)
20 changes: 20 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,26 @@ def test_draft_runner(model_id, expected_runner_type, expected_convert_type):
assert config.convert_type == expected_convert_type


def test_platform_rejects_unsupported_arch_during_config(monkeypatch):
"""Architectures the platform cannot run are rejected in the front end.

`_try_load_model_cls` checks this as well, but only once a worker is
already up and loading the model.
"""
rejected = []

def verify_model_arch(model_arch: str) -> None:
rejected.append(model_arch)
raise ValueError(f"Model architecture '{model_arch}' is not supported")

monkeypatch.setattr(current_platform, "verify_model_arch", verify_model_arch)

with pytest.raises(ValueError, match="Qwen3ForCausalLM"):
ModelConfig("Qwen/Qwen3-0.6B", max_model_len=2048)

assert rejected == ["Qwen3ForCausalLM"]


MODEL_IDS_EXPECTED = [
("Qwen/Qwen1.5-7B", 32768),
("mistralai/Mistral-7B-v0.1", 4096),
Expand Down
5 changes: 5 additions & 0 deletions vllm/config/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,11 @@ def __post_init__(
self._architecture = arch
logger.info("Resolved architecture: %s", arch)

# Reject architectures this platform cannot run before any worker is
# spawned. `_try_load_model_cls` checks this too, but that only runs
# once the model is being loaded inside a worker.
current_platform.verify_model_arch(arch)

# Set default tokenizer modes based on model architecture
if self.tokenizer_mode == "auto":
if self.model_impl == "terratorch":
Expand Down
2 changes: 2 additions & 0 deletions vllm/models/inkling/nvidia/attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from .ops.fa4_rel_attention import (
INKLING_FA4_REL_ATTENTION_KERNEL,
bucket_max_seqlen_q,
check_inkling_fa4_support,
inkling_fa4_num_splits,
)
from .ops.qkvr_prep import fused_qkvr_prep
Expand Down Expand Up @@ -81,6 +82,7 @@ def __init__(
conv_owner: InklingConvState,
) -> None:
super().__init__()
check_inkling_fa4_support()
self.prefix = prefix
self.is_local = is_local
self.hidden_size = config.hidden_size
Expand Down
42 changes: 40 additions & 2 deletions vllm/models/inkling/nvidia/ops/fa4_rel_attention.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,48 @@ def bucket_max_seqlen_q(max_seqlen_q: int) -> int:
return 1 << max(0, max_seqlen_q - 1).bit_length()


# What the vendored FA4 relative-attention forward supports, by compute
# capability major. Kept together because all three describe the same external
# kernel and move as one when cmake/external_projects/tml_fa4.cmake is bumped.
#
# SM12x is listed as unsupported rather than allow-listing the architectures
# that work: SM8x also lacks a paged-KV FA4 forward, but a FlexAttention
# fallback carrying the relative bias through score_mod has been shown to serve
# Inkling end-to-end there, so it is left open for that path.
_NO_PAGED_KV_MAJORS = (12,)
_SHEARED_BIAS_MAJORS = (10, 11)
_SPLIT_KV_MAJORS = (10, 11)


@cache
def _use_sheared_bias() -> bool:
capability = current_platform.get_device_capability()
return capability is not None and capability.major in (10, 11)
return capability is not None and capability.major in _SHEARED_BIAS_MAJORS


def check_inkling_fa4_support() -> None:
"""Reject GPUs with no paged-KV path for Inkling relative attention.

vLLM always attends over the paged KV cache, so on SM12x the FA4 kernel
asserts during the first forward pass -- long after the weights are loaded
and the KV cache is sized. Fail while the model is still being constructed
instead.

Raises:
ValueError: If the device has no paged-KV relative-attention path.
"""
capability = current_platform.get_device_capability()
# None means the capability could not be queried (non-CUDA, or NVML
# unavailable); assume supported rather than blocking startup.
if capability is None or capability.major not in _NO_PAGED_KV_MAJORS:
return
raise ValueError(
f"Inkling is not supported on {current_platform.get_device_name()} "
f"(compute capability {capability.major}.{capability.minor}): the FA4 "
f"relative-attention kernel has no paged-KV forward on SM"
f"{capability.major}x, and vLLM always attends over the paged KV "
f"cache. See vllm-project/vllm#51405."
)


@cache
Expand Down Expand Up @@ -79,7 +117,7 @@ def inkling_fa4_num_splits(
) -> int:
"""Return the split-KV cap for Inkling relative attention."""
capability = current_platform.get_device_capability()
if capability is not None and capability.major == 9:
if capability is not None and capability.major not in _SPLIT_KV_MAJORS:
return 1
if is_local:
return 1
Expand Down
Loading
Loading