Source code for ads.model.deployment.model_deployer

#!/usr/bin/env python
# -*- coding: utf-8; -*-

# Copyright (c) 2021, 2023 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
"""
APIs to interact with Oracle's Model Deployment service.

There are three main classes: ModelDeployment, ModelDeploymentDetails, ModelDeployer.

One creates a ModelDeployment and deploys it under the umbrella of the ModelDeployer class. This way
multiple ModelDeployments can be unified with one ModelDeployer. The ModelDeployer class also serves
as the interface to all the deployments. ModelDeploymentDetails holds information about the particular
details of a particular deployment, such as how many instances, etc. In this way multiple, independent
ModelDeployments with the same details can be created using the ModelDeployer class.

Examples
--------
>>> from model_deploy.model_deployer import ModelDeployer, ModelDeploymentDetails
>>> deployer = ModelDeployer("model_dep_conf.yaml")
>>> deployment_properties = ModelDeploymentProperties(
...             'ocid1.datasciencemodel.ocn.reg.xxxxxxxxxxxxxxxxxxxxxxxxx')
...                 .with_prop('display_name', "My model display name")
...                 .with_prop("project_id", project_id)
...                 .with_prop("compartment_id", compartment_id)
...                 .with_instance_configuration(
...                     config={"INSTANCE_SHAPE":"VM.Standard.E3.Flex",
...                             "INSTANCE_COUNT":"1",
...                             "bandwidth_mbps":10,
...                             "memory_in_gbs":10,
...                             "ocpus":2}
...                  ).build()
>>> deployment_info = deployer.deploy(deployment_properties,
...             max_wait_time=600, poll_interval=15)
>>> print(deployment_info.model_deployment_id)
>>> print(deployment_info.workflow_req_id)
>>> print(deployment_info.url)
>>> deployer.list_deployments() # Optionally pass in a status
"""
from typing import Dict, Union
import warnings

import oci.pagination
import pandas as pd
from ads.common.auth import default_signer
from oci.data_science.data_science_client import DataScienceClient
from oci.data_science import DataScienceClientCompositeOperations

from .common import utils
from .common.utils import OCIClientManager, State
from .model_deployment import DEFAULT_POLL_INTERVAL, DEFAULT_WAIT_TIME, ModelDeployment
from .model_deployment_properties import ModelDeploymentProperties

warnings.warn(
    (
        "The `ads.model.deployment.model_deployer` is deprecated in `oracle-ads 2.8.6` and will be removed in `oracle-ads 3.0`."
        "Use `ModelDeployment` class in `ads.model.deployment` module for initializing and deploying model deployment. "
        "Check https://accelerated-data-science.readthedocs.io/en/latest/user_guide/model_registration/introduction.html"
    ),
    DeprecationWarning,
    stacklevel=2,
)


[docs] class ModelDeployer: """ModelDeployer is the class responsible for deploying the ModelDeployment Attributes ---------- config : dict ADS auth dictionary for OCI authentication. ds_client : DataScienceClient data science client ds_composite_client : DataScienceCompositeClient composite data science client Methods ------- deploy(model_deployment_details, **kwargs) Deploy the model specified by `model_deployment_details`. get_model_deployment(model_deployment_id:str) Get the ModelDeployment specified by `model_deployment_id`. get_model_deployment_state(model_deployment_id) Get the state of the current deployment specified by id. delete(model_deployment_id, **kwargs) Remove the model deployment specified by the id or Model Deployment Object list_deployments(status) lists the model deployments associated with current compartment and data science client show_deployments(status) shows the deployments filtered by `status` in a Dataframe """ def __init__(self, config: dict = None, ds_client: DataScienceClient = None): """Initializes model deployer. Parameters ---------- config : dict, optional ADS auth dictionary for OCI authentication. This can be generated by calling ads.common.auth.api_keys() or ads.common.auth.resource_principal(). If this is None, ads.common.default_signer(client_kwargs) will be used. ds_client: oci.data_science.data_science_client.DataScienceClient The Oracle DataScience client. """ if config: warnings.warn( "`config` will be deprecated in 3.0.0 and will be ignored now. Please use `ads.set_auth()` to config the auth information." ) self.ds_client = None self.ds_composite_client = None if ds_client: self.ds_client = ds_client self.ds_composite_client = DataScienceClientCompositeOperations( self.ds_client ) if not (self.ds_client != None and self.ds_composite_client != None): self.client_manager = OCIClientManager() self.ds_client = self.client_manager.ds_client self.ds_composite_client = self.client_manager.ds_composite_client
[docs] def deploy( self, properties: Union[ModelDeploymentProperties, Dict] = None, wait_for_completion: bool = True, max_wait_time: int = DEFAULT_WAIT_TIME, poll_interval: int = DEFAULT_POLL_INTERVAL, **kwargs, ) -> ModelDeployment: """Deploys a model. Parameters ---------- properties : ModelDeploymentProperties or dict Properties to deploy the model. Properties can be None when kwargs are used for specifying properties. wait_for_completion : bool Flag set for whether to wait for deployment to complete before proceeding. Optional, defaults to True. max_wait_time : int Maximum amount of time to wait in seconds. Optional, defaults to 1200. Negative value implies infinite wait time. poll_interval : int Poll interval in seconds. Optional, defaults to 30. kwargs : Keyword arguments for initializing ModelDeploymentProperties. See ModelDeploymentProperties() for details. Returns ------- ModelDeployment A ModelDeployment instance. """ warnings.warn( "Make sure to provide `compartment_id`, `project_id`, `instance_count`, `instance_shape`, `memory_in_gbs` and `ocpus` either through `kwargs` or `properties`" ) model_deployment = ModelDeployment( properties, **kwargs, ) return model_deployment.deploy( wait_for_completion, max_wait_time, poll_interval )
[docs] def deploy_from_model_uri( self, model_uri: str, properties: Union[ModelDeploymentProperties, Dict] = None, wait_for_completion: bool = True, max_wait_time: int = DEFAULT_WAIT_TIME, poll_interval: int = DEFAULT_POLL_INTERVAL, **kwargs, ) -> ModelDeployment: """Deploys a model. Parameters ---------- model_uri : str uri to model files, can be local or in cloud storage properties : ModelDeploymentProperties or dict Properties to deploy the model. Properties can be None when kwargs are used for specifying properties. wait_for_completion : bool Flag set for whether to wait for deployment to complete before proceeding. Defaults to True max_wait_time : int Maximum amount of time to wait in seconds (Defaults to 1200). Negative implies infinite wait time. poll_interval : int Poll interval in seconds (Defaults to 30). kwargs : Keyword arguments for initializing ModelDeploymentProperties Returns ------- ModelDeployment A ModelDeployment instance """ warnings.warn( "Make sure to provide `compartment_id`, `project_id`, `instance_count`, `instance_shape`, `memory_in_gbs` and `ocpus` either through `kwargs` or `properties`" ) if properties: model_id = self.client_manager.prepare_artifact( model_uri=model_uri, properties=properties ) properties.model_deployment_configuration_details.model_configuration_details.model_id = ( model_id ) else: model_id = self.client_manager.prepare_artifact( model_uri=model_uri, properties=kwargs ) kwargs["model_id"] = model_id return self.deploy( properties, wait_for_completion=wait_for_completion, max_wait_time=max_wait_time, poll_interval=poll_interval, **kwargs, )
[docs] def update( self, model_deployment_id: str, properties: ModelDeploymentProperties = None, wait_for_completion: bool = True, max_wait_time: int = DEFAULT_WAIT_TIME, poll_interval: int = DEFAULT_POLL_INTERVAL, **kwargs, ) -> ModelDeployment: """Updates an existing model deployment. Parameters ---------- model_deployment_id : str Model deployment OCID. properties : ModelDeploymentProperties An instance of ModelDeploymentProperties or dict to initialize the ModelDeploymentProperties. Defaults to None. wait_for_completion : bool Flag set for whether to wait for deployment to complete before proceeding. Defaults to True. max_wait_time : int Maximum amount of time to wait in seconds (Defaults to 1200). poll_interval : int Poll interval in seconds (Defaults to 30). kwargs : Keyword arguments for initializing ModelDeploymentProperties. Returns ------- ModelDeployment A ModelDeployment instance """ model_deployment = self.get_model_deployment(model_deployment_id) # Deployment properties will be refreshed within model_deployment.update() when update is done. return model_deployment.update( properties, wait_for_completion, max_wait_time=max_wait_time, poll_interval=poll_interval, **kwargs, )
[docs] def get_model_deployment(self, model_deployment_id: str) -> ModelDeployment: """Gets a ModelDeployment by OCID. Parameters ---------- model_deployment_id : str Model deployment OCID Returns ------- ModelDeployment A ModelDeployment instance """ try: oci_model_deployment_object = self.ds_client.get_model_deployment( model_deployment_id ).data model_deployment_object = ModelDeployment( oci_model_deployment_object, ) model_deployment_object.set_spec( model_deployment_object.CONST_ID, oci_model_deployment_object.id ) return model_deployment_object.sync() except Exception as e: utils.get_logger().error( "Getting model deployment failed with error: %s", e ) raise e
[docs] def get_model_deployment_state(self, model_deployment_id: str) -> State: """Gets the state of a deployment specified by OCID Parameters ---------- model_deployment_id : str Model deployment OCID Returns ------- str The state of the deployment """ model_deployment = self.get_model_deployment(model_deployment_id) return model_deployment.state
[docs] def delete( self, model_deployment_id, wait_for_completion: bool = True, max_wait_time: int = DEFAULT_WAIT_TIME, poll_interval: int = DEFAULT_POLL_INTERVAL, ) -> ModelDeployment: """Deletes the model deployment specified by OCID. Parameters ---------- model_deployment_id : str Model deployment OCID. wait_for_completion : bool Wait for deletion to complete. Defaults to True. max_wait_time : int Maximum amount of time to wait in seconds (Defaults to 600). Negative implies infinite wait time. poll_interval : int Poll interval in seconds (Defaults to 60). Returns ------- A ModelDeployment instance that was deleted """ try: model_deployment_object = self.get_model_deployment(model_deployment_id) return model_deployment_object.delete( wait_for_completion, max_wait_time, poll_interval ) except Exception as e: utils.get_logger().error( "Deleting model deployment failed with error: %s", format(e) ) raise e
[docs] def list_deployments(self, status=None, compartment_id=None, **kwargs) -> list: """Lists the model deployments associated with current compartment and data science client Parameters ---------- status : str Status of deployment. Defaults to None. compartment_id : str Target compartment to list deployments from. Defaults to the compartment set in the environment variable "NB_SESSION_COMPARTMENT_OCID". If "NB_SESSION_COMPARTMENT_OCID" is not set, the root compartment ID will be used. An ValueError will be raised if root compartment ID cannot be determined. kwargs : The values are passed to oci.data_science.DataScienceClient.list_model_deployments. Returns ------- list A list of ModelDeployment objects. Raises ------ ValueError If compartment_id is not specified and cannot be determined from the environment. """ if not compartment_id: compartment_id = self.client_manager.default_compartment_id() if not compartment_id: raise ValueError( "Unable to determine compartment ID from environment. Specify compartment_id." ) if isinstance(status, State): status = status.name if status is not None: kwargs["lifecycle_state"] = status # https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/api/pagination.html#module-oci.pagination deployments = oci.pagination.list_call_get_all_results( self.ds_client.list_model_deployments, compartment_id, **kwargs ).data result = [] for deployment in deployments: model_deployment = ModelDeployment(deployment) model_deployment.set_spec(model_deployment.CONST_ID, deployment.id) result.append(model_deployment.sync()) return result
[docs] def show_deployments( self, status=None, compartment_id=None, ) -> pd.DataFrame: """Returns the model deployments associated with current compartment and data science client as a Dataframe that can be easily visualized Parameters ---------- status : str Status of deployment. Defaults to None. compartment_id : str Target compartment to list deployments from. Defaults to the compartment set in the environment variable "NB_SESSION_COMPARTMENT_OCID". If "NB_SESSION_COMPARTMENT_OCID" is not set, the root compartment ID will be used. An ValueError will be raised if root compartment ID cannot be determined. Returns ------- DataFrame pandas Dataframe containing information about the ModelDeployments Raises ------ ValueError If compartment_id is not specified and cannot be determined from the environment. """ if not compartment_id: compartment_id = self.client_manager.default_compartment_id() if not compartment_id: raise ValueError( "Unable to determine compartment ID from environment. Specify compartment_id." ) if type(status) == str or status == None: status = State._from_str(status) model_deployments = self.ds_client.list_model_deployments(compartment_id).data display = pd.DataFrame() ids, urls, status_list = [], [], [] for oci_model_deployment_object in model_deployments: state_of_model = State._from_str( oci_model_deployment_object.lifecycle_state ) if status == State.UNKNOWN or status.name == state_of_model.name: model_dep_id = oci_model_deployment_object.id model_dep_url = oci_model_deployment_object._model_deployment_url model_dep_state = oci_model_deployment_object.lifecycle_state ids.append(model_dep_id) urls.append(model_dep_url) status_list.append(model_dep_state) display["model_id"] = ids display["deployment_url"] = urls display["current_state"] = status_list return display