fix: generate repack step for ModelBuilder.register/build in ModelStep (#5828, #5829) - #6338
mohamedzeidan2021 wants to merge 3 commits into
Conversation
…ws#5828, aws#5829) In v2, Model.register()/create() placed the Model instance (carrying sagemaker_session, role, model_data, entry_point, source_dir, ...) into the pipeline context, so ModelStep repacked it. In v3, ModelBuilder.register()/build() place the ModelBuilder into the context, but ModelStep._append_repack_model_step only accepted sagemaker.core.resources.Model, so a ModelBuilder fell through to 'No models to repack' and the model was never repacked with the user's source_code (aws#5828). The v3 core Model is a pydantic model with extra='forbid' and no sagemaker_session field, so it could never satisfy ModelStep's reads (aws#5829). Make the repack path ModelBuilder-aware: accept a ModelBuilder in the repack gate and map its attributes (model_name/role_arn/s3_model_data_url/source_code.requirements) to _RepackModelStep's parameters. Also stop passing v2-era args (dependencies/ output_path/output_kms_key) that are not part of the v3 _RepackModelStep signature and would leak into ModelTrainer; pass 'requirements' as the v3 step expects.
|
Claude finished @mohamedzeidan2021's task in 3m 27s —— View job Code Review — repack step for
|
jam-jee
left a comment
There was a problem hiding this comment.
Summary: The core fix is right and I could confirm each piece: runnable_by_pipeline puts the ModelBuilder in context.model (pipeline_context.py:346-350); ModelBuilder adds id(self) to need_runtime_repack only when entry_point is not None (model_builder.py:2722-2741, 2911-2917), so the new gate is sound and _RepackModelStep's os.path.basename(self._entry_point) is safe; and dropping dependencies/output_path/output_kms_key was necessary because _RepackModelStep forwards **kwargs into ModelTrainer, which is extra="forbid" — that's why the v3 repack path was dead. test_model_builder_register_appends_repack_step hits the #5828 path exactly. Two things need to change before merge.
Blocking
-
sagemaker-mlops/src/sagemaker/mlops/workflow/model_step.py:147—self._runtime_repack_output_prefixis now assigned but never used, andmodel_kms_keyis dropped entirely.ModelBuildercomputescontext.runtime_repack_output_prefix = s3://<bucket>/<key_prefix>(model_builder.py:2741) from the user's bucket /code_location, and carriesmodel_kms_key. After this PR the repacked artifact lands inModelTrainer's default bucket path, not encrypted with the user's key. For accounts with a mandated bucket or SSE-KMS bucket policies (the same population as #5956) the repack job either fails or violates policy, so "v2 parity" doesn't hold. It is wirable today through the same**kwargs:self._repack_model_step_settings.setdefault( "output_data_config", OutputDataConfig( s3_output_path=self._runtime_repack_output_prefix, kms_key_id=getattr(model, "model_kms_key", None), ), )
(
setdefaultso an explicit user override inrepack_model_step_settingswins.) If you'd rather defer it, remove the dead assignment and emit a warning; please don't drop it silently. -
model_step.py:250-258— the legacyModelbranch can't work in v3:sagemaker.core.resources.Modelis pydanticextra="forbid"with nosagemaker_session/role/entry_point(that is #5829 itself), and nothing in v3 puts a coreModelintostep_args.model(onlyModelBuilder.register/buildare@runnable_by_pipeline). KeepingModelin the gate makes such an object fail deeper (None.boto_region_name) instead of at the gate. Either dropModelfrom the gate, or document that branch as duck-typing only. Also, the PR routes around #5829 rather than fixing it — fine, but the description should say so rather than claim to close it.
Should-have
model_step.py:83-85docstring forrepack_model_step_settings: any v2-shaped key (output_path,output_kms_key,dependencies) now flows intoModelTrainer(extra="forbid")and raises. List what is accepted.source_code.requirementsisOptional[StrPipeVar]; aPipelineVariablehitsself._requirements.endswith(".txt")in_utils.pyand raises. Pre-existing, but this PR is the first to route real user values there; anisinstance(..., str)guard would be cheap.- Tests: missing the
build()/PrimaryContainerrewiring (model_step.py:296-302), a populatedvpc_config, and (with item 1) anoutput_data_configassertion.
Nit
model_step.py:228readsmodel.sagemaker_sessiondirectly while the siblings usegetattr(..., None).
CI attribution: integ-tests (sagemaker-mlops) test_deployment_steps_execute_end_to_end (expected exactly one EndpointConfigName for this run, got []) uses only EndpointConfigStep/EndpointStep/InferenceComponentStep, no ModelStep, and passed on #6330/#6339 today — eventual-consistency flake. integ-tests-us-east-1 is the Nova quota. codecov/project is partial-matrix (only mlops+serve ran; patch 81% > 70% target). Nothing red is caused by this PR.
|
Claude finished @mohamedzeidan2021's task in 1m 47s —— View job Code Review — repack step for
|
…with its KMS key _append_repack_model_step dropped output_path / output_kms_key when it stopped forwarding them to _RepackModelStep. Those were necessary to remove: neither is a named parameter of _RepackModelStep, so they fell into **kwargs and reached ModelTrainer, which is pydantic extra="forbid" -- the reason the v3 repack path was dead. But removing them left self._runtime_repack_output_prefix assigned and never read, and dropped model_kms_key entirely, so the repacked tarball silently landed in ModelTrainer's default bucket with no customer CMK. Accounts with a mandated bucket or an SSE-KMS bucket policy would either fail the repack job or write non-compliant artifacts -- the same population as aws#5956. Pass them the way ModelTrainer actually accepts: output_data_config is a declared ModelTrainer field, so an OutputDataConfig built from runtime_repack_output_prefix and the model's model_kms_key survives extra="forbid". setdefault so an explicit repack_model_step_settings override still wins. _repack_inputs_for now carries model_kms_key for both the ModelBuilder and the legacy Model shapes. Also documents why the core Model arm of the isinstance gate is legacy/duck-typed: no v3 @runnable_by_pipeline path produces a core Model, and that class has no sagemaker_session / role / entry_point (aws#5829). Adds two tests asserting the s3 path and KMS key reach _RepackModelStep, and that a user-supplied output_data_config is not overwritten. Without the wiring the first fails with KeyError: 'output_data_config'.
|
Claude finished @mohamedzeidan2021's task in 2m 10s —— View job Code Review — repack step for
|
Issues
Fixes #5828 and #5829.
In v2,
Model.register()/.create()placed the Model instance (carryingsagemaker_session,role,model_data,entry_point,source_dir, …) into the pipeline context, soModelStepgenerated a repack step and repacked the model with the user'ssource_code.In v3,
ModelBuilder.register()/.build()place the ModelBuilder into the pipeline context, butModelStep._append_repack_model_stepstill gated onisinstance(self._model, sagemaker.core.resources.Model). AModelBuilderfails that check, so the step logged "No models to repack" and no repack step was generated — the registered model was never repacked with the source code (#5828). Separately, the v3 coreModelis a pydantic model (extra="forbid") with nosagemaker_sessionfield, so it can never satisfy the attribute readsModelStepperforms (#5829). The result was a v3-only regression of working v2 behavior; the v3 repack path was effectively dead (it also still passed v2-era_RepackModelStepargs —dependencies/output_path/output_kms_key— that don't exist on the v3_RepackModelStepsignature and leaked intoModelTrainer).Fix
Make the repack path ModelBuilder-aware (additive; the legacy
Modelbranch is unchanged):(Model, ModelBuilder)._repack_inputs_for(model)maps aModelBuilder's attributes to_RepackModelStep's parameters (model_name→name,role_arn→role,s3_model_data_url→model_data,entry_point,source_dir,source_code.requirements→requirements).requirements=(what the v3_RepackModelStepexpects) and drop the v2-era args that were leaking intoModelTrainer.need_runtime_repackholdsid(model_builder), so the loop still iterates the raw builder for the identity match.Validation
sagemaker-mlops/tests/unit/workflow/test_model_step.py): new tests assert a repack step is generated and the containerModelDataUrlis rewired to the repacked artifact; and that an unrecognized model type still yields no repack step. Fails on master (0 repack steps), passes with the fix.ModelBuilder.register()→ModelStepproduced aRepackModelTraining step; the pipeline executed to Succeeded, the RepackModel step ran, and the registered model package's containerModelDataUrlpointed to the repacked artifact (differing from the original input).black/flake8clean; broadertests/unit/workflowsubset: 332 passed.Backwards compatibility
Additive — the existing core-
Modelrepack branch is unchanged, thebuild()dict path (model=None) is unaffected, and_resolve_repack_model_step_vpc_configsnow usesgetattr(..., "vpc_config", None)(safe for both types). No public API change.