From 03e2b1a07a2210686babf97f76277387296ba85a Mon Sep 17 00:00:00 2001 From: Adam Ivison Date: Fri, 17 Apr 2026 08:09:51 +0000 Subject: [PATCH 1/2] Fix incorrect method name to load hyperparameters from file file --- sagemaker-train/src/sagemaker/train/model_trainer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sagemaker-train/src/sagemaker/train/model_trainer.py b/sagemaker-train/src/sagemaker/train/model_trainer.py index d07edeb025..141b8af930 100644 --- a/sagemaker-train/src/sagemaker/train/model_trainer.py +++ b/sagemaker-train/src/sagemaker/train/model_trainer.py @@ -1214,7 +1214,7 @@ def from_recipe( ) if is_nova: if hyperparameters and isinstance(hyperparameters, str): - hyperparameters = cls._validate_and_load_hyperparameters_file(hyperparameters) + hyperparameters = cls._validate_and_fetch_hyperparameters_file(hyperparameters) model_trainer_args["hyperparameters"].update(hyperparameters) elif hyperparameters and isinstance(hyperparameters, dict): model_trainer_args["hyperparameters"].update(hyperparameters) From e5ab355fa583acaa101a779fd348a7590cebcf72 Mon Sep 17 00:00:00 2001 From: Mohamed Zeidan Date: Mon, 5 Oct 2026 11:15:51 -0700 Subject: [PATCH 2/2] test(train): cover from_recipe Nova hyperparameters file path Regression tests for the AttributeError in #5770: from_recipe() called a non-existent _validate_and_load_hyperparameters_file when hyperparameters was a path string. Cover JSON and YAML files, the dict path, and a missing file, all of which fail with AttributeError before the one-line fix. --- .../tests/unit/train/test_model_trainer.py | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/sagemaker-train/tests/unit/train/test_model_trainer.py b/sagemaker-train/tests/unit/train/test_model_trainer.py index a002b556b6..8c79905b3b 100644 --- a/sagemaker-train/tests/unit/train/test_model_trainer.py +++ b/sagemaker-train/tests/unit/train/test_model_trainer.py @@ -1773,6 +1773,109 @@ def test_nova_recipe_with_distillation(modules_session): os.unlink(recipe.name) +@pytest.mark.parametrize( + "suffix, dump", + [(".json", json.dumps), (".yaml", yaml.dump)], +) +def test_nova_recipe_with_hyperparameters_file(suffix, dump, modules_session): + """Hyperparameters passed as a file path are loaded and merged into the recipe's.""" + recipe_data = { + "run": { + "name": "dummy-model", + "model_type": "amazon.nova", + "model_name_or_path": "dummy-model", + } + } + hyperparameters = {"param1": "value1", "param2": 2} + + with NamedTemporaryFile(suffix=".yaml", delete=False) as recipe: + with open(recipe.name, "w") as file: + yaml.dump(recipe_data, file) + + with NamedTemporaryFile(suffix=suffix, delete=False, mode="w") as hp_file: + hp_file.write(dump(hyperparameters)) + + try: + trainer = ModelTrainer.from_recipe( + training_recipe=recipe.name, + role=DEFAULT_ROLE, + sagemaker_session=modules_session, + compute=DEFAULT_COMPUTE_CONFIG, + training_image=DEFAULT_IMAGE, + hyperparameters=hp_file.name, + ) + + assert trainer._is_nova_recipe + assert trainer.hyperparameters == { + "base_model": "dummy-model", + "param1": "value1", + "param2": 2, + } + finally: + os.unlink(hp_file.name) + os.unlink(recipe.name) + + +def test_nova_recipe_with_hyperparameters_dict(modules_session): + """Hyperparameters passed as a dict are merged into the recipe's.""" + recipe_data = { + "run": { + "name": "dummy-model", + "model_type": "amazon.nova", + "model_name_or_path": "dummy-model", + } + } + + with NamedTemporaryFile(suffix=".yaml", delete=False) as recipe: + with open(recipe.name, "w") as file: + yaml.dump(recipe_data, file) + + try: + trainer = ModelTrainer.from_recipe( + training_recipe=recipe.name, + role=DEFAULT_ROLE, + sagemaker_session=modules_session, + compute=DEFAULT_COMPUTE_CONFIG, + training_image=DEFAULT_IMAGE, + hyperparameters={"param1": "value1"}, + ) + + assert trainer.hyperparameters == { + "base_model": "dummy-model", + "param1": "value1", + } + finally: + os.unlink(recipe.name) + + +def test_nova_recipe_with_missing_hyperparameters_file(modules_session): + """A hyperparameters path that does not exist raises a ValueError.""" + recipe_data = { + "run": { + "name": "dummy-model", + "model_type": "amazon.nova", + "model_name_or_path": "dummy-model", + } + } + + with NamedTemporaryFile(suffix=".yaml", delete=False) as recipe: + with open(recipe.name, "w") as file: + yaml.dump(recipe_data, file) + + try: + with pytest.raises(ValueError, match="Hyperparameters file not found"): + ModelTrainer.from_recipe( + training_recipe=recipe.name, + role=DEFAULT_ROLE, + sagemaker_session=modules_session, + compute=DEFAULT_COMPUTE_CONFIG, + training_image=DEFAULT_IMAGE, + hyperparameters="nonexistent.json", + ) + finally: + os.unlink(recipe.name) + + def test_nova_recipe_with_model_package_arn(modules_session): """Test that MP ARN in model_name_or_path routes to ModelPackageConfig.""" mp_arn = "arn:aws:sagemaker:us-east-1:123456789012:model-package/my-mpg/1"