diff --git a/src/runtime/models/internvl/MODEL.toml b/src/runtime/models/internvl/MODEL.toml index c7d59df9af..0de8b38fd6 100644 --- a/src/runtime/models/internvl/MODEL.toml +++ b/src/runtime/models/internvl/MODEL.toml @@ -6,5 +6,6 @@ runtime_library = "libtrtmc_model_internvl.so" runtime_plugins = ["plugin.cpp|register_internvl_plugin"] runtime_strategies = ["internvl_vision_language"] runtime_tests = [ - "test_internvl_vl_pipeline|test_internvl_vl_pipeline.cpp|trtmc_model_internvl,trtmc_backend_trt|_|REQUIRES_TRT", + "test_internvl_runtime_config_contract|test_internvl_runtime_config_contract.cpp|_|_|_", + "test_internvl_vl_pipeline|test_internvl_vl_pipeline.cpp|trtmc_model_internvl,trtmc_backend_trt|_|REQUIRES_TRT,REQUIRES_GPU", ] diff --git a/tests/cpp/models/internvl/test_internvl_runtime_config_contract.cpp b/tests/cpp/models/internvl/test_internvl_runtime_config_contract.cpp new file mode 100644 index 0000000000..96e4da05e0 --- /dev/null +++ b/tests/cpp/models/internvl/test_internvl_runtime_config_contract.cpp @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// CPU-only consumer contract for InternVL's serialized runtime config. + +#include "trtmc/runtime/pipeline_plugin.h" + +#include +#include + +int main() { + const std::string config = R"({ + "model_type": "internvl", + "text_config": { + "vocab_size": 151674, + "hidden_size": 1536, + "num_hidden_layers": 28, + "num_attention_heads": 12, + "num_key_value_heads": 2, + "head_dim": 128, + "bos_token_id": 151643 + }, + "vocab_size": 151674, + "hidden_size": 1536, + "num_hidden_layers": 28, + "num_attention_heads": 12, + "num_key_value_heads": 2, + "head_dim": 128, + "bos_token_id": 151643, + "runtime_strategy": "internvl_vision_language" + })"; + + const auto parsed = trtmc::parse_base_config(config, 256); + bool ok = true; + const auto check = [&](bool condition, const char* name) { + if (!condition) { + std::cerr << "FAIL: " << name << '\n'; + ok = false; + } + }; + + check(parsed.runtime_strategy == "internvl_vision_language", "runtime strategy"); + check(parsed.vocab_size == 151674, "vocabulary size"); + check(parsed.hidden_size == 1536, "hidden size"); + check(parsed.num_layers == 28, "layer count"); + check(parsed.num_heads == 12, "attention head count"); + check(parsed.num_kv_heads == 2, "KV head count"); + check(parsed.head_dim == 128, "head dimension"); + check(parsed.id_bos == 151643, "BOS token"); + check(parsed.attention_size == 1536, "attention width"); + return ok ? 0 : 1; +} diff --git a/tests/e2e/models/internvl/test_internvl_family_plugin_weights.py b/tests/e2e/models/internvl/test_internvl_family_plugin_weights.py index 823e7fd400..241aebdd17 100644 --- a/tests/e2e/models/internvl/test_internvl_family_plugin_weights.py +++ b/tests/e2e/models/internvl/test_internvl_family_plugin_weights.py @@ -9,7 +9,8 @@ from __future__ import annotations - +import json +from unittest.mock import patch from tests.builder.family_plugin_test_support import ( ModelConfig, @@ -265,6 +266,94 @@ def test_bundle_config_overrides_flatten_text_decoder(self, tmp_path): "bos_token_id": 151643, } + def test_mock_bundle_serializes_decoder_geometry_at_top_level(self, tmp_path): + """Mock engines while exercising the real InternVL bundle boundary.""" + from tensorrt_model_connect.engine_builder import build_bundle + from tensorrt_model_connect.families.internvl.plugin import ( + plugin as production_plugin, + ) + + source_config = { + "model_type": "internvl", + "text_config": { + "vocab_size": 151674, + "hidden_size": 1536, + "num_hidden_layers": 28, + "num_attention_heads": 12, + "num_key_value_heads": 2, + "head_dim": 128, + "bos_token_id": 151643, + }, + "vision_config": { + "hidden_size": 1024, + "num_hidden_layers": 24, + "patch_size": 14, + }, + } + _write_config(tmp_path, source_config) + + class MockInternVLPlugin: + name = "internvl" + runtime_strategy = "internvl_vision_language" + requires_tokenizer = False + + @staticmethod + def load_weights(_model_dir, _config): + return {} + + @staticmethod + def build_engine(_config, _weights, _max_cache_length, **_kwargs): + return b"MOCK_DECODER_PLAN" + + @staticmethod + def build_vision_engine(_model_dir, _config, _weights, **_kwargs): + return b"MOCK_VISION_PLAN" + + @staticmethod + def get_vl_config(config): + return production_plugin.get_vl_config(config) + + @staticmethod + def get_bundle_config_overrides(config): + return production_plugin.get_bundle_config_overrides(config) + + with ( + patch( + "tensorrt_model_connect.engine_builder.find_plugin", + return_value=MockInternVLPlugin(), + ), + patch( + "tensorrt_model_connect.engine_builder._get_trt_version", + return_value="11.1.0", + ), + patch( + "tensorrt_model_connect.engine_builder._get_gpu_name", + return_value="CPU unit mock", + ), + patch("tensorrt_model_connect.engine_builder.write_bundle") as write_bundle, + ): + build_bundle( + str(tmp_path), + str(tmp_path / "internvl.bundle"), + max_cache_length=256, + ) + + sections = {section.name: section.data for section in write_bundle.call_args.args[2]} + runtime_config = json.loads(sections["config.json"]) + assert runtime_config["text_config"] == source_config["text_config"] + assert { + key: runtime_config[key] + for key in ( + "vocab_size", + "hidden_size", + "num_hidden_layers", + "num_attention_heads", + "num_key_value_heads", + "head_dim", + "bos_token_id", + ) + } == source_config["text_config"] + def test_no_vl_config_without_vision(self, tmp_path): """get_vl_config returns None when no vision_config present.""" from tensorrt_model_connect.families.internvl import plugin