@@ -254,6 +254,60 @@ def test_model_trainer_param_validation(test_case, modules_session):
254254 assert trainer .base_job_name == DEFAULT_BASE_NAME
255255
256256
257+ def test_pipeline_session_request_keeps_training_job_name (modules_session ):
258+ """Regression for #5776 / #6299.
259+
260+ Under a PipelineSession the request must KEEP TrainingJobName. The TrainingStep strips it via
261+ trim_request_dict (dropped by default, prefix preserved when use_custom_job_prefix=True);
262+ popping it here left use_custom_job_prefix nothing to preserve.
263+ """
264+ from unittest .mock import Mock
265+ from sagemaker .core .workflow .pipeline_context import PipelineSession
266+
267+ session = Mock (spec = PipelineSession )
268+ session .default_bucket .return_value = DEFAULT_BUCKET
269+ session .default_bucket_prefix = DEFAULT_BUCKET_PREFIX
270+ session .boto_region_name = DEFAULT_REGION
271+ session .sagemaker_config = {}
272+
273+ trainer = ModelTrainer (
274+ training_image = DEFAULT_IMAGE ,
275+ role = DEFAULT_ROLE ,
276+ compute = DEFAULT_COMPUTE_CONFIG ,
277+ stopping_condition = DEFAULT_STOPPING_CONDITION ,
278+ output_data_config = DEFAULT_OUTPUT_DATA_CONFIG ,
279+ base_job_name = "my-prefix" ,
280+ sagemaker_session = session ,
281+ )
282+
283+ args = trainer ._create_training_job_args (input_data_config = [])
284+
285+ assert "TrainingJobName" in args , "TrainingJobName must survive into the pipeline request"
286+ assert args ["TrainingJobName" ].startswith ("my-prefix" )
287+
288+ # The generated name must use a timestamp format base_from_name can strip, otherwise
289+ # trim_request_dict bakes a definition-time timestamp into the pipeline definition
290+ # instead of preserving the base prefix.
291+ from sagemaker .core .common_utils import base_from_name
292+
293+ assert base_from_name (args ["TrainingJobName" ]) == "my-prefix"
294+
295+ # End-to-end: with use_custom_job_prefix=True, trim_request_dict must leave exactly the base.
296+ from types import SimpleNamespace
297+ from sagemaker .core .workflow .utilities import trim_request_dict
298+ from sagemaker .core .workflow .pipeline_definition_config import PipelineDefinitionConfig
299+
300+ custom_prefix_config = SimpleNamespace (
301+ pipeline_definition_config = PipelineDefinitionConfig (use_custom_job_prefix = True )
302+ )
303+ trimmed = trim_request_dict (dict (args ), "TrainingJobName" , custom_prefix_config )
304+ assert trimmed ["TrainingJobName" ] == "my-prefix"
305+
306+ # And by default (no config) the key is dropped entirely.
307+ dropped = trim_request_dict (dict (args ), "TrainingJobName" , None )
308+ assert "TrainingJobName" not in dropped
309+
310+
257311@patch ("sagemaker.train.model_trainer.TrainingJob" )
258312def test_train_with_default_params (mock_training_job , model_trainer ):
259313 model_trainer .train ()
@@ -871,7 +925,7 @@ def test_remote_debug_config(mock_training_job, modules_session):
871925 )
872926
873927
874- @patch ("sagemaker.train.model_trainer._get_unique_name " )
928+ @patch ("sagemaker.train.model_trainer.name_from_base " )
875929@patch ("sagemaker.train.model_trainer.TrainingJob" )
876930def test_model_trainer_full_init (mock_training_job , mock_unique_name , modules_session ):
877931 def mock_upload_data (path , bucket , key_prefix ):
@@ -1147,7 +1201,7 @@ def mock_upload_data(path, bucket, key_prefix):
11471201
11481202# TODO: Re-Enable after local mode fully migrated to v3
11491203# @patch("sagemaker.train.model_trainer._LocalContainer")
1150- # @patch("sagemaker.train.model_trainer._get_unique_name ")
1204+ # @patch("sagemaker.train.model_trainer.name_from_base ")
11511205# @patch("sagemaker.train.local.local_container.download_folder")
11521206# def test_model_trainer_local_full_init(
11531207# mock_download_folder, mock_unique_name, mock_local_container, modules_session
@@ -1403,7 +1457,7 @@ def test_hyperparameters_invalid(mock_exists, modules_session):
14031457 )
14041458
14051459
1406- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1460+ @patch ("sagemaker.train.model_trainer.name_from_base " )
14071461@patch ("sagemaker.train.model_trainer.TrainingJob" )
14081462def test_model_trainer_default_paths (mock_training_job , mock_unique_name , modules_session ):
14091463 def mock_upload_data (path , bucket , key_prefix ):
@@ -1507,7 +1561,7 @@ def test_metric_definitions(mock_training_job, modules_session):
15071561 )
15081562
15091563
1510- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1564+ @patch ("sagemaker.train.model_trainer.name_from_base " )
15111565@patch ("sagemaker.core.resources.TrainingJob" )
15121566def test_nova_recipe (mock_training_job , mock_unique_name , modules_session ):
15131567 def mock_upload_data (path , bucket , key_prefix ):
@@ -1779,7 +1833,7 @@ def test_nova_recipe_model_package_config_only_mpg_from_recipe(modules_session):
17791833 os .unlink (recipe .name )
17801834
17811835
1782- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1836+ @patch ("sagemaker.train.model_trainer.name_from_base " )
17831837@patch ("sagemaker.train.model_trainer.TrainingJob" )
17841838def test_llmft_recipe (mock_training_job , mock_unique_name , modules_session ):
17851839 def mock_upload_data (path , bucket , key_prefix ):
0 commit comments