From 4d31ba5ddd2af050f32c38a66093623f50af98a6 Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Thu, 30 Jul 2026 16:20:54 +0000 Subject: [PATCH 1/2] autofill model name fix --- src/model_config_utils.cc | 11 ++- src/test/repo_agent_test.cc | 165 +++++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/src/model_config_utils.cc b/src/model_config_utils.cc index 85632e966..e74cee432 100644 --- a/src/model_config_utils.cc +++ b/src/model_config_utils.cc @@ -1259,17 +1259,22 @@ AutoCompleteBackendFields( // When we know the backend is PyTorch, we set the platform and default model // filename as necessary. if (config->backend() == kPyTorchBackend) { + // A non-default runtime resolves its own model file. The Python-based + // PyTorch runtime ("model.py") treats a sibling "model.pt" as the weights + // companion to the model class, so autofilling this field would make it + // load the weights file as the model itself. + const bool autofill_filename = config->default_model_filename().empty() && + (config->runtime() != kPythonFilename); if (config->platform().empty()) { // The default platform for the PyTorch backend is LibTorch until it is // deprecated. AOTI must be explicitly specified to maximize backwards // compatibility. config->set_platform(kPyTorchLibTorchPlatform); - if (config->default_model_filename().empty()) { + if (autofill_filename) { config->set_default_model_filename(kPyTorchLibTorchFilename); } } else if ( - config->platform() == kPyTorchAotiPlatform && - config->default_model_filename().empty()) { + (config->platform() == kPyTorchAotiPlatform) && autofill_filename) { config->set_default_model_filename(kPyTorchAotiFilename); } return Status::Success; diff --git a/src/test/repo_agent_test.cc b/src/test/repo_agent_test.cc index 40de0e4b3..5f0d323d4 100644 --- a/src/test/repo_agent_test.cc +++ b/src/test/repo_agent_test.cc @@ -1,4 +1,4 @@ -// Copyright 2021-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions @@ -28,14 +28,18 @@ #include #include +#include +#include #include #include #include #include #include +#include "constants.h" #include "filesystem/api.h" #include "gtest/gtest.h" +#include "model_config_utils.h" #include "server_message.h" #include "shared_library.h" @@ -2359,6 +2363,165 @@ TEST_F(TritonRepoAgentAPITest, TRITONREPOAGENT_AgentState) } } + +// Exercises AutoCompleteBackendFields() against on-disk model layouts. The +// PyTorch cases pin the contract that a model using the Python-based runtime +// ("model.py") never receives an autofilled default_model_filename: such a +// model resolves its own file and treats a sibling "model.pt"/"model.pt2" as +// the weights companion to the model class, not as the model itself. +class AutoCompleteBackendFieldsTest : public ::testing::Test { + protected: + void SetUp() override + { + char dir_template[] = "/tmp/model_config_autofill_XXXXXX"; + char* dir = mkdtemp(dir_template); + ASSERT_NE(dir, nullptr); + model_path_ = dir; + version_path_ = model_path_ + "/1"; + ASSERT_TRUE(std::filesystem::create_directory(version_path_)); + } + + void TearDown() override { std::filesystem::remove_all(model_path_); } + + void TouchVersionFile(const std::string& filename) + { + std::ofstream file(version_path_ + "/" + filename); + file << "content"; + ASSERT_TRUE(file.good()); + } + + void AutoComplete(inference::ModelConfig* config) + { + tc::Status status = + tc::AutoCompleteBackendFields("test_model", model_path_, config); + ASSERT_TRUE(status.IsOk()) << status.Message(); + } + + std::string model_path_; + std::string version_path_; +}; + +// A model with backend "pytorch" + runtime "model.py" and both "model.py" +// and a weights "model.pt" in the version directory must not have "model.pt" +// autofilled as the default model filename, otherwise the runtime attempts +// torch.jit.load() on the weights file. +TEST_F(AutoCompleteBackendFieldsTest, PythonRuntimeLibTorchKeepsFilenameEmpty) +{ + TouchVersionFile(tc::kPythonFilename); + TouchVersionFile(tc::kPyTorchLibTorchFilename); + + inference::ModelConfig config; + config.set_backend(tc::kPyTorchBackend); + config.set_runtime(tc::kPythonFilename); + + AutoComplete(&config); + + EXPECT_EQ(config.backend(), tc::kPyTorchBackend); + EXPECT_EQ(config.platform(), tc::kPyTorchLibTorchPlatform); + EXPECT_EQ(config.default_model_filename(), ""); +} + +TEST_F(AutoCompleteBackendFieldsTest, PythonRuntimeAotiKeepsFilenameEmpty) +{ + TouchVersionFile(tc::kPythonFilename); + TouchVersionFile(tc::kPyTorchAotiFilename); + + inference::ModelConfig config; + config.set_backend(tc::kPyTorchBackend); + config.set_platform(tc::kPyTorchAotiPlatform); + config.set_runtime(tc::kPythonFilename); + + AutoComplete(&config); + + EXPECT_EQ(config.platform(), tc::kPyTorchAotiPlatform); + EXPECT_EQ(config.default_model_filename(), ""); +} + +// Default-runtime models keep the pre-existing autofill behavior. +TEST_F(AutoCompleteBackendFieldsTest, LibTorchBackendAutofillsFilename) +{ + TouchVersionFile(tc::kPyTorchLibTorchFilename); + + inference::ModelConfig config; + config.set_backend(tc::kPyTorchBackend); + + AutoComplete(&config); + + EXPECT_EQ(config.platform(), tc::kPyTorchLibTorchPlatform); + EXPECT_EQ(config.default_model_filename(), tc::kPyTorchLibTorchFilename); +} + +TEST_F(AutoCompleteBackendFieldsTest, AotiPlatformAutofillsFilename) +{ + TouchVersionFile(tc::kPyTorchAotiFilename); + + inference::ModelConfig config; + config.set_backend(tc::kPyTorchBackend); + config.set_platform(tc::kPyTorchAotiPlatform); + + AutoComplete(&config); + + EXPECT_EQ(config.default_model_filename(), tc::kPyTorchAotiFilename); +} + +// An explicit user-provided filename is never overwritten, runtime or not. +TEST_F(AutoCompleteBackendFieldsTest, ExplicitFilenamePreserved) +{ + TouchVersionFile(tc::kPythonFilename); + TouchVersionFile("custom.pt"); + + inference::ModelConfig config; + config.set_backend(tc::kPyTorchBackend); + config.set_runtime(tc::kPythonFilename); + config.set_default_model_filename("custom.pt"); + + AutoComplete(&config); + + EXPECT_EQ(config.default_model_filename(), "custom.pt"); +} + +// With no backend/platform given, the version directory is inspected. +TEST_F(AutoCompleteBackendFieldsTest, SniffLibTorchFromVersionDir) +{ + TouchVersionFile(tc::kPyTorchLibTorchFilename); + + inference::ModelConfig config; + + AutoComplete(&config); + + EXPECT_EQ(config.backend(), tc::kPyTorchBackend); + EXPECT_EQ(config.platform(), tc::kPyTorchLibTorchPlatform); + EXPECT_EQ(config.default_model_filename(), ""); +} + +TEST_F(AutoCompleteBackendFieldsTest, SniffAotiFromVersionDir) +{ + TouchVersionFile(tc::kPyTorchAotiFilename); + + inference::ModelConfig config; + + AutoComplete(&config); + + EXPECT_EQ(config.backend(), tc::kPyTorchAotiBackend); + EXPECT_EQ(config.platform(), tc::kPyTorchAotiPlatform); + EXPECT_EQ(config.default_model_filename(), tc::kPyTorchAotiFilename); +} + +// The alternate "libtorch" platform spelling selects the PyTorch backend and +// does not trigger filename autofill (platform is non-empty and not AOTI). +TEST_F(AutoCompleteBackendFieldsTest, AltLibTorchPlatformSetsBackend) +{ + TouchVersionFile(tc::kPyTorchLibTorchFilename); + + inference::ModelConfig config; + config.set_platform(tc::kPyTorchLibTorchPlatformAlt); + + AutoComplete(&config); + + EXPECT_EQ(config.backend(), tc::kPyTorchBackend); + EXPECT_EQ(config.default_model_filename(), ""); +} + } // namespace int From cb33b0492e7777896b978c89d352481956f33e4a Mon Sep 17 00:00:00 2001 From: Matthew Wittwer Date: Wed, 5 Aug 2026 00:14:09 +0000 Subject: [PATCH 2/2] update use_autofill_filename name --- src/model_config_utils.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/model_config_utils.cc b/src/model_config_utils.cc index e74cee432..e3e56f1da 100644 --- a/src/model_config_utils.cc +++ b/src/model_config_utils.cc @@ -1263,18 +1263,19 @@ AutoCompleteBackendFields( // PyTorch runtime ("model.py") treats a sibling "model.pt" as the weights // companion to the model class, so autofilling this field would make it // load the weights file as the model itself. - const bool autofill_filename = config->default_model_filename().empty() && - (config->runtime() != kPythonFilename); + const bool use_autofill_filename = + config->default_model_filename().empty() && + (config->runtime() != kPythonFilename); if (config->platform().empty()) { // The default platform for the PyTorch backend is LibTorch until it is // deprecated. AOTI must be explicitly specified to maximize backwards // compatibility. config->set_platform(kPyTorchLibTorchPlatform); - if (autofill_filename) { + if (use_autofill_filename) { config->set_default_model_filename(kPyTorchLibTorchFilename); } } else if ( - (config->platform() == kPyTorchAotiPlatform) && autofill_filename) { + (config->platform() == kPyTorchAotiPlatform) && use_autofill_filename) { config->set_default_model_filename(kPyTorchAotiFilename); } return Status::Success;