@@ -157,15 +157,15 @@ def deploy(
157157 For Nova models, also creates a custom model deployment for inference.
158158
159159 Args:
160- job_name: Name for the model import job (non-Nova models only).
161- imported_model_name: Name for the imported model (non-Nova models only).
160+ job_name: Name for the model import job (OSS models only).
161+ imported_model_name: Name for the imported model (OSS models only).
162162 custom_model_name: Name for the custom model (Nova models only).
163163 role_arn: IAM role ARN with permissions for Bedrock operations.
164- job_tags: Tags for the import job (non-Nova models only).
165- imported_model_tags: Tags for the imported model (non-Nova models only).
164+ job_tags: Tags for the import job (OSS models only).
165+ imported_model_tags: Tags for the imported model (OSS models only).
166166 model_tags: Tags for the custom model (Nova models only).
167- client_request_token: Unique token for idempotency (non-Nova models only).
168- imported_model_kms_key_id: KMS key ID for encryption (non-Nova models only).
167+ client_request_token: Unique token for idempotency (OSS models only).
168+ imported_model_kms_key_id: KMS key ID for encryption (OSS models only).
169169 deployment_name: Name for the deployment (Nova models only). If not provided,
170170 defaults to custom_model_name suffixed with '-deployment'.
171171
@@ -238,15 +238,23 @@ def deploy(
238238 }
239239 params = {k : v for k , v in params .items () if v is not None }
240240
241- logger .info ("Creating model import job for non-Nova deployment" )
241+ logger .info ("Creating model import job for OSS model deployment" )
242242 print (f"[BedrockModelBuilder] Resolved S3 artifacts path: { self .s3_model_artifacts } " )
243243 print (f"[BedrockModelBuilder] create_model_import_job params: { params } " )
244- response = self ._get_bedrock_client ().create_model_import_job (** params )
244+ import_response = self ._get_bedrock_client ().create_model_import_job (** params )
245245 logger .warning (
246- "Bedrock create_model_import_job request: %s, response: %s" , params , response
246+ "Bedrock create_model_import_job request: %s, response: %s" , params , import_response
247247 )
248- _log_bedrock_api_call ("create_model_import_job" , params , response )
249- return response
248+ _log_bedrock_api_call ("create_model_import_job" , params , import_response )
249+
250+ job_arn = import_response .get ("jobArn" )
251+ self ._wait_for_import_job_complete (job_arn )
252+
253+ # Return the completed job details
254+ job_details = self ._get_bedrock_client ().get_model_import_job (
255+ jobIdentifier = job_arn
256+ )
257+ return job_details
250258
251259 def create_deployment (
252260 self ,
@@ -303,6 +311,140 @@ def create_deployment(
303311
304312 return response
305313
314+ def create_provisioned_throughput (
315+ self ,
316+ model_id : str ,
317+ provisioned_model_name : str ,
318+ model_units : int = 1 ,
319+ commitment_duration : Optional [str ] = None ,
320+ tags : Optional [list ] = None ,
321+ poll_interval : int = 60 ,
322+ max_wait : int = 3600 ,
323+ ) -> Dict [str , Any ]:
324+ """Create provisioned throughput for an imported model on Bedrock.
325+
326+ Calls CreateProvisionedModelThroughput and polls until the provisioned
327+ throughput reaches InService status.
328+
329+ Args:
330+ model_id: ARN or ID of the imported model.
331+ provisioned_model_name: Name for the provisioned throughput resource.
332+ model_units: Number of model units to provision. Defaults to 1.
333+ commitment_duration: Commitment duration. Valid values: 'OneMonth',
334+ 'SixMonths'. If not provided, no commitment is set (on-demand).
335+ tags: Tags for the provisioned throughput resource.
336+ poll_interval: Seconds between status checks. Defaults to 60.
337+ max_wait: Maximum seconds to wait. Defaults to 3600.
338+
339+ Returns:
340+ Response from Bedrock create_provisioned_model_throughput API.
341+
342+ Raises:
343+ RuntimeError: If the provisioned throughput fails or times out.
344+ ValueError: If model_id or provisioned_model_name is not provided.
345+ """
346+ if not model_id :
347+ raise ValueError ("model_id is required for create_provisioned_throughput." )
348+ if not provisioned_model_name :
349+ raise ValueError (
350+ "provisioned_model_name is required for create_provisioned_throughput."
351+ )
352+
353+ params = {
354+ "modelId" : model_id ,
355+ "provisionedModelName" : provisioned_model_name ,
356+ "modelUnits" : model_units ,
357+ }
358+ if commitment_duration :
359+ params ["commitmentDuration" ] = commitment_duration
360+ if tags :
361+ params ["tags" ] = tags
362+
363+ logger .info (
364+ "Creating provisioned throughput '%s' for model %s with %d model units" ,
365+ provisioned_model_name ,
366+ model_id ,
367+ model_units ,
368+ )
369+ response = self ._get_bedrock_client ().create_provisioned_model_throughput (** params )
370+
371+ provisioned_model_arn = response .get ("provisionedModelArn" )
372+ if provisioned_model_arn :
373+ self ._wait_for_provisioned_throughput_in_service (
374+ provisioned_model_arn , poll_interval = poll_interval , max_wait = max_wait
375+ )
376+
377+ return response
378+
379+ def _wait_for_import_job_complete (
380+ self , job_arn : str , poll_interval : int = 60 , max_wait : int = 3600
381+ ):
382+ """Poll Bedrock until the model import job reaches Completed status.
383+
384+ Args:
385+ job_arn: ARN of the model import job.
386+ poll_interval: Seconds between status checks. Defaults to 60.
387+ max_wait: Maximum seconds to wait. Defaults to 3600.
388+
389+ Raises:
390+ RuntimeError: If the import job fails or times out.
391+ """
392+ elapsed = 0
393+ status = None
394+ while elapsed < max_wait :
395+ resp = self ._get_bedrock_client ().get_model_import_job (jobIdentifier = job_arn )
396+ status = resp .get ("status" )
397+ logger .info ("Import job status: %s (elapsed %ds)" , status , elapsed )
398+ if status == "Completed" :
399+ return
400+ if status == "Failed" :
401+ failure_reason = resp .get ("failureMessage" , "Unknown" )
402+ raise RuntimeError (
403+ f"Model import job { job_arn } failed. Reason: { failure_reason } "
404+ )
405+ time .sleep (poll_interval )
406+ elapsed += poll_interval
407+ raise RuntimeError (
408+ f"Timed out after { max_wait } s waiting for import job { job_arn } to complete. "
409+ f"Last status: { status } "
410+ )
411+
412+ def _wait_for_provisioned_throughput_in_service (
413+ self , provisioned_model_arn : str , poll_interval : int = 60 , max_wait : int = 3600
414+ ):
415+ """Poll Bedrock until provisioned throughput reaches InService status.
416+
417+ Args:
418+ provisioned_model_arn: ARN of the provisioned model throughput.
419+ poll_interval: Seconds between status checks. Defaults to 60.
420+ max_wait: Maximum seconds to wait. Defaults to 3600.
421+
422+ Raises:
423+ RuntimeError: If the provisioned throughput fails or times out.
424+ """
425+ elapsed = 0
426+ status = None
427+ while elapsed < max_wait :
428+ resp = self ._get_bedrock_client ().get_provisioned_model_throughput (
429+ provisionedModelId = provisioned_model_arn
430+ )
431+ status = resp .get ("status" )
432+ logger .info ("Provisioned throughput status: %s (elapsed %ds)" , status , elapsed )
433+ if status == "InService" :
434+ return
435+ if status == "Failed" :
436+ failure_reason = resp .get ("failureMessage" , "Unknown" )
437+ raise RuntimeError (
438+ f"Provisioned throughput { provisioned_model_arn } failed. "
439+ f"Reason: { failure_reason } "
440+ )
441+ time .sleep (poll_interval )
442+ elapsed += poll_interval
443+ raise RuntimeError (
444+ f"Timed out after { max_wait } s waiting for provisioned throughput "
445+ f"{ provisioned_model_arn } to become InService. Last status: { status } "
446+ )
447+
306448 def _wait_for_model_active (
307449 self , model_arn : str , poll_interval : int = 60 , max_wait : int = 3600
308450 ):
0 commit comments