From 292052a122dd0d79046d13ec194961e40ee704db Mon Sep 17 00:00:00 2001 From: Fred Wei <20514172+WeiHaocheng@users.noreply.github.com> Date: Wed, 21 Oct 2026 18:11:07 -0700 Subject: [PATCH 1/2] [None][refactor] Register modeling_v2 routes with a @routes decorator Replace the hand-maintained MODELING_V2_ROUTERS table with a @routes(...) decorator on each family's route function. models/__init__.py imports every family's routing module, which registers its route; the resolver reads the registry through registered_routes(). Target modeling code is still imported only on the route branch that returns it. The claims test reads the registry instead of the table, and its orphan check now catches a routing.py that is not imported or carries no @routes. Signed-off-by: Fred Wei <20514172+WeiHaocheng@users.noreply.github.com> --- .../_experimental/modeling_v2/__init__.py | 2 - .../modeling_v2/_router_index.py | 84 ++++++++++--------- .../modeling_v2/models/__init__.py | 11 ++- .../modeling_v2/models/deepseek_v3/routing.py | 3 +- .../modeling_v2/models/glm_moe_dsa/routing.py | 11 ++- .../modeling_v2/models/gpt_oss/routing.py | 3 +- .../modeling_v2/models/qwen4_exp/routing.py | 7 +- .../modeling_v2/test_modeling_v2_claims.py | 36 ++++---- 8 files changed, 87 insertions(+), 70 deletions(-) diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/__init__.py b/tensorrt_llm/_torch/_experimental/modeling_v2/__init__.py index c5140e7c912b..96e5209c99dc 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/__init__.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/__init__.py @@ -27,7 +27,6 @@ """ from ._router_index import ( - MODELING_V2_ROUTERS, MODELING_V2_VALIDATE_ENV, ModelingV2Mode, NoTarget, @@ -36,7 +35,6 @@ ) __all__ = [ - "MODELING_V2_ROUTERS", "MODELING_V2_VALIDATE_ENV", "ModelingV2Mode", "NoTarget", diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py b/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py index fc1f9a56de1b..790a9ea677e7 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py @@ -1,9 +1,9 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Architecture name -> routing module, and the resolver that reads the table. +"""Architecture name -> ``route``, and the resolver that consults it. The checkpoint's own ``architectures[0]`` (``GptOssForCausalLM``, -``DeepseekV3ForCausalLM``) selects a routing module, never a target: one +``DeepseekV3ForCausalLM``) selects a family's ``route``, never a target: one architecture has many deployments and a target serves exactly one of them. ``modeling_v2_resolve`` returns the target *class*, the model loader records it on ``ModelConfig.modeling_v2_target``, and ``AutoModelForCausalLM._resolve_class`` @@ -11,11 +11,13 @@ Two levels, on purpose: -* This table maps ``architectures[0]`` -> routing module. Its keys are things - that already exist in every checkpoint's config.json, so adding a new - checkpoint, GPU arch or parallel topology never touches it -- only a new - architecture family does. -* The routing module's ``route`` owns the rest of the decision as one +* Each family's ``routing.py`` declares the ``architectures[0]`` values its + ``route`` serves with ``@routes(...)``, and ``models/__init__.py`` imports + every family's ``routing.py``, which is what registers them. The names are + things that already exist in every checkpoint's config.json, so adding a + new checkpoint, GPU arch or parallel topology never touches either -- only + a new architecture family does. +* That ``route`` owns the rest of the decision as one forward-reading tree over the facts that fix which code runs: the checkpoint's config, the parallel mapping, the GPU's compute capability, the speculative-decoding config and the KV-cache algorithm the engine will run @@ -29,17 +31,17 @@ target claims a configuration regardless of them; ``require`` promises that the target ran, not that a particular knob setting was measured. -Routing modules are imported lazily: resolving a GptOss config never imports -the DeepSeek tree, and a target's modeling code is imported only on the -``route`` branch that returns it. +Routing modules are imported on the first resolve, all of them, and hold no +modeling code: a target's modeling code is imported only on the ``route`` +branch that returns it, so resolving a GptOss config never imports a DeepSeek +target. """ from __future__ import annotations import enum import os -from importlib import import_module -from typing import TYPE_CHECKING, Optional, Tuple +from typing import TYPE_CHECKING, Callable, Dict, Optional, Tuple from tensorrt_llm.logger import logger @@ -47,17 +49,29 @@ from tensorrt_llm._torch.model_config import ModelConfig from tensorrt_llm.llmapi.llm_args import TorchLlmArgs -_PACKAGE = "tensorrt_llm._torch._experimental.modeling_v2" +# architectures[0] -> the ``route`` that decides where it lands, filled by ``@routes``. +_ROUTES: Dict[str, Callable[..., type]] = {} -# architectures[0] -> routing module, relative to this package. -MODELING_V2_ROUTERS = { - "GptOssForCausalLM": "models.gpt_oss.routing", - "DeepseekV3ForCausalLM": "models.deepseek_v3.routing", - # Qwen3.8-Flash-Next publishes ``Qwen4ExpForConditionalGeneration``, and the - # config loader's composite branch writes the same name back. - "Qwen4ExpForConditionalGeneration": "models.qwen4_exp.routing", - "GlmMoeDsaForCausalLM": "models.glm_moe_dsa.routing", -} + +def routes(*architectures: str): + """Declare that the decorated ``route`` decides where these ``architectures[0]`` values land.""" + + def register(route): + for arch in architectures: + assert arch not in _ROUTES, ( + f"{arch} routed by both {_ROUTES[arch].__module__} and {route.__module__}" + ) + _ROUTES[arch] = route + return route + + return register + + +def registered_routes() -> Dict[str, Callable[..., type]]: + """architectures[0] -> ``route``, for every family ``models/__init__.py`` imports.""" + from . import models # noqa: F401 + + return _ROUTES #: Turns the step contract check on. Off by default, so a served engine runs @@ -99,7 +113,7 @@ def of(cls, llm_args: "TorchLlmArgs") -> "ModelingV2Mode": class NoTarget(Exception): - """Raised by a routing module's ``route`` when no target fits. + """Raised by a family's ``route`` when no target fits. The message says, in words, which fact ruled the targets out. Under ``auto`` the resolver logs it and falls back to the built-in implementation; @@ -130,14 +144,6 @@ def tp_reaches_across_nodes(mapping) -> bool: return MNNVLAllReduce.is_mnnvl(mapping, torch.bfloat16) -def routing_module(arch: str): - """Import and return the routing module for ``arch``, or None.""" - name = MODELING_V2_ROUTERS.get(arch) - if name is None: - return None - return import_module(f"{_PACKAGE}.{name}") - - def _device_compute_capability() -> Tuple[int, int]: import torch @@ -172,8 +178,8 @@ def modeling_v2_resolve(config: "ModelConfig", llm_args: "TorchLlmArgs") -> Opti """Decide which modeling_v2 target, if any, this deployment builds. Returns the target class, or None for the built-in implementation: - when ``llm_args.modeling_v2`` is ``off``, when no routing module claims the - architecture, or when the routing module's ``route`` raises ``NoTarget``. + when ``llm_args.modeling_v2`` is ``off``, when no ``route`` is registered + for the architecture, or when that ``route`` raises ``NoTarget``. In ``require`` every one of those but ``off`` raises ``ValueError`` instead. """ mode = ModelingV2Mode.of(llm_args) @@ -186,13 +192,11 @@ def modeling_v2_resolve(config: "ModelConfig", llm_args: "TorchLlmArgs") -> Opti arch = pretrained_config.architectures[0] try: - routing = routing_module(arch) - if routing is None: - known = ", ".join(sorted(MODELING_V2_ROUTERS)) or "(none)" - raise NoTarget( - f"no routing module for this architecture; routed architectures: {known}" - ) - return routing.route( + route = registered_routes().get(arch) + if route is None: + known = ", ".join(sorted(registered_routes())) or "(none)" + raise NoTarget(f"no route for this architecture; routed architectures: {known}") + return route( pretrained_config, config.mapping, _device_compute_capability(), diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/__init__.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/__init__.py index 9da2ae758216..69dd53ea5e1b 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/__init__.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/__init__.py @@ -5,4 +5,13 @@ ``models//`` corresponds one-to-one with the built-in zoo's ``modeling_.py``: the same architecture, split into one self-contained codebase per deployment target instead of one class serving every -checkpoint.""" +checkpoint. + +Importing this package imports every family's ``routing`` module, and each one +registers its ``route`` through ``@routes``. A new family adds one line here. +""" + +from .deepseek_v3 import routing as deepseek_v3_routing # noqa: F401 +from .glm_moe_dsa import routing as glm_moe_dsa_routing # noqa: F401 +from .gpt_oss import routing as gpt_oss_routing # noqa: F401 +from .qwen4_exp import routing as qwen4_exp_routing # noqa: F401 diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py index 6f4dd9fd6c2f..9adab1845fd1 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py @@ -16,7 +16,7 @@ from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget, tp_reaches_across_nodes +from ..._router_index import NoTarget, routes, tp_reaches_across_nodes # The GPUs the target is certified on: sm_100 (B200, GB200) and sm_103 (B300, # GB300). They run the same kernels. @@ -74,6 +74,7 @@ from tensorrt_llm.llmapi.llm_args import TorchLlmArgs +@routes("DeepseekV3ForCausalLM") def route( pretrained_config: Any, mapping: Any, diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py index 7feda0cc4e16..e75ac5b62223 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py @@ -11,24 +11,31 @@ from __future__ import annotations -from typing import Any, Optional, Tuple +from typing import TYPE_CHECKING, Any, Optional, Tuple from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget +from ..._router_index import NoTarget, routes + +if TYPE_CHECKING: + from tensorrt_llm._torch.model_config import ModelConfig + from tensorrt_llm.llmapi.llm_args import TorchLlmArgs # (layers, hidden, routed experts, q_lora_rank). Shape-sniffing also matches # fine-tunes of the same shape; the accuracy gate pins identity. _SHAPE = (78, 6144, 256, 2048) +@routes("GlmMoeDsaForCausalLM") def route( pretrained_config: Any, mapping: Any, compute_capability: Tuple[int, int], spec_config: Optional[Any], kv_cache_quant_algo: Optional[Any], + model_config: Optional[ModelConfig] = None, + llm_args: Optional[TorchLlmArgs] = None, ) -> type: """Return the target class for this deployment, or raise ``NoTarget`` saying why none fits. diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py index 7bd51d78e37a..20098df47baa 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any, Optional, Tuple -from ..._router_index import NoTarget, tp_reaches_across_nodes +from ..._router_index import NoTarget, routes, tp_reaches_across_nodes # The GPUs the target is certified on: sm_100 (B200, GB200) and sm_103 (B300, # GB300). They run the same kernels. @@ -47,6 +47,7 @@ from tensorrt_llm.llmapi.llm_args import TorchLlmArgs +@routes("GptOssForCausalLM") def route( pretrained_config: Any, mapping: Any, diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py index ef2f31f01234..922e081840aa 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py @@ -5,7 +5,7 @@ `route` compares the facts that fix which code runs against the one deployment the target was written for, and raises `NoTarget` stating them otherwise (`auto` falls back to the built-in implementation; `require` raises). The checkpoint's published -architecture name is kept by `load_pretrained_config`, so `_router_index` keys on it. +architecture name is kept by `load_pretrained_config`, so `route` registers under it. All criteria are read off `text_config` / `vision_config`; the composite parent's mirrored fields are unreliable. """ @@ -19,7 +19,7 @@ from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget +from ..._router_index import NoTarget, routes if TYPE_CHECKING: from tensorrt_llm._torch.model_config import ModelConfig @@ -134,6 +134,9 @@ def _check_engine( ) +# Qwen3.8-Flash-Next publishes ``Qwen4ExpForConditionalGeneration``, and the +# config loader's composite branch writes the same name back. +@routes("Qwen4ExpForConditionalGeneration") def route( pretrained_config: Any, mapping: Any, diff --git a/tests/unittest/_torch/modeling_v2/test_modeling_v2_claims.py b/tests/unittest/_torch/modeling_v2/test_modeling_v2_claims.py index f52cafa47e16..6b28ed9f190f 100644 --- a/tests/unittest/_torch/modeling_v2/test_modeling_v2_claims.py +++ b/tests/unittest/_torch/modeling_v2/test_modeling_v2_claims.py @@ -19,27 +19,25 @@ import ast import re +import sys from pathlib import Path import pytest import tensorrt_llm._torch._experimental.modeling_v2 as _modeling_v2 -from tensorrt_llm._torch._experimental.modeling_v2._router_index import ( - MODELING_V2_ROUTERS, - routing_module, -) +from tensorrt_llm._torch._experimental.modeling_v2._router_index import registered_routes # The package, not this file: these paths address the tree under test, and this # test lives in tests/ while that tree lives in tensorrt_llm/. pytestmark = pytest.mark.cpu_only _ROOT = Path(_modeling_v2.__file__).resolve().parent -_ARCHS = sorted(MODELING_V2_ROUTERS) +_ARCHS = sorted(registered_routes()) -def _module_path(dotted: str) -> Path: - """Resolve a package-relative dotted module name to its file.""" - return _ROOT / (dotted.replace(".", "/") + ".py") +def _routing_path(arch: str) -> Path: + """The routing.py whose ``route`` is registered for ``arch``.""" + return Path(sys.modules[registered_routes()[arch].__module__].__file__).resolve() # ``route`` hands back the class itself, imported from the target directory @@ -50,23 +48,19 @@ def _module_path(dotted: str) -> Path: def _routed_targets(arch: str): """(target directory, class name) for every target ``route`` can return.""" - source = _module_path(MODELING_V2_ROUTERS[arch]).read_text() - family_dir = _module_path(MODELING_V2_ROUTERS[arch]).parent + source = _routing_path(arch).read_text() + family_dir = _routing_path(arch).parent return [(family_dir / target, name) for target, name in _TARGET_IMPORT.findall(source)] -def test_every_routed_architecture_has_an_importable_routing_module(): - for arch in _ARCHS: - assert routing_module(arch) is not None, arch - - def test_no_routing_module_is_orphaned(): - """A routing.py the index does not name would never be consulted.""" - indexed = {_module_path(m) for m in MODELING_V2_ROUTERS.values()} + """A routing.py that models/__init__.py does not import, or whose ``route`` + carries no ``@routes``, would never be consulted.""" + registered = {_routing_path(arch) for arch in _ARCHS} on_disk = set((_ROOT / "models").glob("*/routing.py")) - assert on_disk == indexed, ( - f"routing modules on disk but not in MODELING_V2_ROUTERS: " - f"{sorted(p.relative_to(_ROOT) for p in on_disk - indexed)}" + assert on_disk == registered, ( + f"routing modules on disk with no registered route: " + f"{sorted(p.relative_to(_ROOT) for p in on_disk - registered)}" ) @@ -74,7 +68,7 @@ def test_no_routing_module_is_orphaned(): def test_no_target_directory_is_orphaned(arch): """A target directory ``route`` never imports from is dead code: nothing else can reach it.""" - family_dir = _module_path(MODELING_V2_ROUTERS[arch]).parent + family_dir = _routing_path(arch).parent on_disk = {p.parent for p in family_dir.glob("*/modeling.py")} routed = {target_dir for target_dir, _ in _routed_targets(arch)} assert on_disk == routed, ( From a51355add330d7040320c3d2ae8122aa9f552da1 Mon Sep 17 00:00:00 2001 From: Fred Wei <20514172+WeiHaocheng@users.noreply.github.com> Date: Fri, 23 Oct 2026 20:49:20 -0700 Subject: [PATCH 2/2] [None][refactor] Pass a modeling_v2 route the ModelConfig, LLM args and GPU route(model_config, llm_args, compute_capability) replaces the five facts the resolver used to read out for it. Each family reads the pretrained config, mapping and spec config off the ModelConfig, and the KV-cache algorithm the engine will run with through engine_kv_cache_quant_algo. Every route now receives the ModelConfig and the LLM args, so qwen4_exp always checks its engine choices. Signed-off-by: Fred Wei <20514172+WeiHaocheng@users.noreply.github.com> --- .../_experimental/modeling_v2/README.md | 5 ++- .../modeling_v2/_router_index.py | 27 ++++++---------- .../modeling_v2/models/deepseek_v3/routing.py | 22 +++++-------- .../modeling_v2/models/glm_moe_dsa/routing.py | 16 ++++------ .../modeling_v2/models/gpt_oss/routing.py | 22 +++++-------- .../modeling_v2/models/qwen4_exp/routing.py | 32 ++++++------------- 6 files changed, 45 insertions(+), 79 deletions(-) diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/README.md b/tensorrt_llm/_torch/_experimental/modeling_v2/README.md index 4e86b1664588..f9c8568348f3 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/README.md +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/README.md @@ -71,9 +71,8 @@ LLM(model=..., modeling_v2=...) -> ModelLoader +-- modeling_v2_resolve(config, llm_args) | _router_index.py +-- architectures[0] -> routing module - models//routing.py +-- route(config, mapping, compute_capability, - | spec_config, kv_cache_quant_algo, - | model_config, llm_args) + models//routing.py +-- route(model_config, llm_args, + | compute_capability) | -> target class, or NoTarget | config.modeling_v2_target = class diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py b/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py index 790a9ea677e7..b9cd4f09fac1 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/_router_index.py @@ -18,13 +18,14 @@ new checkpoint, GPU arch or parallel topology never touches either -- only a new architecture family does. * That ``route`` owns the rest of the decision as one - forward-reading tree over the facts that fix which code runs: the - checkpoint's config, the parallel mapping, the GPU's compute capability, the - speculative-decoding config and the KV-cache algorithm the engine will run - with. It also receives the ``ModelConfig`` and the LLM arguments, from which a - family derives any other engine choice its targets depend on. It returns the - target class, or raises :class:`NoTarget` with the reason in words. Reading - that one file tells you where any configuration lands. + forward-reading tree over the facts that fix which code runs. It receives + the ``ModelConfig``, the LLM arguments and the GPU's compute capability, and + reads from the first two the checkpoint's config, the parallel mapping, the + speculative-decoding config, the KV-cache algorithm the engine will run with + (:func:`engine_kv_cache_quant_algo`) and any other engine choice its targets + depend on. It returns the target class, or raises :class:`NoTarget` with the + reason in words. Reading that one file tells you where any configuration + lands. Tuning knobs -- ``max_batch_size``, ``max_num_tokens``, CUDA graph settings -- are deliberately not routing inputs. They do not change which code runs, so a @@ -154,7 +155,7 @@ def _device_compute_capability() -> Tuple[int, int]: return torch.cuda.get_device_capability() -def _kv_cache_quant_algo(config: "ModelConfig", llm_args: "TorchLlmArgs"): +def engine_kv_cache_quant_algo(config: "ModelConfig", llm_args: "TorchLlmArgs"): """The KV-cache algorithm the engine will run with, which routing runs too early to read. The loader resolves the target before `validate_and_set_kv_cache_quant` @@ -196,15 +197,7 @@ def modeling_v2_resolve(config: "ModelConfig", llm_args: "TorchLlmArgs") -> Opti if route is None: known = ", ".join(sorted(registered_routes())) or "(none)" raise NoTarget(f"no route for this architecture; routed architectures: {known}") - return route( - pretrained_config, - config.mapping, - _device_compute_capability(), - config.spec_config, - _kv_cache_quant_algo(config, llm_args), - model_config=config, - llm_args=llm_args, - ) + return route(config, llm_args, _device_compute_capability()) except NoTarget as why: if mode is ModelingV2Mode.REQUIRE: raise ValueError(f"modeling_v2='require', but no target fits {arch!r}: {why}") from None diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py index 9adab1845fd1..aeaf7e8aa266 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/deepseek_v3/routing.py @@ -11,12 +11,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional, Tuple +from typing import TYPE_CHECKING, Tuple from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget, routes, tp_reaches_across_nodes +from ..._router_index import NoTarget, engine_kv_cache_quant_algo, routes, tp_reaches_across_nodes # The GPUs the target is certified on: sm_100 (B200, GB200) and sm_103 (B300, # GB300). They run the same kernels. @@ -76,23 +76,17 @@ @routes("DeepseekV3ForCausalLM") def route( - pretrained_config: Any, - mapping: Any, - compute_capability: Tuple[int, int], - spec_config: Optional[Any], - kv_cache_quant_algo: Optional[Any], - model_config: Optional[ModelConfig] = None, - llm_args: Optional[TorchLlmArgs] = None, + model_config: ModelConfig, llm_args: TorchLlmArgs, compute_capability: Tuple[int, int] ) -> type: """Return the target class for this deployment, or raise ``NoTarget`` saying why none fits. - ``kv_cache_quant_algo`` is the KV-cache algorithm the engine will run with - (see ``_router_index._kv_cache_quant_algo``), not the checkpoint's alone. - Each target is imported where it is returned, so declining costs no import - of modeling code. No engine choice in ``model_config`` or ``llm_args`` changes - which of these targets runs, so neither is read. + of modeling code. """ + pretrained_config = model_config.pretrained_config + mapping = model_config.mapping + spec_config = model_config.spec_config + kv_cache_quant_algo = engine_kv_cache_quant_algo(model_config, llm_args) # The checkpoint is recognized by its config shape; see the gpt_oss routing # module for what shape-sniffing does and does not pin. q_lora_rank is in # the shape because it changes the attention weight layout: a checkpoint diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py index e75ac5b62223..6ab54ed99609 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/glm_moe_dsa/routing.py @@ -11,12 +11,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional, Tuple +from typing import TYPE_CHECKING, Tuple from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget, routes +from ..._router_index import NoTarget, engine_kv_cache_quant_algo, routes if TYPE_CHECKING: from tensorrt_llm._torch.model_config import ModelConfig @@ -29,19 +29,17 @@ @routes("GlmMoeDsaForCausalLM") def route( - pretrained_config: Any, - mapping: Any, - compute_capability: Tuple[int, int], - spec_config: Optional[Any], - kv_cache_quant_algo: Optional[Any], - model_config: Optional[ModelConfig] = None, - llm_args: Optional[TorchLlmArgs] = None, + model_config: ModelConfig, llm_args: TorchLlmArgs, compute_capability: Tuple[int, int] ) -> type: """Return the target class for this deployment, or raise ``NoTarget`` saying why none fits. Each target is imported where it is returned, so declining costs no import of modeling code. """ + pretrained_config = model_config.pretrained_config + mapping = model_config.mapping + spec_config = model_config.spec_config + kv_cache_quant_algo = engine_kv_cache_quant_algo(model_config, llm_args) shape = ( pretrained_config.num_hidden_layers, pretrained_config.hidden_size, diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py index 20098df47baa..b739817ff437 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/gpt_oss/routing.py @@ -10,9 +10,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional, Tuple +from typing import TYPE_CHECKING, Tuple -from ..._router_index import NoTarget, routes, tp_reaches_across_nodes +from ..._router_index import NoTarget, engine_kv_cache_quant_algo, routes, tp_reaches_across_nodes # The GPUs the target is certified on: sm_100 (B200, GB200) and sm_103 (B300, # GB300). They run the same kernels. @@ -49,23 +49,17 @@ @routes("GptOssForCausalLM") def route( - pretrained_config: Any, - mapping: Any, - compute_capability: Tuple[int, int], - spec_config: Optional[Any], - kv_cache_quant_algo: Optional[Any], - model_config: Optional[ModelConfig] = None, - llm_args: Optional[TorchLlmArgs] = None, + model_config: ModelConfig, llm_args: TorchLlmArgs, compute_capability: Tuple[int, int] ) -> type: """Return the target class for this deployment, or raise ``NoTarget`` saying why none fits. - ``kv_cache_quant_algo`` is the KV-cache algorithm the engine will run with - (see ``_router_index._kv_cache_quant_algo``), not the checkpoint's alone. - Each target is imported where it is returned, so declining costs no import - of modeling code. No engine choice in ``model_config`` or ``llm_args`` changes - which of these targets runs, so neither is read. + of modeling code. """ + pretrained_config = model_config.pretrained_config + mapping = model_config.mapping + spec_config = model_config.spec_config + kv_cache_quant_algo = engine_kv_cache_quant_algo(model_config, llm_args) # The checkpoint is recognized by its config shape, the upstream idiom # (``is_mla``, ``is_nemotron_hybrid`` do the same). A fine-tune with the # same shape routes here too; the accuracy gate is what pins the identity. diff --git a/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py b/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py index 922e081840aa..af8a50119783 100644 --- a/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py +++ b/tensorrt_llm/_torch/_experimental/modeling_v2/models/qwen4_exp/routing.py @@ -19,7 +19,7 @@ from tensorrt_llm.llmapi.llm_args import MTPDecodingConfig from tensorrt_llm.quantization.mode import QuantAlgo -from ..._router_index import NoTarget, routes +from ..._router_index import NoTarget, engine_kv_cache_quant_algo, routes if TYPE_CHECKING: from tensorrt_llm._torch.model_config import ModelConfig @@ -77,7 +77,7 @@ def engine_choices(model_config: ModelConfig, llm_args: TorchLlmArgs) -> EngineC def _check_engine( - spec_config: Optional[Any], kv_cache_quant_algo: Optional[Any], engine: Optional[EngineChoices] + spec_config: Optional[Any], kv_cache_quant_algo: Optional[Any], engine: EngineChoices ) -> None: """Decline what the target's constructor or first forward would refuse. @@ -98,8 +98,6 @@ def _check_engine( f"a {kv_cache_quant_algo} KV cache; the target stores K/V as the checkpoint's bf16, " "as FP8 or as NVFP4" ) - if engine is None: - return if engine.torch_dtype != torch.bfloat16: raise NoTarget(f"the engine runs the model in {engine.torch_dtype}; the target is bf16") if str(engine.moe_backend).upper() not in _MOE_BACKENDS: @@ -138,23 +136,18 @@ def _check_engine( # config loader's composite branch writes the same name back. @routes("Qwen4ExpForConditionalGeneration") def route( - pretrained_config: Any, - mapping: Any, - compute_capability: Tuple[int, int], - spec_config: Optional[Any], - kv_cache_quant_algo: Optional[Any], - model_config: Optional[ModelConfig] = None, - llm_args: Optional[TorchLlmArgs] = None, + model_config: ModelConfig, llm_args: TorchLlmArgs, compute_capability: Tuple[int, int] ) -> type: """Return the target class for this deployment, or raise ``NoTarget`` saying why none fits. - ``kv_cache_quant_algo`` is the KV-cache algorithm the engine will run with - (see ``_router_index._kv_cache_quant_algo``), not the checkpoint's alone. - The target is imported where it is returned, so declining costs no import - of modeling code. With ``model_config`` and ``llm_args`` it also declines the - engine choices (`EngineChoices`) the target's constructor would refuse. + of modeling code. It also declines the engine choices (`EngineChoices`) the + target's constructor would refuse. """ + pretrained_config = model_config.pretrained_config + mapping = model_config.mapping + spec_config = model_config.spec_config + kv_cache_quant_algo = engine_kv_cache_quant_algo(model_config, llm_args) text = getattr(pretrained_config, "text_config", None) vision = getattr(pretrained_config, "vision_config", None) # Absent means this is not the normalized Qwen4-Exp composite at all, and @@ -216,12 +209,7 @@ def route( # isort: on # fmt: on - engine = ( - engine_choices(model_config, llm_args) - if model_config is not None and llm_args is not None - else None - ) - _check_engine(spec_config, kv_cache_quant_algo, engine) + _check_engine(spec_config, kv_cache_quant_algo, engine_choices(model_config, llm_args)) return ModelingV2Qwen38FlashNextNvfp4Sm103Tp1 raise NoTarget(