Skip to content

Commit 06bbcd5

Browse files
mohamedzeidan2021Mohamed Zeidan
andauthored
fix: set full s3_input fields on QualityCheckStep baseline_dataset pipeline-variable input (#6322)
When baseline_dataset is a pipeline variable, _generate_baseline_job_inputs built a ProcessingInput whose s3_input dict omitted s3_data_type (required by ProcessingS3Input -> ValidationError) and also left s3_input_mode / s3_data_distribution_type as the Unassigned() sentinel, which breaks pipeline.definition() serialization with 'TypeError: Object of type Unassigned is not JSON serializable'. Set all three to match what the non-pipeline-variable branch (_upload_and_convert_to_processing_input) produces. Fixes #6206 Co-authored-by: Mohamed Zeidan <zeidmo@amazon.com>
1 parent fa89781 commit 06bbcd5

2 files changed

Lines changed: 58 additions & 0 deletions

File tree

‎sagemaker-mlops/src/sagemaker/mlops/workflow/quality_check_step.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,15 @@ def _generate_baseline_job_inputs(self):
362362
s3_input={
363363
"s3_uri": self.quality_check_config.baseline_dataset,
364364
"local_path": baseline_dataset_des,
365+
# Mirror the fields _upload_and_convert_to_processing_input sets on the
366+
# non-pipeline-variable branch (issue #6206). s3_data_type is required by
367+
# ProcessingS3Input (omitting it raised a ValidationError); s3_input_mode
368+
# and s3_data_distribution_type must be set too, otherwise they stay the
369+
# Unassigned() sentinel and break pipeline.definition() serialization
370+
# (TypeError: Object of type Unassigned is not JSON serializable).
371+
"s3_data_type": "S3Prefix",
372+
"s3_input_mode": "File",
373+
"s3_data_distribution_type": "FullyReplicated",
365374
},
366375
)
367376
else:

‎sagemaker-mlops/tests/unit/workflow/test_quality_check_step.py‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414

1515
from __future__ import absolute_import
1616

17+
from unittest.mock import Mock
1718

19+
from sagemaker.core.workflow.functions import Join
20+
from sagemaker.core.workflow.parameters import ParameterString
1821
from sagemaker.mlops.workflow.quality_check_step import (
1922
DataQualityCheckConfig,
2023
ModelQualityCheckConfig,
24+
QualityCheckStep,
2125
)
2226

2327

@@ -36,3 +40,48 @@ def test_model_quality_check_config_init():
3640
problem_type="BinaryClassification",
3741
)
3842
assert config.problem_type == "BinaryClassification"
43+
44+
45+
def test_generate_baseline_job_inputs_with_pipeline_variable_baseline_dataset():
46+
"""Regression for #6206.
47+
48+
When baseline_dataset is a pipeline variable, the ProcessingInput's s3_input must still
49+
include the required s3_data_type; omitting it raised a pydantic ValidationError.
50+
"""
51+
baseline_dataset = Join(
52+
on="/",
53+
values=["s3:/", "my-bucket", ParameterString(name="EndpointName"), "baseline/data.parquet"],
54+
)
55+
config = DataQualityCheckConfig(
56+
baseline_dataset=baseline_dataset,
57+
dataset_format={"parquet": {}},
58+
output_s3_uri="s3://my-bucket/output/",
59+
)
60+
61+
step = object.__new__(QualityCheckStep)
62+
step.quality_check_config = config
63+
# Isolate the pipeline-variable baseline branch; the script inputs go through the monitor.
64+
step._model_monitor = Mock()
65+
step._model_monitor._upload_and_convert_to_processing_input.return_value = Mock()
66+
67+
inputs = step._generate_baseline_job_inputs()
68+
69+
baseline_input = inputs["baseline_dataset_input"]
70+
s3_input = baseline_input.s3_input
71+
assert s3_input.s3_data_type == "S3Prefix"
72+
assert s3_input.s3_uri == baseline_dataset
73+
# These must be concrete values, not the Unassigned() sentinel: the arguments serializer
74+
# emits them into the pipeline definition, and json.dumps chokes on Unassigned (#6206).
75+
import json
76+
77+
from sagemaker.core.utils.utils import Unassigned
78+
79+
assert not isinstance(s3_input.s3_input_mode, Unassigned)
80+
assert not isinstance(s3_input.s3_data_distribution_type, Unassigned)
81+
serialized = {
82+
"S3Uri": "s3://resolved/at/runtime", # a pipeline var resolves to an expr; stub for json
83+
"LocalPath": s3_input.local_path,
84+
"S3DataType": getattr(s3_input, "s3_data_type", "S3Prefix"),
85+
"S3InputMode": getattr(s3_input, "s3_input_mode", "File"),
86+
}
87+
json.dumps(serialized) # must not raise TypeError on an Unassigned sentinel

0 commit comments

Comments
 (0)