@@ -257,6 +257,60 @@ def test_model_trainer_param_validation(test_case, modules_session):
257257 assert trainer .base_job_name == DEFAULT_BASE_NAME
258258
259259
260+ def test_pipeline_session_request_keeps_training_job_name (modules_session ):
261+ """Regression for #5776 / #6299.
262+
263+ Under a PipelineSession the request must KEEP TrainingJobName. The TrainingStep strips it via
264+ trim_request_dict (dropped by default, prefix preserved when use_custom_job_prefix=True);
265+ popping it here left use_custom_job_prefix nothing to preserve.
266+ """
267+ from unittest .mock import Mock
268+ from sagemaker .core .workflow .pipeline_context import PipelineSession
269+
270+ session = Mock (spec = PipelineSession )
271+ session .default_bucket .return_value = DEFAULT_BUCKET
272+ session .default_bucket_prefix = DEFAULT_BUCKET_PREFIX
273+ session .boto_region_name = DEFAULT_REGION
274+ session .sagemaker_config = {}
275+
276+ trainer = ModelTrainer (
277+ training_image = DEFAULT_IMAGE ,
278+ role = DEFAULT_ROLE ,
279+ compute = DEFAULT_COMPUTE_CONFIG ,
280+ stopping_condition = DEFAULT_STOPPING_CONDITION ,
281+ output_data_config = DEFAULT_OUTPUT_DATA_CONFIG ,
282+ base_job_name = "my-prefix" ,
283+ sagemaker_session = session ,
284+ )
285+
286+ args = trainer ._create_training_job_args (input_data_config = [])
287+
288+ assert "TrainingJobName" in args , "TrainingJobName must survive into the pipeline request"
289+ assert args ["TrainingJobName" ].startswith ("my-prefix" )
290+
291+ # The generated name must use a timestamp format base_from_name can strip, otherwise
292+ # trim_request_dict bakes a definition-time timestamp into the pipeline definition
293+ # instead of preserving the base prefix.
294+ from sagemaker .core .common_utils import base_from_name
295+
296+ assert base_from_name (args ["TrainingJobName" ]) == "my-prefix"
297+
298+ # End-to-end: with use_custom_job_prefix=True, trim_request_dict must leave exactly the base.
299+ from types import SimpleNamespace
300+ from sagemaker .core .workflow .utilities import trim_request_dict
301+ from sagemaker .core .workflow .pipeline_definition_config import PipelineDefinitionConfig
302+
303+ custom_prefix_config = SimpleNamespace (
304+ pipeline_definition_config = PipelineDefinitionConfig (use_custom_job_prefix = True )
305+ )
306+ trimmed = trim_request_dict (dict (args ), "TrainingJobName" , custom_prefix_config )
307+ assert trimmed ["TrainingJobName" ] == "my-prefix"
308+
309+ # And by default (no config) the key is dropped entirely.
310+ dropped = trim_request_dict (dict (args ), "TrainingJobName" , None )
311+ assert "TrainingJobName" not in dropped
312+
313+
260314@patch ("sagemaker.train.model_trainer.TrainingJob" )
261315@patch ("sagemaker.train.model_trainer.ModelTrainer.create_input_data_channel" )
262316def test_sm_drivers_channel_ignores_user_ignore_patterns (
@@ -1105,7 +1159,7 @@ def test_remote_debug_config(mock_training_job, modules_session):
11051159 )
11061160
11071161
1108- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1162+ @patch ("sagemaker.train.model_trainer.name_from_base " )
11091163@patch ("sagemaker.train.model_trainer.TrainingJob" )
11101164def test_model_trainer_full_init (mock_training_job , mock_unique_name , modules_session ):
11111165 def mock_upload_data (path , bucket , key_prefix , extra_args = None ):
@@ -1381,7 +1435,7 @@ def mock_upload_data(path, bucket, key_prefix, extra_args=None):
13811435
13821436# TODO: Re-Enable after local mode fully migrated to v3
13831437# @patch("sagemaker.train.model_trainer._LocalContainer")
1384- # @patch("sagemaker.train.model_trainer._get_unique_name ")
1438+ # @patch("sagemaker.train.model_trainer.name_from_base ")
13851439# @patch("sagemaker.train.local.local_container.download_folder")
13861440# def test_model_trainer_local_full_init(
13871441# mock_download_folder, mock_unique_name, mock_local_container, modules_session
@@ -1637,7 +1691,7 @@ def test_hyperparameters_invalid(mock_exists, modules_session):
16371691 )
16381692
16391693
1640- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1694+ @patch ("sagemaker.train.model_trainer.name_from_base " )
16411695@patch ("sagemaker.train.model_trainer.TrainingJob" )
16421696def test_model_trainer_default_paths (mock_training_job , mock_unique_name , modules_session ):
16431697 def mock_upload_data (path , bucket , key_prefix , extra_args = None ):
@@ -1828,7 +1882,7 @@ def test_metric_definitions(mock_training_job, modules_session):
18281882 )
18291883
18301884
1831- @patch ("sagemaker.train.model_trainer._get_unique_name " )
1885+ @patch ("sagemaker.train.model_trainer.name_from_base " )
18321886@patch ("sagemaker.core.resources.TrainingJob" )
18331887def test_nova_recipe (mock_training_job , mock_unique_name , modules_session ):
18341888 def mock_upload_data (path , bucket , key_prefix , extra_args = None ):
@@ -2203,7 +2257,7 @@ def test_nova_recipe_model_package_config_only_mpg_from_recipe(modules_session):
22032257 os .unlink (recipe .name )
22042258
22052259
2206- @patch ("sagemaker.train.model_trainer._get_unique_name " )
2260+ @patch ("sagemaker.train.model_trainer.name_from_base " )
22072261@patch ("sagemaker.train.model_trainer.TrainingJob" )
22082262def test_llmft_recipe (mock_training_job , mock_unique_name , modules_session ):
22092263 def mock_upload_data (path , bucket , key_prefix , extra_args = None ):
0 commit comments