11# mypy: disable-error-code="attr-defined,no-untyped-call,assignment,call-arg,no-any-return,union-attr"
2+ import json
3+ import logging
24import os
35from typing import Any , Dict
46
1315)
1416from sagemaker .model_monitor .dataset_format import DatasetFormat
1517
18+ logger = logging .getLogger ()
19+ logger .setLevel (os .environ .get ("LOG_LEVEL" , "INFO" ))
20+ log = logging .getLogger (__name__ )
21+
1622SAGEMAKER_ROLE_ARN = os .environ ["SAGEMAKER_ROLE_ARN" ]
1723
1824sagemaker = boto3 .client ("sagemaker" )
1925
2026
2127def lambda_handler (event : Dict [str , Any ], context : Any ) -> Dict [str , Any ]:
2228 """Lambda handler for SageMaker Model Monitor baselining jobs."""
29+ logger .info (f"Received event: { json .dumps (event )} " )
30+
2331 try :
2432 action = event .get ("action" , "start" )
33+ logger .info (f"Action: { action } " )
2534
2635 if action == "start" :
2736 return start_baselining_job (event )
@@ -30,14 +39,18 @@ def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
3039
3140 return {"statusCode" : 200 }
3241 except KeyError as e :
42+ logger .error (f"Missing required parameter: { e } " )
3343 return {"statusCode" : 400 , "error" : f"Missing required parameter: { e } " }
3444 except Exception as e :
45+ logger .exception (f"Internal error: { str (e )} " )
3546 return {"statusCode" : 500 , "error" : f"Internal error: { str (e )} " }
3647
3748
3849def start_data_quality_baseline (training_data_uri : str , baseline_output_uri : str , event : Dict [str , Any ]) -> str :
3950 """Start data quality baseline job."""
4051 params = DataQualityParams (** event .get ("data_quality_params" , {}))
52+ logger .info (f"Data quality baseline params: { params .model_dump_json ()} " )
53+
4154 monitor = DefaultModelMonitor (
4255 role = SAGEMAKER_ROLE_ARN ,
4356 instance_count = params .instance_count ,
@@ -52,12 +65,16 @@ def start_data_quality_baseline(training_data_uri: str, baseline_output_uri: str
5265 wait = False ,
5366 logs = False ,
5467 )
55- return monitor .latest_baselining_job .job_name
68+ job_name = monitor .latest_baselining_job .job_name
69+ logger .info (f"Started data quality baseline job: { job_name } " )
70+ return job_name
5671
5772
5873def start_model_quality_baseline (training_data_uri : str , baseline_output_uri : str , event : Dict [str , Any ]) -> str :
5974 """Start model quality baseline job."""
6075 params = ModelQualityParams (** event .get ("model_quality_params" , {}))
76+ logger .info (f"Model quality baseline params: { params .model_dump_json ()} " )
77+
6178 monitor = ModelQualityMonitor (
6279 role = SAGEMAKER_ROLE_ARN ,
6380 instance_count = params .instance_count ,
@@ -76,14 +93,18 @@ def start_model_quality_baseline(training_data_uri: str, baseline_output_uri: st
7693 wait = False ,
7794 logs = False ,
7895 )
79- return monitor .latest_baselining_job .job_name
96+ job_name = monitor .latest_baselining_job .job_name
97+ logger .info (f"Started model quality baseline job: { job_name } " )
98+ return job_name
8099
81100
82101def start_model_bias_baseline (
83102 training_data_uri : str , baseline_output_uri : str , endpoint_name : str , event : Dict [str , Any ]
84103) -> str :
85104 """Start model bias baseline job."""
86105 params = ModelBiasParams (** event .get ("model_bias_params" , {}))
106+ logger .info (f"Model bias baseline params: { params .model_dump_json ()} " )
107+
87108 monitor = ModelBiasMonitor (role = SAGEMAKER_ROLE_ARN , max_runtime_in_seconds = params .max_runtime_seconds )
88109
89110 model_bias_data_config = DataConfig (
@@ -114,14 +135,18 @@ def start_model_bias_baseline(
114135 wait = False ,
115136 logs = False ,
116137 )
117- return monitor .latest_baselining_job .job_name
138+ job_name = monitor .latest_baselining_job .job_name
139+ logger .info (f"Started model bias baseline job: { job_name } " )
140+ return job_name
118141
119142
120143def start_model_explainability_baseline (
121144 training_data_uri : str , baseline_output_uri : str , endpoint_name : str , event : Dict [str , Any ]
122145) -> str :
123146 """Start model explainability baseline job."""
124147 params = ModelExplainabilityParams (** event .get ("model_explainability_params" , {}))
148+ logger .info (f"Model explainability baseline params: { params .model_dump_json ()} " )
149+
125150 monitor = ModelExplainabilityMonitor (role = SAGEMAKER_ROLE_ARN , max_runtime_in_seconds = params .max_runtime_seconds )
126151
127152 model_explainability_data_config = DataConfig (
@@ -149,7 +174,9 @@ def start_model_explainability_baseline(
149174 model_config = model_config ,
150175 explainability_config = shap_config ,
151176 )
152- return monitor .latest_baselining_job .job_name
177+ job_name = monitor .latest_baselining_job .job_name
178+ logger .info (f"Started model explainability baseline job: { job_name } " )
179+ return job_name
153180
154181
155182def start_baselining_job (event : Dict [str , Any ]) -> Dict [str , Any ]:
@@ -159,7 +186,13 @@ def start_baselining_job(event: Dict[str, Any]) -> Dict[str, Any]:
159186 endpoint_name = event ["endpoint_name" ]
160187 training_data_uri = event ["training_data_uri" ]
161188 baseline_output_uri = event ["baseline_output_uri" ]
189+
190+ logger .info (
191+ f"Starting baselining job - Monitor type: { monitor_type } , Endpoint: { endpoint_name } , "
192+ f"Training URI: { training_data_uri } , Output URI: { baseline_output_uri } "
193+ )
162194 except KeyError as e :
195+ logger .error (f"Missing required parameter: { e } " )
163196 return {"statusCode" : 400 , "error" : f"Missing required parameter: { e } " }
164197
165198 try :
@@ -173,29 +206,48 @@ def start_baselining_job(event: Dict[str, Any]) -> Dict[str, Any]:
173206 elif monitor_type == "model_explainability" :
174207 job_name = start_model_explainability_baseline (training_data_uri , baseline_output_uri , endpoint_name , event )
175208 else :
209+ logger .error (f"Unsupported monitor_type: { monitor_type } " )
176210 return {"statusCode" : 400 , "error" : f"Unsupported monitor_type: { monitor_type } " }
177211
212+ logger .info (f"Successfully started baselining job: { job_name } " )
178213 return {"statusCode" : 200 , "job_name" : job_name , "status" : "STARTED" }
179214 except Exception as e :
215+ logger .exception (f"Failed to start baseline job: { str (e )} " )
180216 return {"statusCode" : 500 , "error" : f"Failed to start baseline job: { str (e )} " }
181217
182218
183219def check_baselining_job (event : Dict [str , Any ]) -> Dict [str , Any ]:
184220 """Check the status of a baselining processing job."""
185221 try :
186222 job_name = event ["job_name" ]
223+ logger .info (f"Checking status for job: { job_name } " )
187224
188225 response = sagemaker .describe_processing_job (ProcessingJobName = job_name )
189226 status = response ["ProcessingJobStatus" ]
190-
191- return {
192- "statusCode" : 200 ,
193- "job_name" : job_name ,
194- "status" : "COMPLETED" if status == "Completed" else "IN_PROGRESS" ,
195- }
227+ logger .info (f"Job { job_name } status: { status } " )
228+
229+ # Map SageMaker processing job statuses to simplified states
230+ if status == "Completed" :
231+ logger .info (f"Job { job_name } completed successfully" )
232+ return {"statusCode" : 200 , "job_name" : job_name , "status" : "COMPLETED" }
233+ elif status in ["Failed" , "Stopped" , "Stopping" ]:
234+ failure_reason = response .get ("FailureReason" , "Unknown failure reason" )
235+ logger .error (f"Job { job_name } failed: { failure_reason } " )
236+ return {
237+ "statusCode" : 200 ,
238+ "job_name" : job_name ,
239+ "status" : "FAILED" ,
240+ "failure_reason" : failure_reason ,
241+ }
242+ else : # InProgress
243+ logger .info (f"Job { job_name } still in progress" )
244+ return {"statusCode" : 200 , "job_name" : job_name , "status" : "IN_PROGRESS" }
196245 except KeyError as e :
246+ logger .error (f"Missing required parameter: { e } " )
197247 return {"statusCode" : 400 , "error" : f"Missing required parameter: { e } " }
198248 except sagemaker .exceptions .ResourceNotFound :
249+ logger .error (f"Processing job not found: { event .get ('job_name' , 'unknown' )} " )
199250 return {"statusCode" : 404 , "error" : f"Processing job not found: { event .get ('job_name' , 'unknown' )} " }
200251 except Exception as e :
252+ logger .exception (f"Failed to check job status: { str (e )} " )
201253 return {"statusCode" : 500 , "error" : f"Failed to check job status: { str (e )} " }
0 commit comments