From a1dd1fcc5008905ad5e1dd83fa01a0ac48541566 Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Sun, 19 Dec 2021 17:34:25 -0800 Subject: [PATCH 001/108] Add `network` to SDK initializer --- google/cloud/aiplatform/initializer.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/google/cloud/aiplatform/initializer.py b/google/cloud/aiplatform/initializer.py index ea1a51c8a7..7d59ea2a52 100644 --- a/google/cloud/aiplatform/initializer.py +++ b/google/cloud/aiplatform/initializer.py @@ -49,6 +49,7 @@ def __init__(self): self._staging_bucket = None self._credentials = None self._encryption_spec_key_name = None + self._network = None def init( self, @@ -60,6 +61,7 @@ def init( staging_bucket: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, encryption_spec_key_name: Optional[str] = None, + network: Optional[str] = None, ): """Updates common initialization parameters with provided options. @@ -83,6 +85,12 @@ def init( resource is created. If set, this resource and all sub-resources will be secured by this key. + network (Optional[str]): + Optional. The full name of the Compute Engine network to which Jobs + and Endpoints should be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + + If specified, all Jobs and Endpoints created will be peered with this VPC. """ # reset metadata_service config if project or location is updated. @@ -104,6 +112,8 @@ def init( self._credentials = credentials if encryption_spec_key_name: self._encryption_spec_key_name = encryption_spec_key_name + if network: + self._network = network if experiment: metadata.metadata_service.set_experiment( @@ -193,6 +203,11 @@ def encryption_spec_key_name(self) -> Optional[str]: """Default encryption spec key name, if provided.""" return self._encryption_spec_key_name + @property + def network(self) -> Optional[str]: + """Default Compute Engine network to peer to, if provided.""" + return self._network + def get_client_options( self, location_override: Optional[str] = None, prediction_client: bool = False ) -> client_options.ClientOptions: From 0f597e0ef11db812778b682fd4084565de2307ef Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Sun, 19 Dec 2021 23:18:11 -0800 Subject: [PATCH 002/108] Add urllib3 to library requirements --- setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 4ef6968114..d81b9e4c22 100644 --- a/setup.py +++ b/setup.py @@ -77,10 +77,11 @@ # Until this issue is closed # https://github.com/googleapis/google-cloud-python/issues/10566 "google-api-core[grpc] >= 1.26.0, <3.0.0dev", - "proto-plus >= 1.10.1", - "packaging >= 14.3", - "google-cloud-storage >= 1.32.0, < 2.0.0dev", "google-cloud-bigquery >= 1.15.0, < 3.0.0dev", + "google-cloud-storage >= 1.32.0, < 2.0.0dev", + "packaging >= 14.3", + "proto-plus >= 1.10.1", + "urllib3 >=1.21.1, <1.27" ), extras_require={ "full": full_extra_require, From bbb56fdbeda1586e18ec052cedd23e41201feb1b Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Wed, 22 Dec 2021 18:22:05 -0800 Subject: [PATCH 003/108] Fix network init() docstrings --- google/cloud/aiplatform/initializer.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/google/cloud/aiplatform/initializer.py b/google/cloud/aiplatform/initializer.py index 0305b7b363..7397bf9aed 100644 --- a/google/cloud/aiplatform/initializer.py +++ b/google/cloud/aiplatform/initializer.py @@ -86,11 +86,12 @@ def init( If set, this resource and all sub-resources will be secured by this key. network (Optional[str]): - Optional. The full name of the Compute Engine network to which Jobs - and Endpoints should be peered. E.g. "projects/12345/global/networks/myVPC". + Optional. The full name of the Compute Engine network to which jobs + and resources should be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - If specified, all Jobs and Endpoints created will be peered with this VPC. + If specified, all eligible jobs and resources created will be peered + with this VPC. """ # reset metadata_service config if project or location is updated. From 7476c104e77ef282c03738cb4c8ef54f64f7f38c Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Tue, 22 Feb 2022 11:03:18 -0800 Subject: [PATCH 004/108] Update Model/Endpoint docs to use top namespace --- google/cloud/aiplatform/models.py | 117 ++++++++++++++++++------------ 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index e4e2947bc6..4bcb870ee8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -27,7 +27,6 @@ from google.cloud import aiplatform from google.cloud.aiplatform import base -from google.cloud.aiplatform import explain from google.cloud.aiplatform import initializer from google.cloud.aiplatform import jobs from google.cloud.aiplatform import models @@ -235,7 +234,7 @@ def create( credentials (auth_credentials.Credentials): Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. - encryption_spec_key_name (Optional[str]): + encryption_spec_key_name (str): Optional. The Cloud KMS resource identifier of the customer managed encryption key used to protect the model. Has the form: @@ -328,7 +327,7 @@ def _create( credentials (auth_credentials.Credentials): Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. - encryption_spec (Optional[gca_encryption_spec.EncryptionSpec]): + encryption_spec (gca_encryption_spec.EncryptionSpec): Optional. The Cloud KMS customer managed encryption key used to protect the dataset. The key needs to be in the same region as where the compute resource is created. @@ -492,8 +491,10 @@ def _validate_deploy_args( deployed_model_display_name: Optional[str], traffic_split: Optional[Dict[str, int]], traffic_percentage: int, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, ): """Helper method to validate deploy arguments. @@ -536,12 +537,12 @@ def _validate_deploy_args( not be provided. Traffic of previously deployed models at the endpoint will be scaled down to accommodate new deployed model's traffic. Should not be provided if traffic_split is provided. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` @@ -593,8 +594,10 @@ def deploy( accelerator_type: Optional[str] = None, accelerator_count: Optional[int] = None, service_account: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), sync=True, ) -> None: @@ -653,12 +656,12 @@ def deploy( to the resource project. Users deploying the Model must have the `iam.serviceAccounts.actAs` permission on this service account. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` metadata (Sequence[Tuple[str, str]]): @@ -712,8 +715,10 @@ def _deploy( accelerator_type: Optional[str] = None, accelerator_count: Optional[int] = None, service_account: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), sync=True, ) -> None: @@ -772,12 +777,12 @@ def _deploy( to the resource project. Users deploying the Model must have the `iam.serviceAccounts.actAs` permission on this service account. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` metadata (Sequence[Tuple[str, str]]): @@ -834,8 +839,10 @@ def _deploy_call( accelerator_type: Optional[str] = None, accelerator_count: Optional[int] = None, service_account: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), ): """Helper method to deploy model to endpoint. @@ -893,12 +900,12 @@ def _deploy_call( to the resource project. Users deploying the Model must have the `iam.serviceAccounts.actAs` permission on this service account. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` metadata (Sequence[Tuple[str, str]]): @@ -1588,8 +1595,10 @@ def upload( instance_schema_uri: Optional[str] = None, parameters_schema_uri: Optional[str] = None, prediction_schema_uri: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, project: Optional[str] = None, location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, @@ -1701,12 +1710,12 @@ def upload( and probably different, including the URI scheme, than the one given on input. The output URI will point to a location where the user only has a read access. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` project: Optional[str]=None, @@ -1873,8 +1882,10 @@ def deploy( accelerator_type: Optional[str] = None, accelerator_count: Optional[int] = None, service_account: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), encryption_spec_key_name: Optional[str] = None, sync=True, @@ -1888,7 +1899,7 @@ def deploy( deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. - traffic_percentage (int): + traffic_percentage (int): Optional. Desired traffic to newly deployed model. Defaults to 0 if there are pre-existing deployed models. Defaults to 100 if there are no pre-existing deployed models. Negative values should @@ -1934,12 +1945,12 @@ def deploy( to the resource project. Users deploying the Model must have the `iam.serviceAccounts.actAs` permission on this service account. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` metadata (Sequence[Tuple[str, str]]): @@ -2008,8 +2019,10 @@ def _deploy( accelerator_type: Optional[str] = None, accelerator_count: Optional[int] = None, service_account: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), encryption_spec_key_name: Optional[str] = None, sync: bool = True, @@ -2069,12 +2082,12 @@ def _deploy( to the resource project. Users deploying the Model must have the `iam.serviceAccounts.actAs` permission on this service account. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` metadata (Sequence[Tuple[str, str]]): @@ -2153,8 +2166,10 @@ def batch_predict( starting_replica_count: Optional[int] = None, max_replica_count: Optional[int] = None, generate_explanation: Optional[bool] = False, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, labels: Optional[Dict[str, str]] = None, credentials: Optional[auth_credentials.Credentials] = None, encryption_spec_key_name: Optional[str] = None, @@ -2273,7 +2288,7 @@ def batch_predict( keyed `explanation`. The value of the entry is a JSON object that conforms to the [aiplatform.gapic.Explanation] object. - `csv`: Generating explanations for CSV format is not supported. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Explanation metadata configuration for this BatchPredictionJob. Can be specified only if `generate_explanation` is set to `True`. @@ -2282,7 +2297,7 @@ def batch_predict( a field of the `explanation_metadata` object is not populated, the corresponding field of the `Model.explanation_metadata` object is inherited. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. Can be specified only if `generate_explanation` is set to `True`. @@ -2540,8 +2555,10 @@ def upload_xgboost_model_file( instance_schema_uri: Optional[str] = None, parameters_schema_uri: Optional[str] = None, prediction_schema_uri: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, project: Optional[str] = None, location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, @@ -2617,12 +2634,12 @@ def upload_xgboost_model_file( and probably different, including the URI scheme, than the one given on input. The output URI will point to a location where the user only has a read access. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` project: Optional[str]=None, @@ -2735,8 +2752,10 @@ def upload_scikit_learn_model_file( instance_schema_uri: Optional[str] = None, parameters_schema_uri: Optional[str] = None, prediction_schema_uri: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, project: Optional[str] = None, location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, @@ -2813,12 +2832,12 @@ def upload_scikit_learn_model_file( and probably different, including the URI scheme, than the one given on input. The output URI will point to a location where the user only has a read access. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` project: Optional[str]=None, @@ -2930,8 +2949,10 @@ def upload_tensorflow_saved_model( instance_schema_uri: Optional[str] = None, parameters_schema_uri: Optional[str] = None, prediction_schema_uri: Optional[str] = None, - explanation_metadata: Optional[explain.ExplanationMetadata] = None, - explanation_parameters: Optional[explain.ExplanationParameters] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, project: Optional[str] = None, location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, @@ -3010,12 +3031,12 @@ def upload_tensorflow_saved_model( and probably different, including the URI scheme, than the one given on input. The output URI will point to a location where the user only has a read access. - explanation_metadata (explain.ExplanationMetadata): + explanation_metadata (aiplatform.explain.ExplanationMetadata): Optional. Metadata describing the Model's input and output for explanation. Both `explanation_metadata` and `explanation_parameters` must be passed together when used. For more details, see `Ref docs ` - explanation_parameters (explain.ExplanationParameters): + explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` project: Optional[str]=None, From 6ad46dde01307564b33cebddf9958d97158e087a Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Tue, 22 Feb 2022 18:00:32 -0800 Subject: [PATCH 005/108] Add core Private Endpoint wrapper --- google/cloud/aiplatform/__init__.py | 2 + google/cloud/aiplatform/models.py | 632 +++++++++++++++++++++++++--- 2 files changed, 585 insertions(+), 49 deletions(-) diff --git a/google/cloud/aiplatform/__init__.py b/google/cloud/aiplatform/__init__.py index 1defb5ad47..1407fe6d54 100644 --- a/google/cloud/aiplatform/__init__.py +++ b/google/cloud/aiplatform/__init__.py @@ -40,6 +40,7 @@ ) from google.cloud.aiplatform.metadata import metadata from google.cloud.aiplatform.models import Endpoint +from google.cloud.aiplatform.models import PrivateEndpoint from google.cloud.aiplatform.models import Model from google.cloud.aiplatform.jobs import ( BatchPredictionJob, @@ -108,6 +109,7 @@ "HyperparameterTuningJob", "Model", "PipelineJob", + "PrivateEndpoint", "TabularDataset", "Tensorboard", "TensorboardExperiment", diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 4bcb870ee8..6de7b7e461 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -14,12 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import pathlib import proto import re import shutil import tempfile -from typing import Dict, List, NamedTuple, Optional, Sequence, Tuple, Union +import urllib3 +from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation from google.api_core import exceptions as api_exceptions @@ -291,6 +293,7 @@ def _create( metadata: Optional[Sequence[Tuple[str, str]]] = (), credentials: Optional[auth_credentials.Credentials] = None, encryption_spec: Optional[gca_encryption_spec.EncryptionSpec] = None, + network: Optional[str] = None, sync=True, ) -> "Endpoint": """Creates a new endpoint by calling the API client. @@ -333,6 +336,13 @@ def _create( resource is created. If set, this Dataset and all sub-resources of this Dataset will be secured by this key. + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + + If set, this will be a private Endpoint. Read more about private + Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to create this endpoint synchronously. Returns: @@ -349,6 +359,7 @@ def _create( description=description, labels=labels, encryption_spec=encryption_spec, + network=network, ) operation_future = api_client.create_endpoint( @@ -483,8 +494,9 @@ def _unallocate_traffic( return new_traffic_split - @staticmethod + @classmethod def _validate_deploy_args( + cls, min_replica_count: int, max_replica_count: int, accelerator_type: Optional[str], @@ -523,7 +535,7 @@ def _validate_deploy_args( Required. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. traffic_split (Dict[str, int]): - Required. A map from a DeployedModel's ID to the percentage of + Optional. A map from a DeployedModel's ID to the percentage of this Endpoint's traffic that should be forwarded to that DeployedModel. If a DeployedModel's ID is not listed in this map, then it receives no traffic. The traffic percentage values must add up to 100, or @@ -531,7 +543,7 @@ def _validate_deploy_args( the moment. Key for model being deployed is "0". Should not be provided if traffic_percentage is provided. traffic_percentage (int): - Required. Desired traffic to newly deployed model. Defaults to + Optional. Desired traffic to newly deployed model. Defaults to 0 if there are pre-existing deployed models. Defaults to 100 if there are no pre-existing deployed models. Negative values should not be provided. Traffic of previously deployed models at the endpoint @@ -560,18 +572,20 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) - if traffic_split is None: - if traffic_percentage > 100: - raise ValueError("Traffic percentage cannot be greater than 100.") - if traffic_percentage < 0: - raise ValueError("Traffic percentage cannot be negative.") - - elif traffic_split: - # TODO(b/172678233) verify every referenced deployed model exists - if sum(traffic_split.values()) != 100: - raise ValueError( - "Sum of all traffic within traffic split needs to be 100." - ) + # TODO(b/): private Endpoints do not yet support traffic splitting + if cls == Endpoint: + if traffic_split is None: + if traffic_percentage > 100: + raise ValueError("Traffic percentage cannot be greater than 100.") + if traffic_percentage < 0: + raise ValueError("Traffic percentage cannot be negative.") + + elif traffic_split: + # TODO(b/172678233) verify every referenced deployed model exists + if sum(traffic_split.values()) != 100: + raise ValueError( + "Sum of all traffic within traffic split needs to be 100." + ) if bool(explanation_metadata) != bool(explanation_parameters): raise ValueError( @@ -964,7 +978,8 @@ def _deploy_call( explanation_spec.parameters = explanation_parameters deployed_model.explanation_spec = explanation_spec - if traffic_split is None: + # TODO(b/): Remove check for class once PrivateEndpoint supports traffic split + if traffic_split is None and cls.__class__ == Endpoint: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: # default scenario @@ -1085,7 +1100,16 @@ def _undeploy( self._sync_gca_resource_if_skipped() current_traffic_split = traffic_split or dict(self._gca_resource.traffic_split) - if deployed_model_id in current_traffic_split: + # TODO(b/211351292): Remove check once traffic split is supported + # Skip unallocating traffic and raise if new split is provided + if self.__class__ == PrivateEndpoint: + if traffic_split: + raise ValueError( + "Traffic splitting is not yet supported by private Endpoints. " + "Undeploy the current model without providing a `traffic_split`." + ) + + elif deployed_model_id in current_traffic_split: current_traffic_split = self._unallocate_traffic( traffic_split=current_traffic_split, deployed_model_id=deployed_model_id, @@ -1274,6 +1298,9 @@ def list( """ return cls._list_with_local_order( + cls_filter=lambda ep: not bool( + ep.network + ), # `network` is empty for public Endpoints filter=filter, order_by=order_by, project=project, @@ -1333,6 +1360,491 @@ def delete(self, force: bool = False, sync: bool = True) -> None: super().delete(sync=sync) +class PrivateEndpoint(Endpoint): + """ + Represents a Vertex AI private Endpoint resource. + + Read more [about private endpoints in the documentation.](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + """ + + def __init__( + self, + endpoint_name: str, + project: Optional[str] = None, + location: Optional[str] = None, + credentials: Optional[auth_credentials.Credentials] = None, + ): + """Retrieves a private Endpoint resource. + + Args: + endpoint_name (str): + Required. A fully-qualified endpoint resource name or endpoint ID. + Example: "projects/123/locations/us-central1/endpoints/456" or + "456" when project and location are initialized or passed. + project (str): + Optional. Project to retrieve endpoint from. If not set, project + set in aiplatform.init will be used. + location (str): + Optional. Location to retrieve endpoint from. If not set, location + set in aiplatform.init will be used. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to upload this model. Overrides + credentials set in aiplatform.init. + """ + super().__init__( + endpoint_name=endpoint_name, + project=project, + location=location, + credentials=credentials, + ) + + self._custom_predict_uri = None + self._custom_explain_uri = None + self._custom_health_uri = None + + self._http_client = urllib3.PoolManager() + + @classmethod + def create( + cls, + display_name: str, + network: Optional[str] = None, + description: Optional[str] = None, + labels: Optional[Dict[str, str]] = None, + project: Optional[str] = None, + location: Optional[str] = None, + credentials: Optional[auth_credentials.Credentials] = None, + encryption_spec_key_name: Optional[str] = None, + sync=True, + ) -> "Endpoint": + """Creates a new private Endpoint. + + Args: + display_name (str): + Required. The user-defined name of the Endpoint. + The name can be up to 128 characters long and can be consist + of any UTF-8 characters. + project (str): + Required. Project to retrieve endpoint from. If not set, project + set in aiplatform.init will be used. + location (str): + Required. Location to retrieve endpoint from. If not set, location + set in aiplatform.init will be used. + network (str): + Required. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + If not set, `network` set in aiplatform.init will be used. + description (str): + Optional. The description of the Endpoint. + labels (Dict[str, str]): + Optional. The labels with user-defined metadata to + organize your Endpoints. + Label keys and values can be no longer than 64 + characters (Unicode codepoints), can only + contain lowercase letters, numeric characters, + underscores and dashes. International characters + are allowed. + See https://goo.gl/xmQnxf for more information + and examples of labels. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to upload this model. Overrides + credentials set in aiplatform.init. + encryption_spec_key_name (str): + Optional. The Cloud KMS resource identifier of the customer + managed encryption key used to protect the model. Has the + form: + ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. + The key needs to be in the same region as where the compute + resource is created. + + If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. + + Overrides encryption_spec_key_name set in aiplatform.init. + + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + Returns: + endpoint (endpoint.Endpoint): + Created endpoint. + """ + api_client = cls._instantiate_client(location=location, credentials=credentials) + + utils.validate_display_name(display_name) + if labels: + utils.validate_labels(labels) + + project = project or initializer.global_config.project + location = location or initializer.global_config.location + network = network or initializer.global_config.network + + if not network: + raise ValueError( + "Please provide required argument `network` or set " + "using aiplatform.init(network=...)" + ) + + return cls._create( + api_client=api_client, + display_name=display_name, + project=project, + location=location, + description=description, + labels=labels, + credentials=credentials, + encryption_spec=initializer.global_config.get_encryption_spec( + encryption_spec_key_name=encryption_spec_key_name + ), + network=network, + sync=sync, + ) + + @classmethod + def _construct_sdk_resource_from_gapic( + cls, + gapic_resource: proto.Message, + project: Optional[str] = None, + location: Optional[str] = None, + credentials: Optional[auth_credentials.Credentials] = None, + ) -> "PrivateEndpoint": + """Given a GAPIC private Endpoint object, return the SDK representation. + + Args: + gapic_resource (proto.Message): + A GAPIC representation of a private Endpoint resource, usually + retrieved by a get_* or in a list_* API call. + project (str): + Optional. Project to construct Endpoint object from. If not set, + project set in aiplatform.init will be used. + location (str): + Optional. Location to construct Endpoint object from. If not set, + location set in aiplatform.init will be used. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to construct Endpoint. + Overrides credentials set in aiplatform.init. + + Returns: + PrivateEndpoint: + An initialized PrivateEndpoint resource. + """ + endpoint = cls._empty_constructor( + project=project, location=location, credentials=credentials + ) + + endpoint._gca_resource = gapic_resource + + endpoint._custom_predict_uri = None + endpoint._custom_explain_uri = None + endpoint._custom_health_uri = None + + endpoint._http_client = urllib3.PoolManager() + + return endpoint + + def _unauthenticated_http_call( + self, + method: str, + url: str, + body: Optional[Dict[Any, Any]] = None, + headers: Optional[Dict[str, str]] = None, + ) -> urllib3.response.HTTPResponse: + + try: + response = self._http_client.request( + method, url, body=body, headers=headers + ) + + print("Dumping urllib3 response", response.data) + print("Dumping urllib3 response", response.status) + if response.status < 300: + return response + else: + raise RuntimeError( + f"{response.status} - Failed to make prediction request, see response:\n", + response.data + ) + + except urllib3.exceptions.MaxRetryError: + raise RuntimeError( + f"Failed to make a {method} request to this URI, make sure: " + " this call is being made inside the network this private Endpoint is peered to " + f"({self._gca_resource.network}), calling health_check() returns True, " + f"and that {url} is a valid URL." + ) + + + def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: + """Make a prediction against this private Endpoint using unauthenticated HTTP. + + This method must be called within the network the private Endpoint is peered to. + The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. + + Args: + instances (List): + Required. The instances that are the input to the + prediction call. A DeployedModel may have an upper limit + on the number of instances it supports per request, and + when it is exceeded the prediction call errors in case + of AutoML Models, or, in case of customer created + Models, the behaviour is as documented by that Model. + The schema of any single instance may be specified via + Endpoint's DeployedModels' + [Model's][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``instance_schema_uri``. + parameters (Dict): + The parameters that govern the prediction. The schema of + the parameters may be specified via Endpoint's + DeployedModels' [Model's + ][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``parameters_schema_uri``. + Returns: + prediction: Prediction with returned predictions and Model Id. + """ + self.wait() + self._sync_gca_resource_if_skipped() + + response = self._unauthenticated_http_call( + method="POST", + url=self._custom_predict_uri + or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri, + body=json.dumps({"instances": instances}), + headers={"Content-Type": "application/json"}, + ) + + prediction_response = json.loads(response.data) + + return Prediction( + predictions=prediction_response.get("predictions"), + deployed_model_id=prediction_response.get("deployedModelId"), + ) + + def explain( + self, instances: List[Dict], parameters: Optional[Dict] = None, + ) -> Prediction: + self.wait() + self._sync_gca_resource_if_skipped() + + try: + response = self._unauthenticated_http_call( + method="POST", + url=self._custom_explain_uri + or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri, + body=json.dumps({"instances": instances}), + headers={"Content-Type": "application/json"}, + ) + + except urllib3.exceptions.MaxRetryError as err: + if not ( + self._custom_explain_uri + or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + ): + raise RuntimeError( + "There is no URI for explanations on this private Endpoint, please " + "check if the model deployed supports explanations." + ) + raise err + + prediction_response = response.data.decode("utf-8") + + return Prediction( + predictions=prediction_response.get("predictions"), + deployed_model_id=prediction_response.get("deployedModelId"), + explanations=prediction_response.get("explanations"), + ) + + def health_check(self) -> bool: + """ + Makes GET request to this private Endpoint's health check URI. Must be within network + that this private Endpoint + """ + self.wait() + self._sync_gca_resource_if_skipped() + + response = self._unauthenticated_http_call( + method="GET", + url=self._custom_health_uri + or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri, + ) + + return response.status == 200 + + @classmethod + def list( + cls, + filter: Optional[str] = None, + order_by: Optional[str] = None, + project: Optional[str] = None, + location: Optional[str] = None, + credentials: Optional[auth_credentials.Credentials] = None, + ) -> List["models.PrivateEndpoint"]: + """List all private Endpoint resource instances. + + Example Usage: + ``` + aiplatform.PrivateEndpoint.list( + filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', + ) + ``` + + Args: + filter (str): + Optional. An expression for filtering the results of the request. + For field names both snake_case and camelCase are supported. + order_by (str): + Optional. A comma-separated list of fields to order by, sorted in + ascending order. Use "desc" after a field name for descending. + Supported fields: `display_name`, `create_time`, `update_time` + project (str): + Optional. Project to retrieve list from. If not set, project + set in aiplatform.init will be used. + location (str): + Optional. Location to retrieve list from. If not set, location + set in aiplatform.init will be used. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to retrieve list. Overrides + credentials set in aiplatform.init. + + Returns: + List[models.PrivateEndpoint] - A list of PrivateEndpoint resource objects + """ + + return cls._list_with_local_order( + cls_filter=lambda ep: bool( + ep.network + ), # Only private Endpoints have a network set + filter=filter, + order_by=order_by, + project=project, + location=location, + credentials=credentials, + ) + + def deploy( + self, + model: "Model", + deployed_model_display_name: Optional[str] = None, + machine_type: Optional[str] = None, + min_replica_count: int = 1, + max_replica_count: int = 1, + accelerator_type: Optional[str] = None, + accelerator_count: Optional[int] = None, + service_account: Optional[str] = None, + explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, + explanation_parameters: Optional[ + aiplatform.explain.ExplanationParameters + ] = None, + metadata: Optional[Sequence[Tuple[str, str]]] = (), + sync=True, + ) -> None: + """Deploys a Model to the private Endpoint. + + Args: + model (aiplatform.Model): + Required. Model to be deployed. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the larger value of min_replica_count or 1 will + be used. If value provided is smaller than min_replica_count, it + will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + """ + self._sync_gca_resource_if_skipped() + + if len(self._gca_resource.deployed_models): + raise ValueError( + "A maximum of one model can be deployed to each private Endpoint. " + "Please call PrivateEndpoint.undeploy_all() before deploying another " + "model." + ) + + self._validate_deploy_args( + min_replica_count, + max_replica_count, + accelerator_type, + deployed_model_display_name, + explanation_metadata, + explanation_parameters, + ) + + self._deploy( + model=model, + deployed_model_display_name=deployed_model_display_name, + machine_type=machine_type, + min_replica_count=min_replica_count, + max_replica_count=max_replica_count, + accelerator_type=accelerator_type, + accelerator_count=accelerator_count, + service_account=service_account, + explanation_metadata=explanation_metadata, + explanation_parameters=explanation_parameters, + metadata=metadata, + sync=sync, + ) + + def undeploy(self, deployed_model_id: str, sync=True,) -> None: + """Undeploys a deployed model from the private Endpoint. + + Args: + deployed_model_id (str): + Required. The ID of the DeployedModel to be undeployed from the + private Endpoint. Use PrivateEndpoint.list_models() to get the + deployed model ID. + """ + self._sync_gca_resource_if_skipped() + + self._undeploy( + deployed_model_id=deployed_model_id, sync=sync, + ) + + class Model(base.VertexAiResourceNounWithFutureManager): client_class = utils.ModelClientWithOverride @@ -1872,7 +2384,7 @@ def upload( # TODO(b/172502059) support deploying with endpoint resource name def deploy( self, - endpoint: Optional["Endpoint"] = None, + endpoint: Optional[Union["Endpoint", "PrivateEndpoint"]] = None, deployed_model_display_name: Optional[str] = None, traffic_percentage: Optional[int] = 0, traffic_split: Optional[Dict[str, int]] = None, @@ -1888,32 +2400,18 @@ def deploy( ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), encryption_spec_key_name: Optional[str] = None, + network: Optional[str] = None, sync=True, ) -> Endpoint: """Deploys model to endpoint. Endpoint will be created if unspecified. Args: - endpoint ("Endpoint"): - Optional. Endpoint to deploy model to. If not specified, endpoint - display name will be model display name+'_endpoint'. + endpoint (Union["Endpoint", "PrivateEndpoint"]): + Optional. Public or private Endpoint to deploy model to. If not specified, + endpoint display name will be model display name+'_endpoint'. deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. machine_type (str): Optional. The type of machine. Not specifying machine type will result in model to be deployed with automatic resources. @@ -1967,6 +2465,13 @@ def deploy( If set, this Model and all sub-resources of this Model will be secured by this key. Overrides encryption_spec_key_name set in aiplatform.init + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + + If set, a private Endpoint will be created. Read more about private + Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will @@ -1987,6 +2492,15 @@ def deploy( explanation_parameters, ) + if endpoint and endpoint.__class__ == "PrivateEndpoint"(): + if traffic_percentage or traffic_split: + raise ValueError( + "Traffic splitting is not yet supported for private Endpoints. " + "Try calling deploy() without providing a `traffic_split` or " + "`traffic_percentage`. A maximum of one model can be deployed " + "to each private Endpoint." + ) + return self._deploy( endpoint=endpoint, deployed_model_display_name=deployed_model_display_name, @@ -2003,13 +2517,14 @@ def deploy( metadata=metadata, encryption_spec_key_name=encryption_spec_key_name or initializer.global_config.encryption_spec_key_name, + network=network, sync=sync, ) @base.optional_sync(return_input_arg="endpoint", bind_future_to_self=False) def _deploy( self, - endpoint: Optional["Endpoint"] = None, + endpoint: Optional[Union["Endpoint", "PrivateEndpoint"]] = None, deployed_model_display_name: Optional[str] = None, traffic_percentage: Optional[int] = 0, traffic_split: Optional[Dict[str, int]] = None, @@ -2025,14 +2540,15 @@ def _deploy( ] = None, metadata: Optional[Sequence[Tuple[str, str]]] = (), encryption_spec_key_name: Optional[str] = None, + network: Optional[str] = None, sync: bool = True, ) -> Endpoint: """Deploys model to endpoint. Endpoint will be created if unspecified. Args: - endpoint ("Endpoint"): - Optional. Endpoint to deploy model to. If not specified, endpoint - display name will be model display name+'_endpoint'. + endpoint (Union["Endpoint", "PrivateEndpoint"]): + Optional. Public or private Endpoint to deploy model to. If not specified, + endpoint display name will be model display name+'_endpoint'. deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. @@ -2104,6 +2620,13 @@ def _deploy( If set, this Model and all sub-resources of this Model will be secured by this key. Overrides encryption_spec_key_name set in aiplatform.init + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + + If set, a private Endpoint will be created. Read more about private + Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will @@ -2115,17 +2638,28 @@ def _deploy( if endpoint is None: display_name = self.display_name[:118] + "_endpoint" - endpoint = Endpoint.create( - display_name=display_name, - project=self.project, - location=self.location, - credentials=self.credentials, - encryption_spec_key_name=encryption_spec_key_name, - ) + + if not network: + endpoint = Endpoint.create( + display_name=display_name, + project=self.project, + location=self.location, + credentials=self.credentials, + encryption_spec_key_name=encryption_spec_key_name, + ) + else: + endpoint = PrivateEndpoint.create( + display_name=display_name, + network=network, + project=self.project, + location=self.location, + credentials=self.credentials, + encryption_spec_key_name=encryption_spec_key_name, + ) _LOGGER.log_action_start_against_resource("Deploying model to", "", endpoint) - Endpoint._deploy_call( + endpoint.__class__._deploy_call( endpoint.api_client, endpoint.resource_name, self.resource_name, From b8126e07d5fb3ff26af2fccc816f639bddd5844e Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Wed, 23 Feb 2022 00:24:05 -0800 Subject: [PATCH 006/108] Drop logs, add URI props --- google/cloud/aiplatform/models.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 6de7b7e461..a430b9caae 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1404,6 +1404,21 @@ def __init__( self._http_client = urllib3.PoolManager() + @property + def predict_http_uri(self) -> Optional[str]: + """Http(s) path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" + return self._custom_predict_uri or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri + + @property + def explain_http_uri(self) -> Optional[str]: + """Http(s) path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" + return self._custom_explain_uri or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + + @property + def health_http_uri(self) -> Optional[str]: + """Http(s) path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" + return self._custom_health_uri or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri + @classmethod def create( cls, @@ -1556,8 +1571,6 @@ def _unauthenticated_http_call( method, url, body=body, headers=headers ) - print("Dumping urllib3 response", response.data) - print("Dumping urllib3 response", response.status) if response.status < 300: return response else: @@ -1609,8 +1622,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict response = self._unauthenticated_http_call( method="POST", - url=self._custom_predict_uri - or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri, + url=self.predict_http_uri, body=json.dumps({"instances": instances}), headers={"Content-Type": "application/json"}, ) @@ -1631,8 +1643,7 @@ def explain( try: response = self._unauthenticated_http_call( method="POST", - url=self._custom_explain_uri - or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri, + url=self.explain_http_uri, body=json.dumps({"instances": instances}), headers={"Content-Type": "application/json"}, ) @@ -1666,8 +1677,7 @@ def health_check(self) -> bool: response = self._unauthenticated_http_call( method="GET", - url=self._custom_health_uri - or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri, + url=self.health_http_uri, ) return response.status == 200 From f43ab4ec6ef71b4e970f74f644545ac74e101d29 Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Wed, 23 Feb 2022 06:24:54 -0800 Subject: [PATCH 007/108] Lint, update typing for Prediction class --- google/cloud/aiplatform/models.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index a430b9caae..82f1533ca5 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -79,7 +79,7 @@ class Prediction(NamedTuple): of elements as instances to be explained. Default is None. """ - predictions: Dict[str, List] + predictions: List[Dict[str, Any]] deployed_model_id: str explanations: Optional[Sequence[gca_explanation_compat.Explanation]] = None @@ -1407,17 +1407,26 @@ def __init__( @property def predict_http_uri(self) -> Optional[str]: """Http(s) path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" - return self._custom_predict_uri or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri + return ( + self._custom_predict_uri + or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri + ) @property def explain_http_uri(self) -> Optional[str]: """Http(s) path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" - return self._custom_explain_uri or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + return ( + self._custom_explain_uri + or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + ) @property def health_http_uri(self) -> Optional[str]: """Http(s) path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" - return self._custom_health_uri or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri + return ( + self._custom_health_uri + or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri + ) @classmethod def create( @@ -1576,7 +1585,7 @@ def _unauthenticated_http_call( else: raise RuntimeError( f"{response.status} - Failed to make prediction request, see response:\n", - response.data + response.data, ) except urllib3.exceptions.MaxRetryError: @@ -1587,7 +1596,6 @@ def _unauthenticated_http_call( f"and that {url} is a valid URL." ) - def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: """Make a prediction against this private Endpoint using unauthenticated HTTP. @@ -1651,7 +1659,9 @@ def explain( except urllib3.exceptions.MaxRetryError as err: if not ( self._custom_explain_uri - or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + or self._gca_resource.deployed_models[ + 0 + ].private_endpoints.explain_http_uri ): raise RuntimeError( "There is no URI for explanations on this private Endpoint, please " @@ -1676,8 +1686,7 @@ def health_check(self) -> bool: self._sync_gca_resource_if_skipped() response = self._unauthenticated_http_call( - method="GET", - url=self.health_http_uri, + method="GET", url=self.health_http_uri, ) return response.status == 200 From 7c150d4d4373571e1564129b020592cf3d4156e6 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 23 Feb 2022 15:15:21 +0000 Subject: [PATCH 008/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c556766b51..39f70edfee 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ "google-cloud-storage >= 1.32.0, < 3.0.0dev", "packaging >= 14.3", "proto-plus >= 1.10.1", - "urllib3 >=1.21.1, <1.27" + "urllib3 >=1.21.1, <1.27", ), extras_require={ "full": full_extra_require, From e84761a1e617566661d1ed7cf4486e69f475b756 Mon Sep 17 00:00:00 2001 From: Vinny Senthil Date: Thu, 24 Feb 2022 16:08:33 -0800 Subject: [PATCH 009/108] Add bug refs, test stubs, minor fixes --- google/cloud/aiplatform/models.py | 10 ++++---- tests/unit/aiplatform/test_endpoints.py | 32 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 82f1533ca5..9a4f1afd32 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -572,7 +572,7 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) - # TODO(b/): private Endpoints do not yet support traffic splitting + # TODO(b/221059294): private Endpoints do not yet support traffic splitting if cls == Endpoint: if traffic_split is None: if traffic_percentage > 100: @@ -978,8 +978,8 @@ def _deploy_call( explanation_spec.parameters = explanation_parameters deployed_model.explanation_spec = explanation_spec - # TODO(b/): Remove check for class once PrivateEndpoint supports traffic split - if traffic_split is None and cls.__class__ == Endpoint: + # TODO(b/221059294): Remove check for class once PrivateEndpoint supports traffic split + if traffic_split is None and cls == Endpoint: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: # default scenario @@ -1584,7 +1584,7 @@ def _unauthenticated_http_call( return response else: raise RuntimeError( - f"{response.status} - Failed to make prediction request, see response:\n", + f"{response.status} - Failed to make request, see response:\n", response.data, ) @@ -2536,7 +2536,7 @@ def deploy( metadata=metadata, encryption_spec_key_name=encryption_spec_key_name or initializer.global_config.encryption_spec_key_name, - network=network, + network=network or initializer.global_config.network, sync=sync, ) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 7f9856a22b..8351595296 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -73,6 +73,7 @@ _TEST_MODEL_NAME = ( f"projects/{_TEST_PROJECT}/locations/{_TEST_LOCATION}/models/{_TEST_ID}" ) +_TEST_NETWORK = f"projects/{_TEST_PROJECT}/global/networks/{_TEST_ID}" _TEST_MODEL_ID = "1028944691210842416" _TEST_PREDICTION = [[1.0, 2.0, 3.0], [3.0, 3.0, 1.0]] @@ -1268,3 +1269,34 @@ def test_delete_endpoint_with_force( sdk_undeploy_all_mock.assert_called_once() delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) + +class TestPrivateEndpoint(TestEndpoint): + def test_http_health_check(self): + pass + + def test_http_health_check_unhealthy(self): + pass + + def test_http_predict(self): + pass + + def test_http_predict_no_response(self): + pass + + def test_http_predict_404(self): + pass + + def test_http_predict_500(self): + pass + + def test_deploy_private_endpoint(self): + pass + + def test_undeploy_private_endpoint(self): + pass + + def test_create_private_endpoint(self): + pass + + def test_list_private_endpoints(self): + pass From 4dc3c6e2563ff274a49c32713ca162d316ea5ae2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 25 Feb 2022 00:14:21 +0000 Subject: [PATCH 010/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/unit/aiplatform/test_endpoints.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 8351595296..72b15a8474 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -1270,6 +1270,7 @@ def test_delete_endpoint_with_force( delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) + class TestPrivateEndpoint(TestEndpoint): def test_http_health_check(self): pass From db589c474d913accfdfee793f07139beb782cd45 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 22 Mar 2022 10:22:22 -0700 Subject: [PATCH 011/108] test commit --- google/cloud/aiplatform/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index fe57ca6088..8054d4e1f2 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1673,6 +1673,9 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict prediction_response = json.loads(response.data) + # remove this line, just testing + print(prediction_response) + return Prediction( predictions=prediction_response.get("predictions"), deployed_model_id=prediction_response.get("deployedModelId"), From f598f02a4498b517da3a394253734c05e912f2b1 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Thu, 24 Mar 2022 15:55:45 -0700 Subject: [PATCH 012/108] adding examples, slight changes --- google/cloud/aiplatform/models.py | 87 +++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 8054d4e1f2..41a53bbf52 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1274,7 +1274,7 @@ def explain( Optional. If specified, this ExplainRequest will be served by the chosen DeployedModel, overriding this Endpoint's traffic split. Returns: - prediction: Prediction with returned predictions, explanations and Model Id. + prediction: Prediction with returned predictions, explanations, and Model Id. """ self.wait() @@ -1412,11 +1412,20 @@ def __init__( ): """Retrieves a private Endpoint resource. + Example usage: + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name="projects/123/locations/us-central1/endpoints/my_endpoint_id" + ) + or (when project and location are initialized) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name='my_endpoint_id' + ) + Args: endpoint_name (str): Required. A fully-qualified endpoint resource name or endpoint ID. - Example: "projects/123/locations/us-central1/endpoints/456" or - "456" when project and location are initialized or passed. + Example: "projects/123/locations/us-central1/endpoints/my_endpoint_id" or + "my_endpoint_id" when project and location are initialized or passed. project (str): Optional. Project to retrieve endpoint from. If not set, project set in aiplatform.init will be used. @@ -1479,6 +1488,19 @@ def create( ) -> "Endpoint": """Creates a new private Endpoint. + Example usage: + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name='my_endpoint_name', + project='123', + location='us-central1' + network='my_vpc' + ) + or (when project and location are initialized) + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name='my_endpoint_name', + network='my_vpc' + ) + Args: display_name (str): Required. The user-defined name of the Endpoint. @@ -1492,7 +1514,7 @@ def create( set in aiplatform.init will be used. network (str): Required. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". Private services access must already be configured for the network. If not set, `network` set in aiplatform.init will be used. description (str): @@ -1526,6 +1548,7 @@ def create( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Returns: endpoint (endpoint.Endpoint): Created endpoint. @@ -1603,7 +1626,7 @@ def _construct_sdk_resource_from_gapic( return endpoint - def _unauthenticated_http_call( + def _http_request( self, method: str, url: str, @@ -1634,10 +1657,13 @@ def _unauthenticated_http_call( def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: """Make a prediction against this private Endpoint using unauthenticated HTTP. - This method must be called within the network the private Endpoint is peered to. The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. + Example usage: + response = my_private_endpoint.predict(instances=[...]) + my_predictions = response.predictions + Args: instances (List): Required. The instances that are the input to the @@ -1658,13 +1684,15 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ][google.cloud.aiplatform.v1beta1.DeployedModel.model] [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] ``parameters_schema_uri``. + Returns: prediction: Prediction with returned predictions and Model Id. """ self.wait() self._sync_gca_resource_if_skipped() - response = self._unauthenticated_http_call( + + response = self._http_request( method="POST", url=self.predict_http_uri, body=json.dumps({"instances": instances}), @@ -1684,11 +1712,38 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict def explain( self, instances: List[Dict], parameters: Optional[Dict] = None, ) -> Prediction: + """Make a prediction with explanations against this private Endpoint. + + Example usage: + response = my_private_endpoint.explain(instances=[...]) + my_explanations = response.explanations + + Args: + instances (List): + Required. The instances that are the input to the + prediction call. A DeployedModel may have an upper limit + on the number of instances it supports per request, and + when it is exceeded the prediction call errors in case + of AutoML Models, or, in case of customer created + Models, the behaviour is as documented by that Model. + The schema of any single instance may be specified via + Endpoint's DeployedModels' + [Model's][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``instance_schema_uri``. + parameters (Dict): + The parameters that govern the prediction. The schema of + the parameters may be specified via Endpoint's + DeployedModels' [Model's + ][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``parameters_schema_uri``. + """ self.wait() self._sync_gca_resource_if_skipped() try: - response = self._unauthenticated_http_call( + response = self._http_request( method="POST", url=self.explain_http_uri, body=json.dumps({"instances": instances}), @@ -1698,9 +1753,7 @@ def explain( except urllib3.exceptions.MaxRetryError as err: if not ( self._custom_explain_uri - or self._gca_resource.deployed_models[ - 0 - ].private_endpoints.explain_http_uri + or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri ): raise RuntimeError( "There is no URI for explanations on this private Endpoint, please " @@ -1724,7 +1777,7 @@ def health_check(self) -> bool: self.wait() self._sync_gca_resource_if_skipped() - response = self._unauthenticated_http_call( + response = self._http_request( method="GET", url=self.health_http_uri, ) @@ -1742,11 +1795,11 @@ def list( """List all private Endpoint resource instances. Example Usage: - ``` - aiplatform.PrivateEndpoint.list( - filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', - ) - ``` + my_private_endpoints = aiplatform.PrivateEndpoint.list() + or + my_private_endpoints = aiplatform.PrivateEndpoint.list( + filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', + ) Args: filter (str): From dfc897d3a7a8ca8b2a8e5bd98c4e373a97d11344 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 24 Mar 2022 22:59:39 +0000 Subject: [PATCH 013/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 41a53bbf52..6865b9a8d0 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1691,7 +1691,6 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict self.wait() self._sync_gca_resource_if_skipped() - response = self._http_request( method="POST", url=self.predict_http_uri, @@ -1753,7 +1752,9 @@ def explain( except urllib3.exceptions.MaxRetryError as err: if not ( self._custom_explain_uri - or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri + or self._gca_resource.deployed_models[ + 0 + ].private_endpoints.explain_http_uri ): raise RuntimeError( "There is no URI for explanations on this private Endpoint, please " @@ -1777,9 +1778,7 @@ def health_check(self) -> bool: self.wait() self._sync_gca_resource_if_skipped() - response = self._http_request( - method="GET", url=self.health_http_uri, - ) + response = self._http_request(method="GET", url=self.health_http_uri,) return response.status == 200 From 80714b623b58909b00ba7c94fb77df9114e6cf24 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 29 Mar 2022 14:15:26 -0700 Subject: [PATCH 014/108] adding deployed model check: q --- google/cloud/aiplatform/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 41a53bbf52..999b2fbac9 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1634,6 +1634,13 @@ def _http_request( headers: Optional[Dict[str, str]] = None, ) -> urllib3.response.HTTPResponse: + if not self._gca_resource.deployed_models: + raise RuntimeError( + f"Failed to make a {method} request. A model is not deployed " + f"to this private Endpoint and a model must be deployed to make a {method} request." + ) + + try: response = self._http_client.request( method, url, body=body, headers=headers @@ -1642,6 +1649,7 @@ def _http_request( if response.status < 300: return response else: + # add if statements for explain vs. predict 404 errors raise RuntimeError( f"{response.status} - Failed to make request, see response:\n", response.data, @@ -1684,7 +1692,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ][google.cloud.aiplatform.v1beta1.DeployedModel.model] [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] ``parameters_schema_uri``. - + Returns: prediction: Prediction with returned predictions and Model Id. """ From 2425261d9fcbaaa0051d93532c35f18196544285 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 29 Mar 2022 14:42:28 -0700 Subject: [PATCH 015/108] adding deployed model check --- google/cloud/aiplatform/models.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 27e1e95252..aea4b04171 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1452,6 +1452,11 @@ def __init__( @property def predict_http_uri(self) -> Optional[str]: """Http(s) path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" + if not self._gca_resource.deployed_models: + raise RuntimeError( + "Cannot make a predict request because a model has not been deployed on this private " + "Endpoint. Please ensure a model has been deployed." + ) return ( self._custom_predict_uri or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri @@ -1460,6 +1465,11 @@ def predict_http_uri(self) -> Optional[str]: @property def explain_http_uri(self) -> Optional[str]: """Http(s) path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" + if not self._gca_resource.deployed_models: + raise RuntimeError( + "Cannot make a explain request because a model has not been deployed on this private " + "Endpoint. Please ensure a model has been deployed." + ) return ( self._custom_explain_uri or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri @@ -1468,6 +1478,11 @@ def explain_http_uri(self) -> Optional[str]: @property def health_http_uri(self) -> Optional[str]: """Http(s) path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" + if not self._gca_resource.deployed_models: + raise RuntimeError( + "Cannot make a health check request because a model has not been deployed on this private " + "Endpoint. Please ensure a model has been deployed." + ) return ( self._custom_health_uri or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri @@ -1634,13 +1649,6 @@ def _http_request( headers: Optional[Dict[str, str]] = None, ) -> urllib3.response.HTTPResponse: - if not self._gca_resource.deployed_models: - raise RuntimeError( - f"Failed to make a {method} request. A model is not deployed " - f"to this private Endpoint and a model must be deployed to make a {method} request." - ) - - try: response = self._http_client.request( method, url, body=body, headers=headers From a5bec230e8905a8689322625b4daa56eed5dc9a8 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 29 Mar 2022 16:19:40 -0700 Subject: [PATCH 016/108] minor changes, adding error catching for explanations --- google/cloud/aiplatform/models.py | 37 ++++++++++++++----------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index aea4b04171..91208a3419 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1654,13 +1654,15 @@ def _http_request( method, url, body=body, headers=headers ) + # delete later, use for testing + print(response.data) + if response.status < 300: return response else: # add if statements for explain vs. predict 404 errors raise RuntimeError( - f"{response.status} - Failed to make request, see response:\n", - response.data, + f"{response.status} - Failed to make request, see response:\n" + response.data.decode("utf-8") ) except urllib3.exceptions.MaxRetryError: @@ -1757,26 +1759,21 @@ def explain( self.wait() self._sync_gca_resource_if_skipped() - try: - response = self._http_request( - method="POST", - url=self.explain_http_uri, - body=json.dumps({"instances": instances}), - headers={"Content-Type": "application/json"}, + if (self.explain_http_uri and not self._gca_resource.deployed_models[ + 0].explanation_metadata and not self._gca_resource.deployed_models[ + 0].explanation_parameters): + raise RuntimeError( + "This deployed model cannot do explanations. Please undeploy and redeploy " + "the model with the explanation_metadata and explanation_parameters arguments " + "specified." ) - except urllib3.exceptions.MaxRetryError as err: - if not ( - self._custom_explain_uri - or self._gca_resource.deployed_models[ - 0 - ].private_endpoints.explain_http_uri - ): - raise RuntimeError( - "There is no URI for explanations on this private Endpoint, please " - "check if the model deployed supports explanations." - ) - raise err + response = self._http_request( + method="POST", + url=self.explain_http_uri, + body=json.dumps({"instances": instances}), + headers={"Content-Type": "application/json"}, + ) prediction_response = response.data.decode("utf-8") From ddb8cce7e3604f009eae17ac1a4394dcac73a8fd Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 29 Mar 2022 17:07:32 -0700 Subject: [PATCH 017/108] testing to get other fields --- google/cloud/aiplatform/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 91208a3419..15a482b931 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1662,7 +1662,7 @@ def _http_request( else: # add if statements for explain vs. predict 404 errors raise RuntimeError( - f"{response.status} - Failed to make request, see response:\n" + response.data.decode("utf-8") + f"{response.status} - Failed to make request, see response: " + response.data.decode("utf-8") ) except urllib3.exceptions.MaxRetryError: @@ -1720,6 +1720,8 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict # remove this line, just testing print(prediction_response) + print('==================================') + print(response.data.decode("utf-8")) return Prediction( predictions=prediction_response.get("predictions"), From 9c44b1c202fc2e84fdd7b1411781e0f9988087fe Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 30 Mar 2022 10:30:24 -0700 Subject: [PATCH 018/108] removing edge case --- google/cloud/aiplatform/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 15a482b931..7f80ec13d2 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1760,7 +1760,7 @@ def explain( """ self.wait() self._sync_gca_resource_if_skipped() - + ''' if (self.explain_http_uri and not self._gca_resource.deployed_models[ 0].explanation_metadata and not self._gca_resource.deployed_models[ 0].explanation_parameters): @@ -1769,7 +1769,7 @@ def explain( "the model with the explanation_metadata and explanation_parameters arguments " "specified." ) - + ''' response = self._http_request( method="POST", url=self.explain_http_uri, From 7c8687aeedd7d2f820a70c6cce05383324918dc6 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Thu, 31 Mar 2022 15:47:48 -0700 Subject: [PATCH 019/108] adding print statement for debugging explain --- google/cloud/aiplatform/models.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 7f80ec13d2..a294319321 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1655,7 +1655,7 @@ def _http_request( ) # delete later, use for testing - print(response.data) + print(response) if response.status < 300: return response @@ -1719,8 +1719,6 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict prediction_response = json.loads(response.data) # remove this line, just testing - print(prediction_response) - print('==================================') print(response.data.decode("utf-8")) return Prediction( @@ -1760,16 +1758,7 @@ def explain( """ self.wait() self._sync_gca_resource_if_skipped() - ''' - if (self.explain_http_uri and not self._gca_resource.deployed_models[ - 0].explanation_metadata and not self._gca_resource.deployed_models[ - 0].explanation_parameters): - raise RuntimeError( - "This deployed model cannot do explanations. Please undeploy and redeploy " - "the model with the explanation_metadata and explanation_parameters arguments " - "specified." - ) - ''' + response = self._http_request( method="POST", url=self.explain_http_uri, @@ -1788,7 +1777,7 @@ def explain( def health_check(self) -> bool: """ Makes GET request to this private Endpoint's health check URI. Must be within network - that this private Endpoint + that this private Endpoint is in. """ self.wait() self._sync_gca_resource_if_skipped() From 4f2b0a1203648444d11c879bf6b779f323c26924 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 1 Apr 2022 13:23:59 -0700 Subject: [PATCH 020/108] using GET instead of POST for explain --- google/cloud/aiplatform/models.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index a294319321..d08aac871f 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1655,7 +1655,13 @@ def _http_request( ) # delete later, use for testing - print(response) + print(response.body) + print(response.fields) + print(response.headers) + print(response.retries) + print(response.json) + + if response.status < 300: return response @@ -1760,7 +1766,7 @@ def explain( self._sync_gca_resource_if_skipped() response = self._http_request( - method="POST", + method="GET", url=self.explain_http_uri, body=json.dumps({"instances": instances}), headers={"Content-Type": "application/json"}, From fe1e452aa176c8d587268c438c0a6fa6752e44ea Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 1 Apr 2022 13:34:49 -0700 Subject: [PATCH 021/108] using GET instead of POST for explain --- google/cloud/aiplatform/models.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index d08aac871f..99f6ae81a3 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1655,11 +1655,7 @@ def _http_request( ) # delete later, use for testing - print(response.body) - print(response.fields) - print(response.headers) - print(response.retries) - print(response.json) + print(response.items()) From 07e6bfdd59aafc7144f26795b2f90991081cdffc Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 1 Apr 2022 13:54:30 -0700 Subject: [PATCH 022/108] using GET instead of POST for explain --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 99f6ae81a3..6c6e1838e8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1655,7 +1655,7 @@ def _http_request( ) # delete later, use for testing - print(response.items()) + print(response.__dict__) From fc47135461c10ed26f44be5ae9ff5b1fe53d7f90 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 4 Apr 2022 16:34:13 -0700 Subject: [PATCH 023/108] removing explain for now, adding model id --- google/cloud/aiplatform/models.py | 60 +++---------------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 6c6e1838e8..1c2478c1bc 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1654,15 +1654,9 @@ def _http_request( method, url, body=body, headers=headers ) - # delete later, use for testing - print(response.__dict__) - - - if response.status < 300: return response else: - # add if statements for explain vs. predict 404 errors raise RuntimeError( f"{response.status} - Failed to make request, see response: " + response.data.decode("utf-8") ) @@ -1720,60 +1714,14 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict prediction_response = json.loads(response.data) - # remove this line, just testing - print(response.data.decode("utf-8")) - return Prediction( predictions=prediction_response.get("predictions"), - deployed_model_id=prediction_response.get("deployedModelId"), - ) - - def explain( - self, instances: List[Dict], parameters: Optional[Dict] = None, - ) -> Prediction: - """Make a prediction with explanations against this private Endpoint. - - Example usage: - response = my_private_endpoint.explain(instances=[...]) - my_explanations = response.explanations - - Args: - instances (List): - Required. The instances that are the input to the - prediction call. A DeployedModel may have an upper limit - on the number of instances it supports per request, and - when it is exceeded the prediction call errors in case - of AutoML Models, or, in case of customer created - Models, the behaviour is as documented by that Model. - The schema of any single instance may be specified via - Endpoint's DeployedModels' - [Model's][google.cloud.aiplatform.v1beta1.DeployedModel.model] - [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] - ``instance_schema_uri``. - parameters (Dict): - The parameters that govern the prediction. The schema of - the parameters may be specified via Endpoint's - DeployedModels' [Model's - ][google.cloud.aiplatform.v1beta1.DeployedModel.model] - [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] - ``parameters_schema_uri``. - """ - self.wait() - self._sync_gca_resource_if_skipped() - - response = self._http_request( - method="GET", - url=self.explain_http_uri, - body=json.dumps({"instances": instances}), - headers={"Content-Type": "application/json"}, + deployed_model_id=self._gca_resource.deployed_models[0].id, ) - prediction_response = response.data.decode("utf-8") - - return Prediction( - predictions=prediction_response.get("predictions"), - deployed_model_id=prediction_response.get("deployedModelId"), - explanations=prediction_response.get("explanations"), + def explain(self): + raise NotImplementedError( + f"{self.__class__.__name__} class does not support 'explain' as of now." ) def health_check(self) -> bool: From 8b59894fdfc86b735a9bf4c0c27f447798eebe50 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 4 Apr 2022 16:34:34 -0700 Subject: [PATCH 024/108] removing explain for now, adding model id --- google/cloud/aiplatform/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 1c2478c1bc..eb23553515 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1719,10 +1719,10 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict deployed_model_id=self._gca_resource.deployed_models[0].id, ) - def explain(self): - raise NotImplementedError( - f"{self.__class__.__name__} class does not support 'explain' as of now." - ) + #def explain(self): + # raise NotImplementedError( + # f"{self.__class__.__name__} class does not support 'explain' as of now." + # ) def health_check(self) -> bool: """ From 6b96c2a9cb20635526b2ddbb7378b923917e5754 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 4 Apr 2022 16:41:47 -0700 Subject: [PATCH 025/108] removing explain for now, adding model id --- google/cloud/aiplatform/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index eb23553515..1c2478c1bc 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1719,10 +1719,10 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict deployed_model_id=self._gca_resource.deployed_models[0].id, ) - #def explain(self): - # raise NotImplementedError( - # f"{self.__class__.__name__} class does not support 'explain' as of now." - # ) + def explain(self): + raise NotImplementedError( + f"{self.__class__.__name__} class does not support 'explain' as of now." + ) def health_check(self) -> bool: """ From 160c499fcd79837bfc037bd26257229e7aac35b3 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 5 Apr 2022 14:50:18 -0700 Subject: [PATCH 026/108] cleaned and added docstrings --- google/cloud/aiplatform/models.py | 128 ++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 24 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 1c2478c1bc..00880535a0 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -253,6 +253,7 @@ def create( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Returns: endpoint (endpoint.Endpoint): Created endpoint. @@ -347,6 +348,7 @@ def _create( Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to create this endpoint synchronously. + Returns: endpoint (endpoint.Endpoint): Created endpoint. @@ -433,6 +435,7 @@ def _allocate_traffic( Required. Current traffic split of deployed models in endpoint. traffic_percentage (int): Required. Desired traffic to new deployed model. + Returns: new_traffic_split (Dict[str, int]): Traffic split to use. @@ -469,6 +472,7 @@ def _unallocate_traffic( Required. Current traffic split of deployed models in endpoint. deployed_model_id (str): Required. Desired traffic to new deployed model. + Returns: new_traffic_split (Dict[str, int]): Traffic split to use. @@ -808,6 +812,7 @@ def _deploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Raises: ValueError: If there is not current traffic split and traffic percentage is not 0 or 100. @@ -931,6 +936,7 @@ def _deploy_call( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Raises: ValueError: If there is not current traffic split and traffic percentage is not 0 or 100. @@ -1187,6 +1193,7 @@ def _instantiate_prediction_client( credentials (google.auth.credentials.Credentials): Optional custom credentials to use when accessing interacting with the prediction client. + Returns: prediction_client (prediction_service_client.PredictionServiceClient): Initialized prediction client with optional overrides. @@ -1221,6 +1228,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ][google.cloud.aiplatform.v1beta1.DeployedModel.model] [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] ``parameters_schema_uri``. + Returns: prediction: Prediction with returned predictions and Model Id. """ @@ -1247,6 +1255,7 @@ def explain( """Make a prediction with explanations against this Endpoint. Example usage: + response = my_endpoint.explain(instances=[...]) my_explanations = response.explanations @@ -1273,6 +1282,7 @@ def explain( deployed_model_id (str): Optional. If specified, this ExplainRequest will be served by the chosen DeployedModel, overriding this Endpoint's traffic split. + Returns: prediction: Prediction with returned predictions, explanations, and Model Id. """ @@ -1330,7 +1340,7 @@ def list( credentials set in aiplatform.init. Returns: - List[models.Endpoint] - A list of Endpoint resource objects + List[models.Endpoint]: A list of Endpoint resource objects """ return cls._list_with_local_order( @@ -1387,6 +1397,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Raises: FailedPrecondition: If models are deployed on this Endpoint and force = False. """ @@ -1413,10 +1424,13 @@ def __init__( """Retrieves a private Endpoint resource. Example usage: + my_private_endpoint = aiplatform.PrivateEndpoint( endpoint_name="projects/123/locations/us-central1/endpoints/my_endpoint_id" ) + or (when project and location are initialized) + my_private_endpoint = aiplatform.PrivateEndpoint( endpoint_name='my_endpoint_id' ) @@ -1504,13 +1518,16 @@ def create( """Creates a new private Endpoint. Example usage: + my_private_endpoint = aiplatform.PrivateEndpoint.create( display_name='my_endpoint_name', project='123', location='us-central1' network='my_vpc' ) + or (when project and location are initialized) + my_private_endpoint = aiplatform.PrivateEndpoint.create( display_name='my_endpoint_name', network='my_vpc' @@ -1648,6 +1665,24 @@ def _http_request( body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> urllib3.response.HTTPResponse: + """Helper function used to perform HTTP requests for private Endpoint. + + Args: + method (str): + Required. The HTTP request method to use. Example: "POST" or "GET" + url (str): + Required. Project to construct Endpoint object from. If not set, + project set in aiplatform.init will be used. + body (Dict[Any, Any]): + Optional. Data sent to the url in the HTTP request. For private + Endpoint, an instance is sent and a prediction response is expected. + headers (Dict[str, str]): + Optional. Header in the HTTP request. + + Returns: + urllib3.response.HTTPResponse: + An initialized PrivateEndpoint resource. + """ try: response = self._http_client.request( @@ -1675,6 +1710,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. Example usage: + response = my_private_endpoint.predict(instances=[...]) my_predictions = response.predictions @@ -1728,6 +1764,14 @@ def health_check(self) -> bool: """ Makes GET request to this private Endpoint's health check URI. Must be within network that this private Endpoint is in. + + Example Usage: + + if my_private_endpoint.health_check(): + print("Endpoint is healthy!") + + Returns: + bool: Checks if calls can be made to this private Endpoint. """ self.wait() self._sync_gca_resource_if_skipped() @@ -1748,8 +1792,11 @@ def list( """List all private Endpoint resource instances. Example Usage: + my_private_endpoints = aiplatform.PrivateEndpoint.list() + or + my_private_endpoints = aiplatform.PrivateEndpoint.list( filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', ) @@ -1806,6 +1853,12 @@ def deploy( ) -> None: """Deploys a Model to the private Endpoint. + Example Usage: + + my_private_endpoint.deploy( + model=my_model + ) + Args: model (aiplatform.Model): Required. Model to be deployed. @@ -1896,6 +1949,19 @@ def deploy( def undeploy(self, deployed_model_id: str, sync=True,) -> None: """Undeploys a deployed model from the private Endpoint. + Example Usage: + + my_private_endpoint.undeploy( + deployed_model_id="1234567891232567891" + ) + + or + + my_deployed_model_id = my_private_endpoint.list_models()[0].id + my_private_endpoint.undeploy( + deployed_model_id=my_deployed_model_id + ) + Args: deployed_model_id (str): Required. The ID of the DeployedModel to be undeployed from the @@ -2094,11 +2160,11 @@ def update( Example usage: - my_model = my_model.update( - display_name='my-model', - description='my description', - labels={'key': 'value'}, - ) + my_model = my_model.update( + display_name='my-model', + description='my description', + labels={'key': 'value'}, + ) Args: display_name (str): @@ -2116,8 +2182,10 @@ def update( are allowed. See https://goo.gl/xmQnxf for more information and examples of labels. + Returns: model: Updated model resource. + Raises: ValueError: If `labels` is not the correct format. """ @@ -2188,11 +2256,11 @@ def upload( Example usage: - my_model = Model.upload( - display_name='my-model', - artifact_uri='gs://my-model/saved-model' - serving_container_image_uri='tensorflow/serving' - ) + my_model = Model.upload( + display_name='my-model', + artifact_uri='gs://my-model/saved-model' + serving_container_image_uri='tensorflow/serving' + ) Args: display_name (str): @@ -2327,8 +2395,10 @@ def upload( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + Returns: model: Instantiated representation of the uploaded model resource. + Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` is specified. @@ -2540,6 +2610,7 @@ def deploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Returns: endpoint ("Endpoint"): Endpoint with the deployed model. @@ -2695,6 +2766,7 @@ def _deploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Returns: endpoint ("Endpoint"): Endpoint with the deployed model. @@ -2780,12 +2852,12 @@ def batch_predict( Example usage: - my_model.batch_predict( - job_display_name="prediction-123", - gcs_source="gs://example-bucket/instances.csv", - instances_format="csv", - bigquery_destination_prefix="projectId.bqDatasetId.bqTableId" - ) + my_model.batch_predict( + job_display_name="prediction-123", + gcs_source="gs://example-bucket/instances.csv", + instances_format="csv", + bigquery_destination_prefix="projectId.bqDatasetId.bqTableId" + ) Args: job_display_name (str): @@ -2925,6 +2997,7 @@ def batch_predict( If set, this Model and all sub-resources of this Model will be secured by this key. Overrides encryption_spec_key_name set in aiplatform.init. + Returns: (jobs.BatchPredictionJob): Instantiated representation of the created batch prediction job. @@ -2969,9 +3042,9 @@ def list( Example Usage: - aiplatform.Model.list( - filter='labels.my_label="my_label_value" AND display_name="my_model"', - ) + aiplatform.Model.list( + filter='labels.my_label="my_label_value" AND display_name="my_model"', + ) Args: filter (str): @@ -3018,7 +3091,8 @@ def export_model( A Model is considered to be exportable if it has at least one `supported_export_formats`. Either `artifact_destination` or `image_destination` must be provided. - Usage: + Example Usage: + my_model.export( export_format_id='tf-saved-model' artifact_destination='gs://my-bucket/models/' @@ -3170,7 +3244,7 @@ def upload_xgboost_model_file( Note: This function is *experimental* and can be changed in the future. - Example usage:: + Example usage: my_model = Model.upload_xgboost_model_file( model_file_path="iris.xgboost_model.bst" @@ -3273,8 +3347,10 @@ def upload_xgboost_model_file( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + Returns: model: Instantiated representation of the uploaded model resource. + Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` is specified. @@ -3367,7 +3443,7 @@ def upload_scikit_learn_model_file( Note: This function is *experimental* and can be changed in the future. - Example usage:: + Example usage: my_model = Model.upload_scikit_learn_model_file( model_file_path="iris.sklearn_model.joblib" @@ -3471,8 +3547,10 @@ def upload_scikit_learn_model_file( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + Returns: model: Instantiated representation of the uploaded model resource. + Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` is specified. @@ -3564,7 +3642,7 @@ def upload_tensorflow_saved_model( Note: This function is *experimental* and can be changed in the future. - Example usage:: + Example usage: my_model = Model.upload_scikit_learn_model_file( model_file_path="iris.tensorflow_model.SavedModel" @@ -3670,8 +3748,10 @@ def upload_tensorflow_saved_model( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + Returns: model: Instantiated representation of the uploaded model resource. + Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` is specified. From e1ce3a462202345f3daa0f4551befd9cc23c3d11 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Thu, 7 Apr 2022 11:26:40 -0700 Subject: [PATCH 027/108] adding explain back --- google/cloud/aiplatform/models.py | 60 ++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 137ed14e15..363fe7ee9a 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -29,6 +29,7 @@ from google.cloud import aiplatform from google.cloud.aiplatform import base +from google.cloud.aiplatform import explain from google.cloud.aiplatform import initializer from google.cloud.aiplatform import jobs from google.cloud.aiplatform import models @@ -1750,7 +1751,7 @@ def _http_request( ) def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction against this private Endpoint using unauthenticated HTTP. + """Make a prediction against this private Endpoint using a HTTP request. This method must be called within the network the private Endpoint is peered to. The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. @@ -1800,9 +1801,58 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict deployed_model_id=self._gca_resource.deployed_models[0].id, ) - def explain(self): - raise NotImplementedError( - f"{self.__class__.__name__} class does not support 'explain' as of now." + def explain(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: + """Make a prediction with an explanation against this private Endpoint using a HTTP + request. An explanation will only be produced if the `explanation_metadata` and + `explaination_parameters` fields are specified when deploying a model. + + Example usage: + + response = my_private_endpoint.explain(instances=[...]) + my_explanations = response.explanations + + Args: + instances (List): + Required. The instances that are the input to the + prediction call. A DeployedModel may have an upper limit + on the number of instances it supports per request, and + when it is exceeded the prediction call errors in case + of AutoML Models, or, in case of customer created + Models, the behaviour is as documented by that Model. + The schema of any single instance may be specified via + Endpoint's DeployedModels' + [Model's][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``instance_schema_uri``. + parameters (Dict): + The parameters that govern the prediction. The schema of + the parameters may be specified via Endpoint's + DeployedModels' [Model's + ][google.cloud.aiplatform.v1beta1.DeployedModel.model] + [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] + ``parameters_schema_uri``. + + Returns: + prediction: Prediction with returned predictions and Model Id. + """ + self.wait() + self._sync_gca_resource_if_skipped() + + response = self._http_request( + method="POST", + url=self.explain_http_uri, + body=json.dumps({"instances": instances}), + headers={"Content-Type": "application/json"}, + ) + + prediction_response = json.loads(response.data) + # testing prediction + print(prediction_response) + + return Prediction( + predictions=prediction_response.get("predictions"), + deployed_model_id=self._gca_resource.deployed_models[0].id, + explanations=prediction_response.get("explanations"), ) def health_check(self) -> bool: @@ -2272,7 +2322,6 @@ def update( @base.optional_sync() def upload( cls, - display_name: Optional[str] = None, serving_container_image_uri: str, *, artifact_uri: Optional[str] = None, @@ -2288,6 +2337,7 @@ def upload( prediction_schema_uri: Optional[str] = None, explanation_metadata: Optional[explain.ExplanationMetadata] = None, explanation_parameters: Optional[explain.ExplanationParameters] = None, + display_name: Optional[str] = None, project: Optional[str] = None, location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, From 9f2d4789dd1a7a3820f223f466bc191c81bb6c64 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 7 Apr 2022 18:29:19 +0000 Subject: [PATCH 028/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 363fe7ee9a..551434c0a8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -258,7 +258,7 @@ def create( be immediately returned and synced when the Future has completed. create_request_timeout (float): Optional. The timeout for the create request in seconds. - + Returns: endpoint (endpoint.Endpoint): Created endpoint. @@ -1739,7 +1739,8 @@ def _http_request( return response else: raise RuntimeError( - f"{response.status} - Failed to make request, see response: " + response.data.decode("utf-8") + f"{response.status} - Failed to make request, see response: " + + response.data.decode("utf-8") ) except urllib3.exceptions.MaxRetryError: @@ -1802,8 +1803,8 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ) def explain(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction with an explanation against this private Endpoint using a HTTP - request. An explanation will only be produced if the `explanation_metadata` and + """Make a prediction with an explanation against this private Endpoint using a HTTP + request. An explanation will only be produced if the `explanation_metadata` and `explaination_parameters` fields are specified when deploying a model. Example usage: @@ -1864,14 +1865,17 @@ def health_check(self) -> bool: if my_private_endpoint.health_check(): print("Endpoint is healthy!") - + Returns: bool: Checks if calls can be made to this private Endpoint. """ self.wait() self._sync_gca_resource_if_skipped() - response = self._http_request(method="GET", url=self.health_http_uri,) + response = self._http_request( + method="GET", + url=self.health_http_uri, + ) return response.status == 200 @@ -2041,7 +2045,11 @@ def deploy( sync=sync, ) - def undeploy(self, deployed_model_id: str, sync=True,) -> None: + def undeploy( + self, + deployed_model_id: str, + sync=True, + ) -> None: """Undeploys a deployed model from the private Endpoint. Example Usage: @@ -2061,12 +2069,13 @@ def undeploy(self, deployed_model_id: str, sync=True,) -> None: deployed_model_id (str): Required. The ID of the DeployedModel to be undeployed from the private Endpoint. Use PrivateEndpoint.list_models() to get the - deployed model ID. + deployed model ID. """ self._sync_gca_resource_if_skipped() self._undeploy( - deployed_model_id=deployed_model_id, sync=sync, + deployed_model_id=deployed_model_id, + sync=sync, ) From f46a555506ce10fa48e75cf149bc7a7e128fb4e9 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Thu, 7 Apr 2022 13:27:44 -0700 Subject: [PATCH 029/108] removing explain until working --- google/cloud/aiplatform/models.py | 57 +++---------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 363fe7ee9a..361015ed8d 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1751,7 +1751,7 @@ def _http_request( ) def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction against this private Endpoint using a HTTP request. + """Make a prediction against this private Endpoint using unauthenticated HTTP. This method must be called within the network the private Endpoint is peered to. The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. @@ -1801,58 +1801,9 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict deployed_model_id=self._gca_resource.deployed_models[0].id, ) - def explain(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction with an explanation against this private Endpoint using a HTTP - request. An explanation will only be produced if the `explanation_metadata` and - `explaination_parameters` fields are specified when deploying a model. - - Example usage: - - response = my_private_endpoint.explain(instances=[...]) - my_explanations = response.explanations - - Args: - instances (List): - Required. The instances that are the input to the - prediction call. A DeployedModel may have an upper limit - on the number of instances it supports per request, and - when it is exceeded the prediction call errors in case - of AutoML Models, or, in case of customer created - Models, the behaviour is as documented by that Model. - The schema of any single instance may be specified via - Endpoint's DeployedModels' - [Model's][google.cloud.aiplatform.v1beta1.DeployedModel.model] - [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] - ``instance_schema_uri``. - parameters (Dict): - The parameters that govern the prediction. The schema of - the parameters may be specified via Endpoint's - DeployedModels' [Model's - ][google.cloud.aiplatform.v1beta1.DeployedModel.model] - [PredictSchemata's][google.cloud.aiplatform.v1beta1.Model.predict_schemata] - ``parameters_schema_uri``. - - Returns: - prediction: Prediction with returned predictions and Model Id. - """ - self.wait() - self._sync_gca_resource_if_skipped() - - response = self._http_request( - method="POST", - url=self.explain_http_uri, - body=json.dumps({"instances": instances}), - headers={"Content-Type": "application/json"}, - ) - - prediction_response = json.loads(response.data) - # testing prediction - print(prediction_response) - - return Prediction( - predictions=prediction_response.get("predictions"), - deployed_model_id=self._gca_resource.deployed_models[0].id, - explanations=prediction_response.get("explanations"), + def explain(self): + raise NotImplementedError( + f"{self.__class__.__name__} class does not support 'explain' as of now." ) def health_check(self) -> bool: From 9c47d745aad54a5414271a33a179fec4d85f1e2d Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 8 Apr 2022 11:22:46 -0700 Subject: [PATCH 030/108] cleaning up docstrings --- google/cloud/aiplatform/models.py | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index fe2e6cea7d..f8b06c8711 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1478,7 +1478,7 @@ def __init__( or (when project and location are initialized) my_private_endpoint = aiplatform.PrivateEndpoint( - endpoint_name='my_endpoint_id' + endpoint_name="my_endpoint_id" ) Args: @@ -1566,17 +1566,17 @@ def create( Example usage: my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name='my_endpoint_name', - project='123', - location='us-central1' - network='my_vpc' + display_name="my_endpoint_name", + project="my_project_id", + location="us-central1", + network="projects/123/global/networks/my_vpc" ) or (when project and location are initialized) my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name='my_endpoint_name', - network='my_vpc' + display_name="my_endpoint_name", + network="projects/123/global/networks/my_vpc" ) Args: @@ -2216,8 +2216,8 @@ def update( Example usage: my_model = my_model.update( - display_name='my-model', - description='my description', + display_name="my-model", + description="my description", labels={'key': 'value'}, ) @@ -2313,9 +2313,9 @@ def upload( Example usage: my_model = Model.upload( - display_name='my-model', - artifact_uri='gs://my-model/saved-model' - serving_container_image_uri='tensorflow/serving' + display_name="my-model", + artifact_uri="gs://my-model/saved-model", + serving_container_image_uri="tensorflow/serving" ) Args: @@ -3170,15 +3170,15 @@ def export_model( Example Usage: my_model.export( - export_format_id='tf-saved-model' - artifact_destination='gs://my-bucket/models/' + export_format_id="tf-saved-model", + artifact_destination="gs://my-bucket/models/" ) or my_model.export( - export_format_id='custom-model' - image_destination='us-central1-docker.pkg.dev/projectId/repo/image' + export_format_id="custom-model", + image_destination="us-central1-docker.pkg.dev/projectId/repo/image" ) Args: From 6d80cf0b582bfc5153b7f34c295df8c978c45705 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 12 Apr 2022 16:29:55 -0700 Subject: [PATCH 031/108] create test done, working on predict --- tests/unit/aiplatform/test_endpoints.py | 118 +++++++++++++++++++++++- 1 file changed, 115 insertions(+), 3 deletions(-) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 46576f0fd5..21cf4dd0be 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -15,8 +15,11 @@ # limitations under the License. # +from cgi import test import copy import pytest +import urllib3 +import json from unittest import mock from importlib import reload @@ -50,6 +53,7 @@ encryption_spec as gca_encryption_spec, ) + _TEST_PROJECT = "test-project" _TEST_PROJECT_2 = "test-project-2" _TEST_LOCATION = "us-central1" @@ -178,6 +182,14 @@ _TEST_LABELS = {"my_key": "my_value"} + +""" +---------------------------------------------------------------------------- +Endpoint Fixtures +---------------------------------------------------------------------------- +""" + + @pytest.fixture def get_endpoint_mock(): with mock.patch.object( @@ -227,6 +239,7 @@ def get_endpoint_with_models_mock(): yield get_endpoint_mock + @pytest.fixture def get_endpoint_with_many_models_mock(): with mock.patch.object( @@ -379,6 +392,7 @@ def predict_client_predict_mock(): with mock.patch.object( prediction_service_client.PredictionServiceClient, "predict" ) as predict_mock: + print(predict_mock) predict_mock.return_value = gca_prediction_service.PredictResponse( deployed_model_id=_TEST_MODEL_ID ) @@ -402,6 +416,55 @@ def predict_client_explain_mock(): yield predict_mock +""" +---------------------------------------------------------------------------- +Private Endpoint Fixtures +---------------------------------------------------------------------------- +""" + + +@pytest.fixture +def create_private_endpoint_mock(): + with mock.patch.object( + endpoint_service_client.EndpointServiceClient, "create_endpoint" + ) as create_private_endpoint_mock: + create_private_endpoint_lro_mock = mock.Mock(ga_operation.Operation) + create_private_endpoint_lro_mock.result.return_value = gca_endpoint.Endpoint( + name=_TEST_ENDPOINT_NAME, + display_name=_TEST_DISPLAY_NAME, + network=_TEST_NETWORK + ) + create_private_endpoint_mock.return_value = create_private_endpoint_lro_mock + yield create_private_endpoint_mock + + +@pytest.fixture +def get_private_endpoint_with_model_mock(): + with mock.patch.object( + endpoint_service_client.EndpointServiceClient, "get_endpoint" + ) as get_endpoint_mock: + get_endpoint_mock.return_value = gca_endpoint.Endpoint( + display_name=_TEST_DISPLAY_NAME, + name=_TEST_ENDPOINT_NAME, + network=_TEST_NETWORK, + deployed_models=[_TEST_DEPLOYED_MODELS[0]], + traffic_split=_TEST_TRAFFIC_SPLIT, + ) + yield get_endpoint_mock + + +@pytest.fixture +def predict_private_endpoint_mock(): + with mock.patch.object(aiplatform.PrivateEndpoint, "predict") as predict_mock: + + predict_mock.return_value = gca_prediction_service.PredictResponse( + deployed_model_id=_TEST_MODEL_ID + ) + predict_mock.return_value.predictions.extend(_TEST_PREDICTION) + yield predict_mock + + + class TestEndpoint: def setup_method(self): reload(initializer) @@ -1287,7 +1350,8 @@ def test_undeploy_zero_traffic_model_without_new_traffic_split( metadata=(), ) - def test_predict(self, get_endpoint_mock, predict_client_predict_mock): + @pytest.mark.usefixtures("get_endpoint_mock") + def test_predict(self, predict_client_predict_mock): test_endpoint = models.Endpoint(_TEST_ID) test_prediction = test_endpoint.predict( @@ -1298,7 +1362,8 @@ def test_predict(self, get_endpoint_mock, predict_client_predict_mock): predictions=_TEST_PREDICTION, deployed_model_id=_TEST_ID ) - assert true_prediction == test_prediction + print(true_prediction, test_prediction) + assert true_prediction != test_prediction predict_client_predict_mock.assert_called_once_with( endpoint=_TEST_ENDPOINT_NAME, instances=_TEST_INSTANCES, @@ -1306,7 +1371,8 @@ def test_predict(self, get_endpoint_mock, predict_client_predict_mock): timeout=None, ) - def test_explain(self, get_endpoint_mock, predict_client_explain_mock): + @pytest.mark.usefixtures("get_endpoint_mock") + def test_explain(self, predict_client_explain_mock): test_endpoint = models.Endpoint(_TEST_ID) test_prediction = test_endpoint.explain( @@ -1492,6 +1558,52 @@ def test_delete_endpoint_with_force( class TestPrivateEndpoint(TestEndpoint): + @pytest.mark.parametrize("sync", [True, False]) + def test_create(self, create_private_endpoint_mock, sync): + my_endpoint = models.PrivateEndpoint.create( + display_name=_TEST_DISPLAY_NAME, + project=_TEST_PROJECT, + location=_TEST_LOCATION, + network=_TEST_NETWORK, + sync=sync, + create_request_timeout=None, + ) + + if not sync: + my_endpoint.wait() + + expected_endpoint = gca_endpoint.Endpoint( + display_name=_TEST_DISPLAY_NAME, + network=_TEST_NETWORK + ) + + create_private_endpoint_mock.assert_called_once_with( + parent=_TEST_PARENT, + endpoint=expected_endpoint, + metadata=(), + timeout=None, + ) + + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + def test_predict(self, predict_private_endpoint_mock): + test_endpoint = models.PrivateEndpoint(_TEST_ID) + test_prediction = test_endpoint.predict( + instances=_TEST_INSTANCES, parameters={"param": 3.0} + ) + + true_prediction = models.Prediction( + predictions=_TEST_PREDICTION, deployed_model_id=_TEST_ID + ) + + print(test_prediction, true_prediction) + assert true_prediction == test_prediction + predict_private_endpoint_mock.assert_called_once_with( + endpoint=_TEST_ENDPOINT_NAME, + instances=_TEST_INSTANCES, + parameters={"param": 3.0}, + timeout=None, + ) + def test_http_health_check(self): pass From f95cd29bb069ba1a3a762aa3cc9f0ef260b082aa Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 12 Apr 2022 23:32:50 +0000 Subject: [PATCH 032/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/unit/aiplatform/test_endpoints.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 21cf4dd0be..cb3afcdfa3 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -182,7 +182,6 @@ _TEST_LABELS = {"my_key": "my_value"} - """ ---------------------------------------------------------------------------- Endpoint Fixtures @@ -239,7 +238,6 @@ def get_endpoint_with_models_mock(): yield get_endpoint_mock - @pytest.fixture def get_endpoint_with_many_models_mock(): with mock.patch.object( @@ -432,7 +430,7 @@ def create_private_endpoint_mock(): create_private_endpoint_lro_mock.result.return_value = gca_endpoint.Endpoint( name=_TEST_ENDPOINT_NAME, display_name=_TEST_DISPLAY_NAME, - network=_TEST_NETWORK + network=_TEST_NETWORK, ) create_private_endpoint_mock.return_value = create_private_endpoint_lro_mock yield create_private_endpoint_mock @@ -464,7 +462,6 @@ def predict_private_endpoint_mock(): yield predict_mock - class TestEndpoint: def setup_method(self): reload(initializer) @@ -1573,8 +1570,7 @@ def test_create(self, create_private_endpoint_mock, sync): my_endpoint.wait() expected_endpoint = gca_endpoint.Endpoint( - display_name=_TEST_DISPLAY_NAME, - network=_TEST_NETWORK + display_name=_TEST_DISPLAY_NAME, network=_TEST_NETWORK ) create_private_endpoint_mock.assert_called_once_with( From f3c77e5c5681addd4dd259a896d6b3e65a2ec5cc Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 13 Apr 2022 18:22:20 -0700 Subject: [PATCH 033/108] added unit tests for PrivateEndpoint --- tests/unit/aiplatform/test_endpoints.py | 134 ++++++++++++++++-------- 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 21cf4dd0be..3ec8aeebdc 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -15,7 +15,6 @@ # limitations under the License. # -from cgi import test import copy import pytest import urllib3 @@ -175,6 +174,15 @@ ), ] +_TEST_PRIVATE_ENDPOINT_LIST = [ + gca_endpoint.Endpoint( + name=_TEST_ENDPOINT_NAME, + display_name="aac", + create_time=datetime.now() - timedelta(minutes=15), + network=_TEST_NETWORK, + ), +] + _TEST_LIST_FILTER = 'display_name="abc"' _TEST_LIST_ORDER_BY_CREATE_TIME = "create_time desc" _TEST_LIST_ORDER_BY_DISPLAY_NAME = "display_name" @@ -182,7 +190,6 @@ _TEST_LABELS = {"my_key": "my_value"} - """ ---------------------------------------------------------------------------- Endpoint Fixtures @@ -239,7 +246,6 @@ def get_endpoint_with_models_mock(): yield get_endpoint_mock - @pytest.fixture def get_endpoint_with_many_models_mock(): with mock.patch.object( @@ -392,7 +398,6 @@ def predict_client_predict_mock(): with mock.patch.object( prediction_service_client.PredictionServiceClient, "predict" ) as predict_mock: - print(predict_mock) predict_mock.return_value = gca_prediction_service.PredictResponse( deployed_model_id=_TEST_MODEL_ID ) @@ -432,7 +437,7 @@ def create_private_endpoint_mock(): create_private_endpoint_lro_mock.result.return_value = gca_endpoint.Endpoint( name=_TEST_ENDPOINT_NAME, display_name=_TEST_DISPLAY_NAME, - network=_TEST_NETWORK + network=_TEST_NETWORK, ) create_private_endpoint_mock.return_value = create_private_endpoint_lro_mock yield create_private_endpoint_mock @@ -448,22 +453,34 @@ def get_private_endpoint_with_model_mock(): name=_TEST_ENDPOINT_NAME, network=_TEST_NETWORK, deployed_models=[_TEST_DEPLOYED_MODELS[0]], - traffic_split=_TEST_TRAFFIC_SPLIT, ) yield get_endpoint_mock @pytest.fixture def predict_private_endpoint_mock(): - with mock.patch.object(aiplatform.PrivateEndpoint, "predict") as predict_mock: - - predict_mock.return_value = gca_prediction_service.PredictResponse( - deployed_model_id=_TEST_MODEL_ID + with mock.patch.object(urllib3.PoolManager, "request") as predict_mock: + predict_mock.return_value = urllib3.response.HTTPResponse( + status=200, body=json.dumps({"predictions": _TEST_PREDICTION}) ) - predict_mock.return_value.predictions.extend(_TEST_PREDICTION) yield predict_mock +@pytest.fixture +def health_check_private_endpoint_mock(): + with mock.patch.object(urllib3.PoolManager, "request") as health_check_mock: + health_check_mock.return_value = urllib3.response.HTTPResponse(status=200) + yield health_check_mock + + +@pytest.fixture +def list_private_endpoints_mock(): + with mock.patch.object( + endpoint_service_client.EndpointServiceClient, "list_endpoints" + ) as list_endpoints_mock: + list_endpoints_mock.return_value = _TEST_PRIVATE_ENDPOINT_LIST + yield list_endpoints_mock + class TestEndpoint: def setup_method(self): @@ -1236,7 +1253,6 @@ def test_undeploy(self, undeploy_model_mock, sync): endpoint=test_endpoint.resource_name, deployed_model_id="model1", traffic_split={}, - # traffic_split={"model1": 0}, metadata=(), ) @@ -1362,8 +1378,7 @@ def test_predict(self, predict_client_predict_mock): predictions=_TEST_PREDICTION, deployed_model_id=_TEST_ID ) - print(true_prediction, test_prediction) - assert true_prediction != test_prediction + assert true_prediction == test_prediction predict_client_predict_mock.assert_called_once_with( endpoint=_TEST_ENDPOINT_NAME, instances=_TEST_INSTANCES, @@ -1566,15 +1581,13 @@ def test_create(self, create_private_endpoint_mock, sync): location=_TEST_LOCATION, network=_TEST_NETWORK, sync=sync, - create_request_timeout=None, ) if not sync: my_endpoint.wait() expected_endpoint = gca_endpoint.Endpoint( - display_name=_TEST_DISPLAY_NAME, - network=_TEST_NETWORK + display_name=_TEST_DISPLAY_NAME, network=_TEST_NETWORK ) create_private_endpoint_mock.assert_called_once_with( @@ -1595,41 +1608,78 @@ def test_predict(self, predict_private_endpoint_mock): predictions=_TEST_PREDICTION, deployed_model_id=_TEST_ID ) - print(test_prediction, true_prediction) assert true_prediction == test_prediction predict_private_endpoint_mock.assert_called_once_with( - endpoint=_TEST_ENDPOINT_NAME, - instances=_TEST_INSTANCES, - parameters={"param": 3.0}, - timeout=None, + method="POST", + url="", + body='{"instances": [[1.0, 2.0, 3.0], [1.0, 3.0, 4.0]]}', + headers={"Content-Type": "application/json"}, ) - def test_http_health_check(self): - pass + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + def test_health_check(self, health_check_private_endpoint_mock): + test_endpoint = models.PrivateEndpoint(_TEST_ID) + test_health_check = test_endpoint.health_check() - def test_http_health_check_unhealthy(self): - pass + true_health_check = True + + assert true_health_check == test_health_check + health_check_private_endpoint_mock.assert_called_once_with( + method="GET", url="", body=None, headers=None + ) + + @pytest.mark.usefixtures("get_endpoint_mock", "get_model_mock") + @pytest.mark.parametrize("sync", [True, False]) + def test_deploy(self, deploy_model_mock, sync): + test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) + test_model = models.Model(_TEST_ID) + test_model._gca_resource.supported_deployment_resources_types.append( + aiplatform.gapic.Model.DeploymentResourcesType.AUTOMATIC_RESOURCES + ) + test_endpoint.deploy( + test_model, + sync=sync, + ) - def test_http_predict(self): - pass + if not sync: + test_endpoint.wait() - def test_http_predict_no_response(self): - pass + automatic_resources = gca_machine_resources.AutomaticResources( + min_replica_count=1, + max_replica_count=1, + ) - def test_http_predict_404(self): - pass + deployed_model = gca_endpoint.DeployedModel( + automatic_resources=automatic_resources, + model=test_model.resource_name, + display_name=None, + ) - def test_http_predict_500(self): - pass + deploy_model_mock.assert_called_once_with( + endpoint=test_endpoint.resource_name, + deployed_model=deployed_model, + metadata=(), + timeout=None, + traffic_split=None, + ) - def test_deploy_private_endpoint(self): - pass + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.parametrize("sync", [True, False]) + def test_undeploy(self, undeploy_model_mock, sync): + test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) + test_endpoint.undeploy("model1", sync=sync) - def test_undeploy_private_endpoint(self): - pass + if not sync: + test_endpoint.wait() - def test_create_private_endpoint(self): - pass + undeploy_model_mock.assert_called_once_with( + endpoint=test_endpoint.resource_name, + deployed_model_id="model1", + metadata=(), + traffic_split={}, + ) - def test_list_private_endpoints(self): - pass + @pytest.mark.usefixtures("list_private_endpoints_mock") + def test_list(self): + ep_list = aiplatform.PrivateEndpoint.list() + assert ep_list # Ensure list is not empty From ee1102a8fee9a0f5fce5a5f63d452ef29b00caff Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 19 Apr 2022 12:48:41 -0700 Subject: [PATCH 034/108] test debugging --- google/cloud/aiplatform/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index f8b06c8711..2e086a36eb 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1732,7 +1732,7 @@ def _http_request( try: response = self._http_client.request( - method, url, body=body, headers=headers + method=method, url=url, body=body, headers=headers ) if response.status < 300: @@ -1797,6 +1797,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict prediction_response = json.loads(response.data) + print(prediction_response) return Prediction( predictions=prediction_response.get("predictions"), deployed_model_id=self._gca_resource.deployed_models[0].id, From ce11fafd2c9cb8d475cdab62b7bea2ad4f02145c Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 19 Apr 2022 12:57:04 -0700 Subject: [PATCH 035/108] fixing unit tests --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 869bf4d184..f42c23fb1f 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -2692,7 +2692,7 @@ def deploy( explanation_parameters, ) - if endpoint and endpoint.__class__ == "PrivateEndpoint"(): + if endpoint and endpoint.__class__ == "PrivateEndpoint": if traffic_percentage or traffic_split: raise ValueError( "Traffic splitting is not yet supported for private Endpoints. " From 5639ed74df7faf4b5cb036318523ecce7d5cf067 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 19 Apr 2022 14:07:03 -0700 Subject: [PATCH 036/108] adding fixes --- google/cloud/aiplatform/__init__.py | 2 +- google/cloud/aiplatform/initializer.py | 2 +- google/cloud/aiplatform/models.py | 37 +++++++++++++------------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/google/cloud/aiplatform/__init__.py b/google/cloud/aiplatform/__init__.py index 1407fe6d54..38d5c28115 100644 --- a/google/cloud/aiplatform/__init__.py +++ b/google/cloud/aiplatform/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/cloud/aiplatform/initializer.py b/google/cloud/aiplatform/initializer.py index 78da345a8f..c75eca35e5 100644 --- a/google/cloud/aiplatform/initializer.py +++ b/google/cloud/aiplatform/initializer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index f42c23fb1f..fc7b176413 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2020 Google LLC +# Copyright 2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -362,7 +362,7 @@ def _create( Optional. The timeout for the create request in seconds. Returns: - endpoint (endpoint.Endpoint): + endpoint (Endpoint): Created endpoint. """ @@ -1552,15 +1552,15 @@ def health_http_uri(self) -> Optional[str]: def create( cls, display_name: str, + project: Optional[str] = None, + location: Optional[str] = None, network: Optional[str] = None, description: Optional[str] = None, labels: Optional[Dict[str, str]] = None, - project: Optional[str] = None, - location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, encryption_spec_key_name: Optional[str] = None, sync=True, - ) -> "Endpoint": + ) -> "PrivateEndpoint": """Creates a new private Endpoint. Example usage: @@ -1616,19 +1616,16 @@ def create( form: ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. The key needs to be in the same region as where the compute - resource is created. - - If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. - - Overrides encryption_spec_key_name set in aiplatform.init. - + resource is created. If set, this Endpoint and all sub-resources of this + Endpoint will be secured by this key. Overrides encryption_spec_key_name + set in aiplatform.init. sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. Returns: - endpoint (endpoint.Endpoint): + endpoint (PrivateEndpoint): Created endpoint. """ api_client = cls._instantiate_client(location=location, credentials=credentials) @@ -2022,6 +2019,10 @@ def undeploy( Required. The ID of the DeployedModel to be undeployed from the private Endpoint. Use PrivateEndpoint.list_models() to get the deployed model ID. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. """ self._sync_gca_resource_if_skipped() @@ -2599,11 +2600,11 @@ def deploy( network: Optional[str] = None, sync=True, deploy_request_timeout: Optional[float] = None, - ) -> Endpoint: + ) -> Union[Endpoint, PrivateEndpoint]: """Deploys model to endpoint. Endpoint will be created if unspecified. Args: - endpoint (Union["Endpoint", "PrivateEndpoint"]): + endpoint (Union[Endpoint, PrivateEndpoint]): Optional. Public or private Endpoint to deploy model to. If not specified, endpoint display name will be model display name+'_endpoint'. deployed_model_display_name (str): @@ -2677,7 +2678,7 @@ def deploy( Optional. The timeout for the deploy request in seconds. Returns: - endpoint ("Endpoint"): + endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. """ @@ -2744,11 +2745,11 @@ def _deploy( network: Optional[str] = None, sync: bool = True, deploy_request_timeout: Optional[float] = None, - ) -> Endpoint: + ) -> Union[Endpoint, PrivateEndpoint]: """Deploys model to endpoint. Endpoint will be created if unspecified. Args: - endpoint (Union["Endpoint", "PrivateEndpoint"]): + endpoint (Union[Endpoint, PrivateEndpoint]): Optional. Public or private Endpoint to deploy model to. If not specified, endpoint display name will be model display name+'_endpoint'. deployed_model_display_name (str): @@ -2837,7 +2838,7 @@ def _deploy( Optional. The timeout for the deploy request in seconds. Returns: - endpoint ("Endpoint"): + endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. """ From c50954ff29be46941326a5b6e2415e1d1ad18460 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 19 Apr 2022 21:09:45 +0000 Subject: [PATCH 037/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index fc7b176413..d95324dc90 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1616,8 +1616,8 @@ def create( form: ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. The key needs to be in the same region as where the compute - resource is created. If set, this Endpoint and all sub-resources of this - Endpoint will be secured by this key. Overrides encryption_spec_key_name + resource is created. If set, this Endpoint and all sub-resources of this + Endpoint will be secured by this key. Overrides encryption_spec_key_name set in aiplatform.init. sync (bool): Whether to execute this method synchronously. If False, this method From fe737d89aff15e3aa6c8a0d1777c0fa11f0d9cb7 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 19 Apr 2022 14:13:05 -0700 Subject: [PATCH 038/108] formatting encryption_spec_key_name --- google/cloud/aiplatform/models.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index fc7b176413..8f59015e48 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1616,9 +1616,11 @@ def create( form: ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. The key needs to be in the same region as where the compute - resource is created. If set, this Endpoint and all sub-resources of this - Endpoint will be secured by this key. Overrides encryption_spec_key_name - set in aiplatform.init. + resource is created. + + If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. + + Overrides encryption_spec_key_name set in aiplatform.init. sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will @@ -2660,9 +2662,9 @@ def deploy( The key needs to be in the same region as where the compute resource is created. - If set, this Model and all sub-resources of this Model will be secured by this key. + If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. - Overrides encryption_spec_key_name set in aiplatform.init + Overrides encryption_spec_key_name set in aiplatform.init. network (str): Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". From 547aba9552be011c353345d2a7fe2ade2aa3e890 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 19 Apr 2022 21:16:34 +0000 Subject: [PATCH 039/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 136 +++++++++++++++--------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 82ed387be9..041d2ba6ba 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1563,78 +1563,78 @@ def create( ) -> "PrivateEndpoint": """Creates a new private Endpoint. - Example usage: - - my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name="my_endpoint_name", - project="my_project_id", - location="us-central1", - network="projects/123/global/networks/my_vpc" - ) - - or (when project and location are initialized) - - my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name="my_endpoint_name", - network="projects/123/global/networks/my_vpc" - ) + Example usage: - Args: - display_name (str): - Required. The user-defined name of the Endpoint. - The name can be up to 128 characters long and can be consist - of any UTF-8 characters. - project (str): - Required. Project to retrieve endpoint from. If not set, project - set in aiplatform.init will be used. - location (str): - Required. Location to retrieve endpoint from. If not set, location - set in aiplatform.init will be used. - network (str): - Required. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". - Private services access must already be configured for the network. - If not set, `network` set in aiplatform.init will be used. - description (str): - Optional. The description of the Endpoint. - labels (Dict[str, str]): - Optional. The labels with user-defined metadata to - organize your Endpoints. - Label keys and values can be no longer than 64 - characters (Unicode codepoints), can only - contain lowercase letters, numeric characters, - underscores and dashes. International characters - are allowed. - See https://goo.gl/xmQnxf for more information - and examples of labels. - credentials (auth_credentials.Credentials): - Optional. Custom credentials to use to upload this model. Overrides - credentials set in aiplatform.init. - encryption_spec_key_name (str): - Optional. The Cloud KMS resource identifier of the customer - managed encryption key used to protect the model. Has the - form: - ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. - The key needs to be in the same region as where the compute -<<<<<<< HEAD - resource is created. + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name="my_endpoint_name", + project="my_project_id", + location="us-central1", + network="projects/123/global/networks/my_vpc" + ) - If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. + or (when project and location are initialized) - Overrides encryption_spec_key_name set in aiplatform.init. -======= - resource is created. If set, this Endpoint and all sub-resources of this - Endpoint will be secured by this key. Overrides encryption_spec_key_name - set in aiplatform.init. ->>>>>>> c50954ff29be46941326a5b6e2415e1d1ad18460 - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name="my_endpoint_name", + network="projects/123/global/networks/my_vpc" + ) - Returns: - endpoint (PrivateEndpoint): - Created endpoint. + Args: + display_name (str): + Required. The user-defined name of the Endpoint. + The name can be up to 128 characters long and can be consist + of any UTF-8 characters. + project (str): + Required. Project to retrieve endpoint from. If not set, project + set in aiplatform.init will be used. + location (str): + Required. Location to retrieve endpoint from. If not set, location + set in aiplatform.init will be used. + network (str): + Required. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + Private services access must already be configured for the network. + If not set, `network` set in aiplatform.init will be used. + description (str): + Optional. The description of the Endpoint. + labels (Dict[str, str]): + Optional. The labels with user-defined metadata to + organize your Endpoints. + Label keys and values can be no longer than 64 + characters (Unicode codepoints), can only + contain lowercase letters, numeric characters, + underscores and dashes. International characters + are allowed. + See https://goo.gl/xmQnxf for more information + and examples of labels. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to upload this model. Overrides + credentials set in aiplatform.init. + encryption_spec_key_name (str): + Optional. The Cloud KMS resource identifier of the customer + managed encryption key used to protect the model. Has the + form: + ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. + The key needs to be in the same region as where the compute + <<<<<<< HEAD + resource is created. + + If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. + + Overrides encryption_spec_key_name set in aiplatform.init. + ======= + resource is created. If set, this Endpoint and all sub-resources of this + Endpoint will be secured by this key. Overrides encryption_spec_key_name + set in aiplatform.init. + >>>>>>> c50954ff29be46941326a5b6e2415e1d1ad18460 + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + + Returns: + endpoint (PrivateEndpoint): + Created endpoint. """ api_client = cls._instantiate_client(location=location, credentials=credentials) From a2c8387f22190cc7cc0b1a8a81cf12c0883d8283 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 20 Apr 2022 14:07:57 -0700 Subject: [PATCH 040/108] fixing comments --- google/cloud/aiplatform/models.py | 161 +++++++++++++----------------- 1 file changed, 68 insertions(+), 93 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index b0101c8eac..3186c17ed4 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1326,7 +1326,6 @@ def explain( """Make a prediction with explanations against this Endpoint. Example usage: - response = my_endpoint.explain(instances=[...]) my_explanations = response.explanations @@ -1389,10 +1388,9 @@ def list( """List all Endpoint resource instances. Example Usage: - - aiplatform.Endpoint.list( - filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', - ) + aiplatform.Endpoint.list( + filter='labels.my_label="my_label_value" OR display_name=!"old_endpoint"', + ) Args: filter (str): @@ -1497,7 +1495,6 @@ def __init__( """Retrieves a private Endpoint resource. Example usage: - my_private_endpoint = aiplatform.PrivateEndpoint( endpoint_name="projects/123/locations/us-central1/endpoints/my_endpoint_id" ) @@ -1590,78 +1587,71 @@ def create( ) -> "PrivateEndpoint": """Creates a new private Endpoint. - Example usage: + Example usage: + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name="my_endpoint_name", + project="my_project_id", + location="us-central1", + network="projects/123/global/networks/my_vpc" + ) - my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name="my_endpoint_name", - project="my_project_id", - location="us-central1", - network="projects/123/global/networks/my_vpc" - ) + or (when project and location are initialized) - or (when project and location are initialized) + my_private_endpoint = aiplatform.PrivateEndpoint.create( + display_name="my_endpoint_name", + network="projects/123/global/networks/my_vpc" + ) - my_private_endpoint = aiplatform.PrivateEndpoint.create( - display_name="my_endpoint_name", - network="projects/123/global/networks/my_vpc" - ) + Args: + display_name (str): + Required. The user-defined name of the Endpoint. + The name can be up to 128 characters long and can be consist + of any UTF-8 characters. + project (str): + Required. Project to retrieve endpoint from. If not set, project + set in aiplatform.init will be used. + location (str): + Required. Location to retrieve endpoint from. If not set, location + set in aiplatform.init will be used. + network (str): + Required. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + Private services access must already be configured for the network. + If not set, `network` set in aiplatform.init will be used. + description (str): + Optional. The description of the Endpoint. + labels (Dict[str, str]): + Optional. The labels with user-defined metadata to + organize your Endpoints. + Label keys and values can be no longer than 64 + characters (Unicode codepoints), can only + contain lowercase letters, numeric characters, + underscores and dashes. International characters + are allowed. + See https://goo.gl/xmQnxf for more information + and examples of labels. + credentials (auth_credentials.Credentials): + Optional. Custom credentials to use to upload this model. Overrides + credentials set in aiplatform.init. + encryption_spec_key_name (str): + Optional. The Cloud KMS resource identifier of the customer + managed encryption key used to protect the model. Has the + form: + ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. + The key needs to be in the same region as where the compute + resource is created. - Args: - display_name (str): - Required. The user-defined name of the Endpoint. - The name can be up to 128 characters long and can be consist - of any UTF-8 characters. - project (str): - Required. Project to retrieve endpoint from. If not set, project - set in aiplatform.init will be used. - location (str): - Required. Location to retrieve endpoint from. If not set, location - set in aiplatform.init will be used. - network (str): - Required. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". - Private services access must already be configured for the network. - If not set, `network` set in aiplatform.init will be used. - description (str): - Optional. The description of the Endpoint. - labels (Dict[str, str]): - Optional. The labels with user-defined metadata to - organize your Endpoints. - Label keys and values can be no longer than 64 - characters (Unicode codepoints), can only - contain lowercase letters, numeric characters, - underscores and dashes. International characters - are allowed. - See https://goo.gl/xmQnxf for more information - and examples of labels. - credentials (auth_credentials.Credentials): - Optional. Custom credentials to use to upload this model. Overrides - credentials set in aiplatform.init. - encryption_spec_key_name (str): - Optional. The Cloud KMS resource identifier of the customer - managed encryption key used to protect the model. Has the - form: - ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. - The key needs to be in the same region as where the compute - <<<<<<< HEAD - resource is created. - - If set, this Endpoint and all sub-resources of this Endpoint will be secured by this key. - - Overrides encryption_spec_key_name set in aiplatform.init. - ======= - resource is created. If set, this Endpoint and all sub-resources of this - Endpoint will be secured by this key. Overrides encryption_spec_key_name - set in aiplatform.init. - >>>>>>> c50954ff29be46941326a5b6e2415e1d1ad18460 - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. - - Returns: - endpoint (PrivateEndpoint): - Created endpoint. + If set, this Model and all sub-resources of this Model will be secured by this key. + + Overrides encryption_spec_key_name set in aiplatform.init. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + + Returns: + endpoint (PrivateEndpoint): + Created endpoint. """ api_client = cls._instantiate_client(location=location, credentials=credentials) @@ -1789,7 +1779,6 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. Example usage: - response = my_private_endpoint.predict(instances=[...]) my_predictions = response.predictions @@ -1846,7 +1835,6 @@ def health_check(self) -> bool: that this private Endpoint is in. Example Usage: - if my_private_endpoint.health_check(): print("Endpoint is healthy!") @@ -1875,7 +1863,6 @@ def list( """List all private Endpoint resource instances. Example Usage: - my_private_endpoints = aiplatform.PrivateEndpoint.list() or @@ -1937,7 +1924,6 @@ def deploy( """Deploys a Model to the private Endpoint. Example Usage: - my_private_endpoint.deploy( model=my_model ) @@ -2037,7 +2023,6 @@ def undeploy( """Undeploys a deployed model from the private Endpoint. Example Usage: - my_private_endpoint.undeploy( deployed_model_id="1234567891232567891" ) @@ -2251,7 +2236,6 @@ def update( """Updates a model. Example usage: - my_model = my_model.update( display_name="my-model", description="my description", @@ -2348,7 +2332,6 @@ def upload( resource. Example usage: - my_model = Model.upload( display_name="my-model", artifact_uri="gs://my-model/saved-model", @@ -2958,7 +2941,6 @@ def batch_predict( required. Example usage: - my_model.batch_predict( job_display_name="prediction-123", gcs_source="gs://example-bucket/instances.csv", @@ -3154,7 +3136,6 @@ def list( """List all Model resource instances. Example Usage: - aiplatform.Model.list( filter='labels.my_label="my_label_value" AND display_name="my_model"', ) @@ -3205,7 +3186,6 @@ def export_model( Either `artifact_destination` or `image_destination` must be provided. Example Usage: - my_model.export( export_format_id="tf-saved-model", artifact_destination="gs://my-bucket/models/" @@ -3361,7 +3341,6 @@ def upload_xgboost_model_file( Note: This function is *experimental* and can be changed in the future. Example usage: - my_model = Model.upload_xgboost_model_file( model_file_path="iris.xgboost_model.bst" ) @@ -3567,7 +3546,6 @@ def upload_scikit_learn_model_file( Note: This function is *experimental* and can be changed in the future. Example usage: - my_model = Model.upload_scikit_learn_model_file( model_file_path="iris.sklearn_model.joblib" ) @@ -3773,7 +3751,6 @@ def upload_tensorflow_saved_model( Note: This function is *experimental* and can be changed in the future. Example usage: - my_model = Model.upload_scikit_learn_model_file( model_file_path="iris.tensorflow_model.SavedModel" ) @@ -3925,12 +3902,11 @@ def list_model_evaluations( """List all Model Evaluation resources associated with this model. Example Usage: + my_model = Model( + model_name="projects/123/locations/us-central1/models/456" + ) - my_model = Model( - model_name="projects/123/locations/us-central1/models/456" - ) - - my_evaluations = my_model.list_model_evaluations() + my_evaluations = my_model.list_model_evaluations() Returns: List[model_evaluation.ModelEvaluation]: List of ModelEvaluation resources @@ -3953,7 +3929,6 @@ def get_model_evaluation( with this model. Example usage: - my_model = Model( model_name="projects/123/locations/us-central1/models/456" ) From a23523c351c5008408aa4a6b3d72885cfb758433 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 20 Apr 2022 15:14:19 -0700 Subject: [PATCH 041/108] adjusting traffic for private endpoint --- google/cloud/aiplatform/models.py | 43 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 3186c17ed4..e754aee5fd 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -553,7 +553,7 @@ def _validate_deploy_args( accelerator_type: Optional[str], deployed_model_display_name: Optional[str], traffic_split: Optional[Dict[str, int]], - traffic_percentage: int, + traffic_percentage: Optional[int], explanation_metadata: Optional[aiplatform.explain.ExplanationMetadata] = None, explanation_parameters: Optional[ aiplatform.explain.ExplanationParameters @@ -622,27 +622,23 @@ def _validate_deploy_args( raise ValueError("Max replica cannot be negative.") if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) - - # TODO(b/221059294): private Endpoints do not yet support traffic splitting - if cls == Endpoint: - if traffic_split is None: - if traffic_percentage > 100: - raise ValueError("Traffic percentage cannot be greater than 100.") - if traffic_percentage < 0: - raise ValueError("Traffic percentage cannot be negative.") - - elif traffic_split: - # TODO(b/172678233) verify every referenced deployed model exists - if sum(traffic_split.values()) != 100: - raise ValueError( - "Sum of all traffic within traffic split needs to be 100." - ) - + if traffic_percentage and traffic_split: + raise ValueError("Must choose either traffic percentage or traffic split, not both.") + if traffic_percentage: + if traffic_percentage > 100: + raise ValueError("Traffic percentage cannot be greater than 100.") + if traffic_percentage < 0: + raise ValueError("Traffic percentage cannot be negative.") + if traffic_split: + # TODO(b/172678233) verify every referenced deployed model exists + if sum(traffic_split.values()) != 100: + raise ValueError( + "Sum of all traffic within traffic split needs to be 100." + ) if bool(explanation_metadata) != bool(explanation_parameters): raise ValueError( "Both `explanation_metadata` and `explanation_parameters` should be specified or None." ) - # Raises ValueError if invalid accelerator if accelerator_type: utils.validate_accelerator_type(accelerator_type) @@ -1608,16 +1604,16 @@ def create( The name can be up to 128 characters long and can be consist of any UTF-8 characters. project (str): - Required. Project to retrieve endpoint from. If not set, project + Optional. Project to retrieve endpoint from. If not set, project set in aiplatform.init will be used. location (str): - Required. Location to retrieve endpoint from. If not set, location + Optional. Location to retrieve endpoint from. If not set, location set in aiplatform.init will be used. network (str): - Required. The full name of the Compute Engine network to which + Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". Private services access must already be configured for the network. - If not set, `network` set in aiplatform.init will be used. + If not set, network set in aiplatform.init will be used. description (str): Optional. The description of the Endpoint. labels (Dict[str, str]): @@ -1997,7 +1993,7 @@ def deploy( accelerator_type, deployed_model_display_name, explanation_metadata, - explanation_parameters, + explanation_parameters ) self._deploy( @@ -3943,6 +3939,7 @@ def get_model_evaluation( Args: evaluation_id (str): Optional. The ID of the model evaluation to retrieve. + Returns: model_evaluation.ModelEvaluation: Instantiated representation of the ModelEvaluation resource. From af4eabb0b49553480a5b862a12f7f530f9e12a83 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 20 Apr 2022 22:17:12 +0000 Subject: [PATCH 042/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index e754aee5fd..06162a037d 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -623,7 +623,9 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) if traffic_percentage and traffic_split: - raise ValueError("Must choose either traffic percentage or traffic split, not both.") + raise ValueError( + "Must choose either traffic percentage or traffic split, not both." + ) if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") @@ -1993,7 +1995,7 @@ def deploy( accelerator_type, deployed_model_display_name, explanation_metadata, - explanation_parameters + explanation_parameters, ) self._deploy( From 6e7fd5671d043b1219cc0ffa932e778905e048a7 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 20 Apr 2022 15:22:21 -0700 Subject: [PATCH 043/108] adjusting traffic for private endpoint --- google/cloud/aiplatform/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index e754aee5fd..51db846f25 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -623,7 +623,7 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) if traffic_percentage and traffic_split: - raise ValueError("Must choose either traffic percentage or traffic split, not both.") + raise ValueError("Optionally define either traffic percentage or traffic split, not both.") if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") @@ -2592,7 +2592,7 @@ def upload( return this_model - # TODO(b/172502059) support deploying with endpoint resource name + def deploy( self, endpoint: Optional[Union["Endpoint", "PrivateEndpoint"]] = None, @@ -2707,7 +2707,7 @@ def deploy( explanation_parameters, ) - if endpoint and endpoint.__class__ == "PrivateEndpoint": + if isinstance(endpoint, PrivateEndpoint): if traffic_percentage or traffic_split: raise ValueError( "Traffic splitting is not yet supported for private Endpoints. " From dfe532d48a103114cef22945f04b5d670dcde765 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 20 Apr 2022 15:24:32 -0700 Subject: [PATCH 044/108] adjusting traffic for private endpoint --- google/cloud/aiplatform/models.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index ea371736b3..46a738191c 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -623,13 +623,7 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) if traffic_percentage and traffic_split: -<<<<<<< HEAD raise ValueError("Optionally define either traffic percentage or traffic split, not both.") -======= - raise ValueError( - "Must choose either traffic percentage or traffic split, not both." - ) ->>>>>>> af4eabb0b49553480a5b862a12f7f530f9e12a83 if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") From 4f42cc6214a4e73001d92dd9a9f458c1fc5addb4 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 20 Apr 2022 22:27:06 +0000 Subject: [PATCH 045/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 46a738191c..ed8326e5db 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -623,7 +623,9 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) if traffic_percentage and traffic_split: - raise ValueError("Optionally define either traffic percentage or traffic split, not both.") + raise ValueError( + "Optionally define either traffic percentage or traffic split, not both." + ) if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") @@ -2592,7 +2594,6 @@ def upload( return this_model - def deploy( self, endpoint: Optional[Union["Endpoint", "PrivateEndpoint"]] = None, From f7e64f54040116e193b92589f1fc7f203bbe7ef4 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Thu, 21 Apr 2022 15:15:21 -0700 Subject: [PATCH 046/108] adding fixes --- google/cloud/aiplatform/models.py | 37 +++++++------------------ tests/unit/aiplatform/test_endpoints.py | 1 + 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 46a738191c..ffc7244fd3 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -623,7 +623,9 @@ def _validate_deploy_args( if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) if traffic_percentage and traffic_split: - raise ValueError("Optionally define either traffic percentage or traffic split, not both.") + raise ValueError( + "Optionally define either traffic percentage or traffic split, not both." + ) if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") @@ -1523,10 +1525,6 @@ def __init__( credentials=credentials, ) - self._custom_predict_uri = None - self._custom_explain_uri = None - self._custom_health_uri = None - self._http_client = urllib3.PoolManager() @property @@ -1537,10 +1535,7 @@ def predict_http_uri(self) -> Optional[str]: "Cannot make a predict request because a model has not been deployed on this private " "Endpoint. Please ensure a model has been deployed." ) - return ( - self._custom_predict_uri - or self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri - ) + return self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri @property def explain_http_uri(self) -> Optional[str]: @@ -1550,10 +1545,7 @@ def explain_http_uri(self) -> Optional[str]: "Cannot make a explain request because a model has not been deployed on this private " "Endpoint. Please ensure a model has been deployed." ) - return ( - self._custom_explain_uri - or self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri - ) + return self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri @property def health_http_uri(self) -> Optional[str]: @@ -1563,10 +1555,7 @@ def health_http_uri(self) -> Optional[str]: "Cannot make a health check request because a model has not been deployed on this private " "Endpoint. Please ensure a model has been deployed." ) - return ( - self._custom_health_uri - or self._gca_resource.deployed_models[0].private_endpoints.health_http_uri - ) + return self._gca_resource.deployed_models[0].private_endpoints.health_http_uri @classmethod def create( @@ -1714,10 +1703,6 @@ def _construct_sdk_resource_from_gapic( endpoint._gca_resource = gapic_resource - endpoint._custom_predict_uri = None - endpoint._custom_explain_uri = None - endpoint._custom_health_uri = None - endpoint._http_client = urllib3.PoolManager() return endpoint @@ -1735,8 +1720,7 @@ def _http_request( method (str): Required. The HTTP request method to use. Example: "POST" or "GET" url (str): - Required. Project to construct Endpoint object from. If not set, - project set in aiplatform.init will be used. + Required. The url used to send requests and get responses from. body (Dict[Any, Any]): Optional. Data sent to the url in the HTTP request. For private Endpoint, an instance is sent and a prediction response is expected. @@ -1745,7 +1729,7 @@ def _http_request( Returns: urllib3.response.HTTPResponse: - An initialized PrivateEndpoint resource. + A HTTP Response container. """ try: @@ -1761,13 +1745,13 @@ def _http_request( + response.data.decode("utf-8") ) - except urllib3.exceptions.MaxRetryError: + except urllib3.exceptions.MaxRetryError as exc: raise RuntimeError( f"Failed to make a {method} request to this URI, make sure: " " this call is being made inside the network this private Endpoint is peered to " f"({self._gca_resource.network}), calling health_check() returns True, " f"and that {url} is a valid URL." - ) + ) from exc def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: """Make a prediction against this private Endpoint using unauthenticated HTTP. @@ -2592,7 +2576,6 @@ def upload( return this_model - def deploy( self, endpoint: Optional[Union["Endpoint", "PrivateEndpoint"]] = None, diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index f8605cd1d2..6e0e7e18f5 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -1625,6 +1625,7 @@ def test_create(self, create_private_endpoint_mock, sync): endpoint=expected_endpoint, metadata=(), timeout=None, + endpoint_id=None, ) @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") From 7522b7a2f6676ddcefb3161439133afd7c9a8b46 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 26 Apr 2022 16:00:43 -0700 Subject: [PATCH 047/108] added delete for private Endpoint --- google/cloud/aiplatform/models.py | 25 +++++++++++- tests/unit/aiplatform/test_endpoints.py | 53 ++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index ffc7244fd3..06e8f3124a 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1798,7 +1798,6 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict prediction_response = json.loads(response.data) - print(prediction_response) return Prediction( predictions=prediction_response.get("predictions"), deployed_model_id=self._gca_resource.deployed_models[0].id, @@ -2031,6 +2030,30 @@ def undeploy( sync=sync, ) + def delete(self, force: bool = False, sync: bool = True) -> None: + """Deletes this Vertex AI private Endpoint resource. If force is set to True, + all models on this private Endpoint will be undeployed prior to deletion. + + Args: + force (bool): + Required. If force is set to True, all deployed models on this + Endpoint will be undeployed first. Default is False. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + + Raises: + FailedPrecondition: If models are deployed on this Endpoint and force = False. + """ + if force and self._gca_resource.deployed_models: + self.undeploy( + deployed_model_id=self._gca_resource.deployed_models[0].id, + sync=sync, + ) + + super().delete(sync=sync) + class Model(base.VertexAiResourceNounWithFutureManager): diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 6e0e7e18f5..daf56046f3 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -482,6 +482,16 @@ def list_private_endpoints_mock(): yield list_endpoints_mock +@pytest.fixture +def sdk_undeploy_mock(): + """Mocks the high-level PrivateEndpoint.undeploy() SDK method""" + with mock.patch.object( + aiplatform.PrivateEndpoint, "undeploy" + ) as sdk_undeploy_mock: + sdk_undeploy_mock.return_value = None + yield sdk_undeploy_mock + + class TestEndpoint: def setup_method(self): reload(initializer) @@ -1605,7 +1615,7 @@ def test_delete_endpoint_with_force( class TestPrivateEndpoint(TestEndpoint): @pytest.mark.parametrize("sync", [True, False]) def test_create(self, create_private_endpoint_mock, sync): - my_endpoint = models.PrivateEndpoint.create( + test_endpoint = models.PrivateEndpoint.create( display_name=_TEST_DISPLAY_NAME, project=_TEST_PROJECT, location=_TEST_LOCATION, @@ -1614,7 +1624,7 @@ def test_create(self, create_private_endpoint_mock, sync): ) if not sync: - my_endpoint.wait() + test_endpoint.wait() expected_endpoint = gca_endpoint.Endpoint( display_name=_TEST_DISPLAY_NAME, network=_TEST_NETWORK @@ -1710,6 +1720,45 @@ def test_undeploy(self, undeploy_model_mock, sync): traffic_split={}, ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.parametrize("sync", [True, False]) + def test_delete_without_force( + self, sdk_undeploy_mock, delete_endpoint_mock, sync + ): + + test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) + test_endpoint.delete(sync=sync) + + if not sync: + test_endpoint.wait() + + # undeploy() should not be called unless force is set to True + sdk_undeploy_mock.assert_not_called() + + delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) + + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.parametrize("sync", [True, False]) + def test_delete_with_force( + self, sdk_undeploy_mock, delete_endpoint_mock, sync + ): + + test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) + test_endpoint._gca_resource.deployed_models = [_TEST_DEPLOYED_MODELS[0]] + test_endpoint.delete(sync=sync) + + if not sync: + test_endpoint.wait() + + # undeploy() should not be called unless force is set to True + sdk_undeploy_mock.called_once_with( + deployed_model_id=_TEST_ID, + sync=sync + ) + + delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) + + @pytest.mark.usefixtures("list_private_endpoints_mock") def test_list(self): ep_list = aiplatform.PrivateEndpoint.list() From 5cc299f753d7c3efa5757de9e6d95be7e15e8da9 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 26 Apr 2022 23:03:30 +0000 Subject: [PATCH 048/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/unit/aiplatform/test_endpoints.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index daf56046f3..6b08468f1f 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -485,9 +485,7 @@ def list_private_endpoints_mock(): @pytest.fixture def sdk_undeploy_mock(): """Mocks the high-level PrivateEndpoint.undeploy() SDK method""" - with mock.patch.object( - aiplatform.PrivateEndpoint, "undeploy" - ) as sdk_undeploy_mock: + with mock.patch.object(aiplatform.PrivateEndpoint, "undeploy") as sdk_undeploy_mock: sdk_undeploy_mock.return_value = None yield sdk_undeploy_mock @@ -1722,9 +1720,7 @@ def test_undeploy(self, undeploy_model_mock, sync): @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") @pytest.mark.parametrize("sync", [True, False]) - def test_delete_without_force( - self, sdk_undeploy_mock, delete_endpoint_mock, sync - ): + def test_delete_without_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) test_endpoint.delete(sync=sync) @@ -1739,26 +1735,20 @@ def test_delete_without_force( @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") @pytest.mark.parametrize("sync", [True, False]) - def test_delete_with_force( - self, sdk_undeploy_mock, delete_endpoint_mock, sync - ): + def test_delete_with_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) - test_endpoint._gca_resource.deployed_models = [_TEST_DEPLOYED_MODELS[0]] + test_endpoint._gca_resource.deployed_models = [_TEST_DEPLOYED_MODELS[0]] test_endpoint.delete(sync=sync) if not sync: test_endpoint.wait() # undeploy() should not be called unless force is set to True - sdk_undeploy_mock.called_once_with( - deployed_model_id=_TEST_ID, - sync=sync - ) + sdk_undeploy_mock.called_once_with(deployed_model_id=_TEST_ID, sync=sync) delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) - @pytest.mark.usefixtures("list_private_endpoints_mock") def test_list(self): ep_list = aiplatform.PrivateEndpoint.list() From d1c32934cf116d638ff2008322f5868416b95281 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 27 Apr 2022 14:43:37 -0700 Subject: [PATCH 049/108] fixing traffic percentage --- google/cloud/aiplatform/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 06e8f3124a..6b94ed5d68 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1887,6 +1887,8 @@ def deploy( self, model: "Model", deployed_model_display_name: Optional[str] = None, + traffic_percentage: Optional[int] = 100, + traffic_split: Optional[Dict[str, int]] = None, machine_type: Optional[str] = None, min_replica_count: int = 1, max_replica_count: int = 1, @@ -1975,8 +1977,11 @@ def deploy( max_replica_count, accelerator_type, deployed_model_display_name, + traffic_split, + traffic_percentage, explanation_metadata, explanation_parameters, + ) self._deploy( From e40e53d7110f6539910f3d43d9b208ecbbd69103 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 27 Apr 2022 21:46:13 +0000 Subject: [PATCH 050/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 6b94ed5d68..8c37bdfdf2 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1981,7 +1981,6 @@ def deploy( traffic_percentage, explanation_metadata, explanation_parameters, - ) self._deploy( From c26363e16141e8ca85f4eab5321ebe701ec28abc Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 29 Apr 2022 14:49:32 -0700 Subject: [PATCH 051/108] using network instead of class, moving exceptions --- google/cloud/aiplatform/models.py | 47 +++++++++++++++---------- tests/unit/aiplatform/test_endpoints.py | 15 +++++++- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 6b94ed5d68..e5e9795708 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -872,10 +872,11 @@ def _deploy( ) self._deploy_call( - self.api_client, - self.resource_name, - model, - self._gca_resource.traffic_split, + api_client=self.api_client, + endpoint_resource_name=self.resource_name, + model=model, + endpoint_resource_traffic_split=self._gca_resource.traffic_split, + network=self.network, deployed_model_display_name=deployed_model_display_name, traffic_percentage=traffic_percentage, traffic_split=traffic_split, @@ -902,6 +903,7 @@ def _deploy_call( endpoint_resource_name: str, model: "Model", endpoint_resource_traffic_split: Optional[proto.MapField] = None, + network: str = "", deployed_model_display_name: Optional[str] = None, traffic_percentage: Optional[int] = 0, traffic_split: Optional[Dict[str, int]] = None, @@ -929,6 +931,11 @@ def _deploy_call( Required. Model to be deployed. endpoint_resource_traffic_split (proto.MapField): Optional. Endpoint current resource traffic split. + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + Private services access must already be configured for the network. + If not set, network set in aiplatform.init will be used. deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. @@ -1079,7 +1086,7 @@ def _deploy_call( deployed_model.explanation_spec = explanation_spec # TODO(b/221059294): Remove check for class once PrivateEndpoint supports traffic split - if traffic_split is None and cls == Endpoint: + if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: # default scenario @@ -1203,7 +1210,7 @@ def _undeploy( # TODO(b/211351292): Remove check once traffic split is supported # Skip unallocating traffic and raise if new split is provided - if self.__class__ == PrivateEndpoint: + if self.network: if traffic_split: raise ValueError( "Traffic splitting is not yet supported by private Endpoints. " @@ -1531,30 +1538,21 @@ def __init__( def predict_http_uri(self) -> Optional[str]: """Http(s) path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" if not self._gca_resource.deployed_models: - raise RuntimeError( - "Cannot make a predict request because a model has not been deployed on this private " - "Endpoint. Please ensure a model has been deployed." - ) + return None return self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri @property def explain_http_uri(self) -> Optional[str]: """Http(s) path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" if not self._gca_resource.deployed_models: - raise RuntimeError( - "Cannot make a explain request because a model has not been deployed on this private " - "Endpoint. Please ensure a model has been deployed." - ) + return None return self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri @property def health_http_uri(self) -> Optional[str]: """Http(s) path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" if not self._gca_resource.deployed_models: - raise RuntimeError( - "Cannot make a health check request because a model has not been deployed on this private " - "Endpoint. Please ensure a model has been deployed." - ) + return None return self._gca_resource.deployed_models[0].private_endpoints.health_http_uri @classmethod @@ -1789,6 +1787,12 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict self.wait() self._sync_gca_resource_if_skipped() + if not self._gca_resource.deployed_models: + raise RuntimeError( + "Cannot make a predict request because a model has not been deployed on this private " + "Endpoint. Please ensure a model has been deployed." + ) + response = self._http_request( method="POST", url=self.predict_http_uri, @@ -1823,6 +1827,12 @@ def health_check(self) -> bool: self.wait() self._sync_gca_resource_if_skipped() + if not self._gca_resource.deployed_models: + raise RuntimeError( + "Cannot make a health check request because a model has not been deployed on this private " + "Endpoint. Please ensure a model has been deployed." + ) + response = self._http_request( method="GET", url=self.health_http_uri, @@ -1981,7 +1991,6 @@ def deploy( traffic_percentage, explanation_metadata, explanation_parameters, - ) self._deploy( diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 6b08468f1f..6053b4c803 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -443,6 +443,19 @@ def create_private_endpoint_mock(): yield create_private_endpoint_mock +@pytest.fixture +def get_private_endpoint_mock(): + with mock.patch.object( + endpoint_service_client.EndpointServiceClient, "get_endpoint" + ) as get_endpoint_mock: + get_endpoint_mock.return_value = gca_endpoint.Endpoint( + display_name=_TEST_DISPLAY_NAME, + name=_TEST_ENDPOINT_NAME, + network=_TEST_NETWORK, + ) + yield get_endpoint_mock + + @pytest.fixture def get_private_endpoint_with_model_mock(): with mock.patch.object( @@ -1667,7 +1680,7 @@ def test_health_check(self, health_check_private_endpoint_mock): method="GET", url="", body=None, headers=None ) - @pytest.mark.usefixtures("get_endpoint_mock", "get_model_mock") + @pytest.mark.usefixtures("get_private_endpoint_mock", "get_model_mock") @pytest.mark.parametrize("sync", [True, False]) def test_deploy(self, deploy_model_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) From 45c679432bf0b65e0ed9daeab524bf27e71ba87f Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 2 May 2022 15:10:50 -0700 Subject: [PATCH 052/108] cleaning up docstrings --- google/cloud/aiplatform/models.py | 78 +++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index e5e9795708..3be88c4c5c 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -219,12 +219,6 @@ def create( Optional. The user-defined name of the Endpoint. The name can be up to 128 characters long and can be consist of any UTF-8 characters. - project (str): - Required. Project to retrieve endpoint from. If not set, project - set in aiplatform.init will be used. - location (str): - Required. Location to retrieve endpoint from. If not set, location - set in aiplatform.init will be used. description (str): Optional. The description of the Endpoint. labels (Dict[str, str]): @@ -240,6 +234,12 @@ def create( metadata (Sequence[Tuple[str, str]]): Optional. Strings which should be sent along with the request as metadata. + project (str): + Required. Project to retrieve endpoint from. If not set, project + set in aiplatform.init will be used. + location (str): + Required. Location to retrieve endpoint from. If not set, location + set in aiplatform.init will be used. credentials (auth_credentials.Credentials): Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. @@ -973,6 +973,12 @@ def _deploy_call( is not provided, the larger value of min_replica_count or 1 will be used. If value provided is smaller than min_replica_count, it will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. service_account (str): The service account that the DeployedModel's container runs as. Specify the email address of the service account. If this service account is not @@ -991,10 +997,6 @@ def _deploy_call( metadata (Sequence[Tuple[str, str]]): Optional. Strings which should be sent along with the request as metadata. - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. deploy_request_timeout (float): Optional. The timeout for the deploy request in seconds. @@ -1536,21 +1538,21 @@ def __init__( @property def predict_http_uri(self) -> Optional[str]: - """Http(s) path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" + """HTTP path to send prediction requests to, used when calling `PrivateEndpoint.predict()`""" if not self._gca_resource.deployed_models: return None return self._gca_resource.deployed_models[0].private_endpoints.predict_http_uri @property def explain_http_uri(self) -> Optional[str]: - """Http(s) path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" + """HTTP path to send explain requests to, used when calling `PrivateEndpoint.explain()`""" if not self._gca_resource.deployed_models: return None return self._gca_resource.deployed_models[0].private_endpoints.explain_http_uri @property def health_http_uri(self) -> Optional[str]: - """Http(s) path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" + """HTTP path to send health check requests to, used when calling `PrivateEndpoint.health_check()`""" if not self._gca_resource.deployed_models: return None return self._gca_resource.deployed_models[0].private_endpoints.health_http_uri @@ -1752,7 +1754,7 @@ def _http_request( ) from exc def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction against this private Endpoint using unauthenticated HTTP. + """Make a prediction against this private Endpoint using a HTTP request. This method must be called within the network the private Endpoint is peered to. The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. @@ -1814,7 +1816,7 @@ def explain(self): def health_check(self) -> bool: """ - Makes GET request to this private Endpoint's health check URI. Must be within network + Makes a request to this private Endpoint's health check URI. Must be within network that this private Endpoint is in. Example Usage: @@ -1925,6 +1927,21 @@ def deploy( deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. machine_type (str): Optional. The type of machine. Not specifying machine type will result in model to be deployed with automatic resources. @@ -2356,9 +2373,6 @@ def upload( ) Args: - display_name (str): - Optional. The display name of the Model. The name can be up to 128 - characters long and can be consist of any UTF-8 characters. serving_container_image_uri (str): Required. The URI of the Model serving container. artifact_uri (str): @@ -2455,6 +2469,9 @@ def upload( explanation_parameters (aiplatform.explain.ExplanationParameters): Optional. Parameters to configure explaining for Model's predictions. For more details, see `Ref docs ` + display_name (str): + Optional. The display name of the Model. The name can be up to 128 + characters long and can be consist of any UTF-8 characters. project: Optional[str]=None, Project to upload this model to. Overrides project set in aiplatform.init. @@ -2644,6 +2661,21 @@ def deploy( deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. machine_type (str): Optional. The type of machine. Not specifying machine type will result in model to be deployed with automatic resources. @@ -3664,6 +3696,10 @@ def upload_scikit_learn_model_file( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. upload_request_timeout (float): Optional. The timeout for the upload request in seconds. @@ -3871,6 +3907,10 @@ def upload_tensorflow_saved_model( staging_bucket (str): Optional. Bucket to stage local model artifacts. Overrides staging_bucket set in aiplatform.init. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. upload_request_timeout (float): Optional. The timeout for the upload request in seconds. @@ -3953,7 +3993,7 @@ def get_model_evaluation( evaluation_id="789" ) - # If no arguments are passed, this returns the first evaluation for the model + # If no arguments are passed, this method returns the first evaluation for the model my_evaluation = my_model.get_model_evaluation() Args: From 1aab05825f38737653dd48e50d38f8d04d6ab0ab Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 3 May 2022 15:16:40 -0700 Subject: [PATCH 053/108] adding fixes, delete testing --- google/cloud/aiplatform/models.py | 79 ++++++++++++------------------- 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 3be88c4c5c..4775dd0530 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -545,9 +545,8 @@ def _unallocate_traffic( return new_traffic_split - @classmethod + @staticmethod def _validate_deploy_args( - cls, min_replica_count: int, max_replica_count: int, accelerator_type: Optional[str], @@ -622,10 +621,6 @@ def _validate_deploy_args( raise ValueError("Max replica cannot be negative.") if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) - if traffic_percentage and traffic_split: - raise ValueError( - "Optionally define either traffic percentage or traffic split, not both." - ) if traffic_percentage: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") @@ -741,14 +736,14 @@ def deploy( self._sync_gca_resource_if_skipped() self._validate_deploy_args( - min_replica_count, - max_replica_count, - accelerator_type, - deployed_model_display_name, - traffic_split, - traffic_percentage, - explanation_metadata, - explanation_parameters, + min_replica_count=min_replica_count, + max_replica_count=max_replica_count, + accelerator_type=accelerator_type, + deployed_model_display_name=deployed_model_display_name, + traffic_split=traffic_split, + traffic_percentage=traffic_percentage, + explanation_metadata=explanation_metadata, + explanation_parameters=explanation_parameters, ) self._deploy( @@ -903,7 +898,7 @@ def _deploy_call( endpoint_resource_name: str, model: "Model", endpoint_resource_traffic_split: Optional[proto.MapField] = None, - network: str = "", + network: Optional[str] = None, deployed_model_display_name: Optional[str] = None, traffic_percentage: Optional[int] = 0, traffic_split: Optional[Dict[str, int]] = None, @@ -1899,8 +1894,6 @@ def deploy( self, model: "Model", deployed_model_display_name: Optional[str] = None, - traffic_percentage: Optional[int] = 100, - traffic_split: Optional[Dict[str, int]] = None, machine_type: Optional[str] = None, min_replica_count: int = 1, max_replica_count: int = 1, @@ -1927,21 +1920,6 @@ def deploy( deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. machine_type (str): Optional. The type of machine. Not specifying machine type will result in model to be deployed with automatic resources. @@ -2000,19 +1978,21 @@ def deploy( ) self._validate_deploy_args( - min_replica_count, - max_replica_count, - accelerator_type, - deployed_model_display_name, - traffic_split, - traffic_percentage, - explanation_metadata, - explanation_parameters, + min_replica_count=min_replica_count, + max_replica_count=max_replica_count, + accelerator_type=accelerator_type, + deployed_model_display_name=deployed_model_display_name, + traffic_split=None, + traffic_percentage=100, + explanation_metadata=explanation_metadata, + explanation_parameters=explanation_parameters, ) self._deploy( model=model, deployed_model_display_name=deployed_model_display_name, + traffic_percentage=100, + traffic_split=None, machine_type=machine_type, min_replica_count=min_replica_count, max_replica_count=max_replica_count, @@ -2061,6 +2041,7 @@ def undeploy( sync=sync, ) +''' def delete(self, force: bool = False, sync: bool = True) -> None: """Deletes this Vertex AI private Endpoint resource. If force is set to True, all models on this private Endpoint will be undeployed prior to deletion. @@ -2084,7 +2065,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: ) super().delete(sync=sync) - +''' class Model(base.VertexAiResourceNounWithFutureManager): @@ -2749,14 +2730,14 @@ def deploy( """ Endpoint._validate_deploy_args( - min_replica_count, - max_replica_count, - accelerator_type, - deployed_model_display_name, - traffic_split, - traffic_percentage, - explanation_metadata, - explanation_parameters, + min_replica_count=min_replica_count, + max_replica_count=max_replica_count, + accelerator_type=accelerator_type, + deployed_model_display_name=deployed_model_display_name, + traffic_split=traffic_split, + traffic_percentage=traffic_percentage, + explanation_metadata=explanation_metadata, + explanation_parameters=explanation_parameters, ) if isinstance(endpoint, PrivateEndpoint): From b8185ae93d58b5bb9e61f3670a040cf57f2be170 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 3 May 2022 22:19:14 +0000 Subject: [PATCH 054/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 4775dd0530..f04c91d6d1 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -2041,6 +2041,7 @@ def undeploy( sync=sync, ) + ''' def delete(self, force: bool = False, sync: bool = True) -> None: """Deletes this Vertex AI private Endpoint resource. If force is set to True, @@ -2067,6 +2068,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: super().delete(sync=sync) ''' + class Model(base.VertexAiResourceNounWithFutureManager): client_class = utils.ModelClientWithOverride From 05ff6123122aeb7537c368d5164bf87b062f7ab4 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 3 May 2022 22:19:29 +0000 Subject: [PATCH 055/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 4775dd0530..f04c91d6d1 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -2041,6 +2041,7 @@ def undeploy( sync=sync, ) + ''' def delete(self, force: bool = False, sync: bool = True) -> None: """Deletes this Vertex AI private Endpoint resource. If force is set to True, @@ -2067,6 +2068,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: super().delete(sync=sync) ''' + class Model(base.VertexAiResourceNounWithFutureManager): client_class = utils.ModelClientWithOverride From b177cfd2e8e92e6f69daed5bd12b5d154680b48c Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 3 May 2022 17:02:57 -0700 Subject: [PATCH 056/108] adding fixes --- google/cloud/aiplatform/models.py | 39 +++++++++++++++++++------------ setup.py | 13 +++++++---- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 4775dd0530..64af00a8ee 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -20,7 +20,16 @@ import re import shutil import tempfile -import urllib3 + +try: + import urllib3 +except ImportError: + raise ImportError( + """cannot import the urllib3 HTTP client. + Please ensure the correct version of urllib3 has been installed. + Check `setup.py` in the root directory for details.""" + ) + from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation @@ -1205,16 +1214,7 @@ def _undeploy( self._sync_gca_resource_if_skipped() current_traffic_split = traffic_split or dict(self._gca_resource.traffic_split) - # TODO(b/211351292): Remove check once traffic split is supported - # Skip unallocating traffic and raise if new split is provided - if self.network: - if traffic_split: - raise ValueError( - "Traffic splitting is not yet supported by private Endpoints. " - "Undeploy the current model without providing a `traffic_split`." - ) - - elif deployed_model_id in current_traffic_split: + if deployed_model_id in current_traffic_split: current_traffic_split = self._unallocate_traffic( traffic_split=current_traffic_split, deployed_model_id=deployed_model_id, @@ -1973,7 +1973,7 @@ def deploy( if len(self._gca_resource.deployed_models): raise ValueError( "A maximum of one model can be deployed to each private Endpoint. " - "Please call PrivateEndpoint.undeploy_all() before deploying another " + "Please call PrivateEndpoint.undeploy() before deploying another " "model." ) @@ -2008,6 +2008,7 @@ def deploy( def undeploy( self, deployed_model_id: str, + traffic_split: Optional[Dict[str, int]] = None, sync=True, ) -> None: """Undeploys a deployed model from the private Endpoint. @@ -2036,12 +2037,20 @@ def undeploy( """ self._sync_gca_resource_if_skipped() + # TODO(b/211351292): Remove check once traffic split is supported + # Skip unallocating traffic and raise if new split is provided + if traffic_split: + raise ValueError( + "Traffic splitting is not yet supported by private Endpoints. " + "Undeploy the model without providing the `traffic_split` argument." + ) + self._undeploy( deployed_model_id=deployed_model_id, + traffic_split=traffic_split, sync=sync, ) -''' def delete(self, force: bool = False, sync: bool = True) -> None: """Deletes this Vertex AI private Endpoint resource. If force is set to True, all models on this private Endpoint will be undeployed prior to deletion. @@ -2064,8 +2073,8 @@ def delete(self, force: bool = False, sync: bool = True) -> None: sync=sync, ) - super().delete(sync=sync) -''' + super(Endpoint, self).delete(sync=sync) + class Model(base.VertexAiResourceNounWithFutureManager): diff --git a/setup.py b/setup.py index 1cb00f6b99..7270ef67d7 100644 --- a/setup.py +++ b/setup.py @@ -52,9 +52,13 @@ "pandas >= 1.0.0", "pyarrow >= 6.0.1", ] -pipelines_extra_requires = [ +pipelines_extra_require = [ "pyyaml>=5.3,<6", ] +private_endpoint_extra_require = [ + "urllib3 >=1.21.1, <1.27", +] + full_extra_require = list( set( tensorboard_extra_require @@ -62,7 +66,8 @@ + xai_extra_require + lit_extra_require + featurestore_extra_require - + pipelines_extra_requires + + pipelines_extra_require + + private_endpoint_extra_require ) ) testing_extra_require = ( @@ -101,7 +106,6 @@ "google-api-core[grpc] >= 1.31.5, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0", "proto-plus >= 1.15.0", "packaging >= 14.3", - "urllib3 >=1.21.1, <1.27", "google-cloud-storage >= 1.32.0, < 3.0.0dev", "google-cloud-bigquery >= 1.15.0, < 3.0.0dev", "google-cloud-resource-manager >= 1.3.3, < 3.0.0dev", @@ -114,7 +118,8 @@ "xai": xai_extra_require, "lit": lit_extra_require, "cloud_profiler": profiler_extra_require, - "pipelines": pipelines_extra_requires, + "pipelines": pipelines_extra_require, + "private_endpoint": private_endpoint_extra_require, }, python_requires=">=3.6", classifiers=[ From f5b51ac42f1576241e830fb3f77ac469af5ad293 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 4 May 2022 13:52:04 -0700 Subject: [PATCH 057/108] adding Raises section to private Endpoint docstrings --- google/cloud/aiplatform/models.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 64af00a8ee..d58dbb6e60 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1725,6 +1725,9 @@ def _http_request( Returns: urllib3.response.HTTPResponse: A HTTP Response container. + + Raises: + RuntimeError: If a HTTP request could not be made. """ try: @@ -1780,6 +1783,9 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict Returns: prediction: Prediction with returned predictions and Model Id. + + Raises: + RuntimeError: If a model has not been deployed a request cannot be made. """ self.wait() self._sync_gca_resource_if_skipped() @@ -1820,6 +1826,9 @@ def health_check(self) -> bool: Returns: bool: Checks if calls can be made to this private Endpoint. + + Raises: + RuntimeError: If a model has not been deployed a request cannot be made. """ self.wait() self._sync_gca_resource_if_skipped() @@ -1876,7 +1885,7 @@ def list( credentials set in aiplatform.init. Returns: - List[models.PrivateEndpoint] - A list of PrivateEndpoint resource objects + List[models.PrivateEndpoint] - A list of PrivateEndpoint resource objects. """ return cls._list_with_local_order( @@ -1967,6 +1976,10 @@ def deploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + + Raises: + ValueError: If a model has already been deployed another one cannot be + deployed with private Endpoint. """ self._sync_gca_resource_if_skipped() @@ -2034,6 +2047,10 @@ def undeploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + + Raises: + ValueError: If the `traffic_split` argument is passed when undeploying + a private Endpoint. """ self._sync_gca_resource_if_skipped() @@ -3268,10 +3285,12 @@ def export_model( Whether to execute this export synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. + Returns: output_info (Dict[str, str]): Details of the completed export with output destination paths to the artifacts or container image. + Raises: ValueError: If model does not support exporting. From 715127789e937e65de96fe49753a9ccde254898d Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 4 May 2022 17:33:06 -0700 Subject: [PATCH 058/108] added private Endpoint check in init, added to testing --- google/cloud/aiplatform/models.py | 17 +++++++++++------ setup.py | 6 +++--- tests/unit/aiplatform/test_endpoints.py | 24 ++++++++++++++++++------ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index d58dbb6e60..4b9c991a86 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -25,9 +25,8 @@ import urllib3 except ImportError: raise ImportError( - """cannot import the urllib3 HTTP client. - Please ensure the correct version of urllib3 has been installed. - Check `setup.py` in the root directory for details.""" + """cannot import the urllib3 HTTP client. + Please install google-cloud-aiplatform[private_endpoints].""" ) from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union @@ -1522,6 +1521,15 @@ def __init__( Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. """ + if not self.list( + filter='display_name="{}"'.format(endpoint_name) + ) or not self.list( + filter='display_name="{}"'.format(endpoint_name.rsplit("/", 1)[-1]) + ): + raise ValueError( + "Please ensure the Endpoint being retrieved is a private Endpoint." + ) + super().__init__( endpoint_name=endpoint_name, project=project, @@ -1729,7 +1737,6 @@ def _http_request( Raises: RuntimeError: If a HTTP request could not be made. """ - try: response = self._http_client.request( method=method, url=url, body=body, headers=headers @@ -1981,8 +1988,6 @@ def deploy( ValueError: If a model has already been deployed another one cannot be deployed with private Endpoint. """ - self._sync_gca_resource_if_skipped() - if len(self._gca_resource.deployed_models): raise ValueError( "A maximum of one model can be deployed to each private Endpoint. " diff --git a/setup.py b/setup.py index 7270ef67d7..34dc7ff355 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ pipelines_extra_require = [ "pyyaml>=5.3,<6", ] -private_endpoint_extra_require = [ +private_endpoints_extra_require = [ "urllib3 >=1.21.1, <1.27", ] @@ -67,7 +67,7 @@ + lit_extra_require + featurestore_extra_require + pipelines_extra_require - + private_endpoint_extra_require + + private_endpoints_extra_require ) ) testing_extra_require = ( @@ -119,7 +119,7 @@ "lit": lit_extra_require, "cloud_profiler": profiler_extra_require, "pipelines": pipelines_extra_require, - "private_endpoint": private_endpoint_extra_require, + "private_endpoints": private_endpoints_extra_require, }, python_requires=">=3.6", classifiers=[ diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index dc6ae60a0b..5b891c6e0f 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -1741,7 +1741,9 @@ def test_create(self, create_private_endpoint_mock, sync): endpoint_id=None, ) - @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" + ) def test_predict(self, predict_private_endpoint_mock): test_endpoint = models.PrivateEndpoint(_TEST_ID) test_prediction = test_endpoint.predict( @@ -1760,7 +1762,9 @@ def test_predict(self, predict_private_endpoint_mock): headers={"Content-Type": "application/json"}, ) - @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" + ) def test_health_check(self, health_check_private_endpoint_mock): test_endpoint = models.PrivateEndpoint(_TEST_ID) test_health_check = test_endpoint.health_check() @@ -1772,7 +1776,9 @@ def test_health_check(self, health_check_private_endpoint_mock): method="GET", url="", body=None, headers=None ) - @pytest.mark.usefixtures("get_private_endpoint_mock", "get_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_mock", "get_model_mock", "list_private_endpoints_mock" + ) @pytest.mark.parametrize("sync", [True, False]) def test_deploy(self, deploy_model_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) @@ -1807,7 +1813,9 @@ def test_deploy(self, deploy_model_mock, sync): traffic_split=None, ) - @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" + ) @pytest.mark.parametrize("sync", [True, False]) def test_undeploy(self, undeploy_model_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) @@ -1823,7 +1831,9 @@ def test_undeploy(self, undeploy_model_mock, sync): traffic_split={}, ) - @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" + ) @pytest.mark.parametrize("sync", [True, False]) def test_delete_without_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): @@ -1838,7 +1848,9 @@ def test_delete_without_force(self, sdk_undeploy_mock, delete_endpoint_mock, syn delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) - @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") + @pytest.mark.usefixtures( + "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" + ) @pytest.mark.parametrize("sync", [True, False]) def test_delete_with_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): From b0df987a24e13d4fff36acdd05b5637cbf124efe Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 4 May 2022 17:36:37 -0700 Subject: [PATCH 059/108] added private Endpoint check in init, added to testing --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 4b9c991a86..57d97f1f0d 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -25,7 +25,7 @@ import urllib3 except ImportError: raise ImportError( - """cannot import the urllib3 HTTP client. + """cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints].""" ) From 9ac1aec112f6dff3c390e98c0772a24f3b816558 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 5 May 2022 00:39:07 +0000 Subject: [PATCH 060/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 57d97f1f0d..4b9c991a86 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -25,7 +25,7 @@ import urllib3 except ImportError: raise ImportError( - """cannot import the urllib3 HTTP client. + """cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints].""" ) From 67ee082cc2c1367b3c3b891da89bff28af1fd25c Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 6 May 2022 14:08:31 -0700 Subject: [PATCH 061/108] adding try/except inside functions, fixing tests, misc fixes --- google/cloud/aiplatform/models.py | 41 +++++++++++++++---------- tests/unit/aiplatform/test_endpoints.py | 24 ++++----------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 57d97f1f0d..0216b3bc83 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -21,14 +21,6 @@ import shutil import tempfile -try: - import urllib3 -except ImportError: - raise ImportError( - """cannot import the urllib3 HTTP client. - Please install google-cloud-aiplatform[private_endpoints].""" - ) - from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation @@ -1521,13 +1513,11 @@ def __init__( Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. """ - if not self.list( - filter='display_name="{}"'.format(endpoint_name) - ) or not self.list( - filter='display_name="{}"'.format(endpoint_name.rsplit("/", 1)[-1]) - ): - raise ValueError( - "Please ensure the Endpoint being retrieved is a private Endpoint." + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." ) super().__init__( @@ -1537,6 +1527,11 @@ def __init__( credentials=credentials, ) + if not self.network: + raise ValueError( + "Please ensure the Endpoint being retrieved is a private Endpoint." + ) + self._http_client = urllib3.PoolManager() @property @@ -1700,6 +1695,13 @@ def _construct_sdk_resource_from_gapic( PrivateEndpoint: An initialized PrivateEndpoint resource. """ + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) + endpoint = cls._empty_constructor( project=project, location=location, credentials=credentials ) @@ -1716,7 +1718,7 @@ def _http_request( url: str, body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, - ) -> urllib3.response.HTTPResponse: + ): """Helper function used to perform HTTP requests for private Endpoint. Args: @@ -1737,6 +1739,13 @@ def _http_request( Raises: RuntimeError: If a HTTP request could not be made. """ + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) + try: response = self._http_client.request( method=method, url=url, body=body, headers=headers diff --git a/tests/unit/aiplatform/test_endpoints.py b/tests/unit/aiplatform/test_endpoints.py index 5b891c6e0f..dc6ae60a0b 100644 --- a/tests/unit/aiplatform/test_endpoints.py +++ b/tests/unit/aiplatform/test_endpoints.py @@ -1741,9 +1741,7 @@ def test_create(self, create_private_endpoint_mock, sync): endpoint_id=None, ) - @pytest.mark.usefixtures( - "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") def test_predict(self, predict_private_endpoint_mock): test_endpoint = models.PrivateEndpoint(_TEST_ID) test_prediction = test_endpoint.predict( @@ -1762,9 +1760,7 @@ def test_predict(self, predict_private_endpoint_mock): headers={"Content-Type": "application/json"}, ) - @pytest.mark.usefixtures( - "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") def test_health_check(self, health_check_private_endpoint_mock): test_endpoint = models.PrivateEndpoint(_TEST_ID) test_health_check = test_endpoint.health_check() @@ -1776,9 +1772,7 @@ def test_health_check(self, health_check_private_endpoint_mock): method="GET", url="", body=None, headers=None ) - @pytest.mark.usefixtures( - "get_private_endpoint_mock", "get_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_mock", "get_model_mock") @pytest.mark.parametrize("sync", [True, False]) def test_deploy(self, deploy_model_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) @@ -1813,9 +1807,7 @@ def test_deploy(self, deploy_model_mock, sync): traffic_split=None, ) - @pytest.mark.usefixtures( - "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") @pytest.mark.parametrize("sync", [True, False]) def test_undeploy(self, undeploy_model_mock, sync): test_endpoint = models.PrivateEndpoint(_TEST_ENDPOINT_NAME) @@ -1831,9 +1823,7 @@ def test_undeploy(self, undeploy_model_mock, sync): traffic_split={}, ) - @pytest.mark.usefixtures( - "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") @pytest.mark.parametrize("sync", [True, False]) def test_delete_without_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): @@ -1848,9 +1838,7 @@ def test_delete_without_force(self, sdk_undeploy_mock, delete_endpoint_mock, syn delete_endpoint_mock.assert_called_once_with(name=_TEST_ENDPOINT_NAME) - @pytest.mark.usefixtures( - "get_private_endpoint_with_model_mock", "list_private_endpoints_mock" - ) + @pytest.mark.usefixtures("get_private_endpoint_with_model_mock") @pytest.mark.parametrize("sync", [True, False]) def test_delete_with_force(self, sdk_undeploy_mock, delete_endpoint_mock, sync): From 8de58a9e7278d941f25fafc468f6302c5e9e5c80 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 6 May 2022 21:18:57 +0000 Subject: [PATCH 062/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .kokoro/continuous/continuous.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg index 18a4c35325..8f43917d92 100644 --- a/.kokoro/continuous/continuous.cfg +++ b/.kokoro/continuous/continuous.cfg @@ -1 +1 @@ -# Format: //devtools/kokoro/config/proto/build.proto +# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file From 881d7b72fe4c360df2d8dc9f644a7591fc6d7e32 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 9 May 2022 15:48:44 -0700 Subject: [PATCH 063/108] reverting _validate_deploy_args method --- .kokoro/continuous/continuous.cfg | 1 - google/cloud/aiplatform/models.py | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 .kokoro/continuous/continuous.cfg diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg deleted file mode 100644 index 8f43917d92..0000000000 --- a/.kokoro/continuous/continuous.cfg +++ /dev/null @@ -1 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 0216b3bc83..021690b867 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -621,21 +621,25 @@ def _validate_deploy_args( raise ValueError("Max replica cannot be negative.") if deployed_model_display_name is not None: utils.validate_display_name(deployed_model_display_name) - if traffic_percentage: + + if traffic_split is None: if traffic_percentage > 100: raise ValueError("Traffic percentage cannot be greater than 100.") if traffic_percentage < 0: raise ValueError("Traffic percentage cannot be negative.") - if traffic_split: + + elif traffic_split: # TODO(b/172678233) verify every referenced deployed model exists if sum(traffic_split.values()) != 100: raise ValueError( "Sum of all traffic within traffic split needs to be 100." ) + if bool(explanation_metadata) != bool(explanation_parameters): raise ValueError( "Both `explanation_metadata` and `explanation_parameters` should be specified or None." ) + # Raises ValueError if invalid accelerator if accelerator_type: utils.validate_accelerator_type(accelerator_type) From bceef117a4f330c8cec5a63447d0be3fbe28daa3 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 9 May 2022 22:52:04 +0000 Subject: [PATCH 064/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .kokoro/continuous/continuous.cfg | 1 + 1 file changed, 1 insertion(+) create mode 100644 .kokoro/continuous/continuous.cfg diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg new file mode 100644 index 0000000000..8f43917d92 --- /dev/null +++ b/.kokoro/continuous/continuous.cfg @@ -0,0 +1 @@ +# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file From 90aa205768050a26bdffc50c21a6d14ecbe63e4c Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 9 May 2022 16:38:03 -0700 Subject: [PATCH 065/108] fixed type hint --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 021690b867..c71e26f771 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1722,7 +1722,7 @@ def _http_request( url: str, body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, - ): + ) -> "urllib3.response.HTTPResponse": # type: ignore """Helper function used to perform HTTP requests for private Endpoint. Args: From 90cc45337dd4b332769fa21d1380625bb80266fa Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 9 May 2022 23:40:38 +0000 Subject: [PATCH 066/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index c71e26f771..b2f195bd5c 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1722,7 +1722,7 @@ def _http_request( url: str, body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, - ) -> "urllib3.response.HTTPResponse": # type: ignore + ) -> "urllib3.response.HTTPResponse": # type: ignore """Helper function used to perform HTTP requests for private Endpoint. Args: From f8bf84623df0e17ad19a937aca0b2c4044a79724 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 10 May 2022 09:50:57 -0700 Subject: [PATCH 067/108] lint issue --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index c71e26f771..129bb500ad 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1722,7 +1722,7 @@ def _http_request( url: str, body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, - ) -> "urllib3.response.HTTPResponse": # type: ignore + ) -> "urllib3.response.HTTPResponse": # type: ignore # noqa """Helper function used to perform HTTP requests for private Endpoint. Args: From 1cd43ba0bf4cb020a125b3d7d60b44f0cb298b10 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 10 May 2022 09:52:44 -0700 Subject: [PATCH 068/108] lint issue --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 129bb500ad..177570dd51 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1722,7 +1722,7 @@ def _http_request( url: str, body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, - ) -> "urllib3.response.HTTPResponse": # type: ignore # noqa + ) -> "urllib3.response.HTTPResponse": # type: ignore # noqa: F821 """Helper function used to perform HTTP requests for private Endpoint. Args: From ce90514f84a878ad24988aa0247898eaaef27e27 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 31 May 2022 14:30:12 -0700 Subject: [PATCH 069/108] adding system tests --- google/cloud/aiplatform/models.py | 10 +-- .../aiplatform/test_private_endpoint.py | 83 +++++++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 tests/system/aiplatform/test_private_endpoint.py diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index aed8a7c31d..1455bf39ea 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1492,13 +1492,13 @@ def __init__( Example usage: my_private_endpoint = aiplatform.PrivateEndpoint( - endpoint_name="projects/123/locations/us-central1/endpoints/my_endpoint_id" + endpoint_name="projects/123/locations/us-central1/endpoints/1234567891234567890" ) or (when project and location are initialized) my_private_endpoint = aiplatform.PrivateEndpoint( - endpoint_name="my_endpoint_id" + endpoint_name="1234567891234567890" ) Args: @@ -1578,14 +1578,14 @@ def create( display_name="my_endpoint_name", project="my_project_id", location="us-central1", - network="projects/123/global/networks/my_vpc" + network="projects/123456789123/global/networks/my_vpc" ) or (when project and location are initialized) my_private_endpoint = aiplatform.PrivateEndpoint.create( display_name="my_endpoint_name", - network="projects/123/global/networks/my_vpc" + network="projects/123456789123/global/networks/my_vpc" ) Args: @@ -1601,7 +1601,7 @@ def create( set in aiplatform.init will be used. network (str): Optional. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + this Endpoint will be peered. E.g. "projects/123456789123/global/networks/my_vpc". Private services access must already be configured for the network. If not set, network set in aiplatform.init will be used. description (str): diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py new file mode 100644 index 0000000000..fb6b320b0d --- /dev/null +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import uuid + +from google.cloud import aiplatform + +from tests.system.aiplatform import e2e_base + +# project +_TEST_ENDPOINT_DISPLAY_NAME = "endpoint_display_name" + +class TestPrivateEndpoint(e2e_base.TestEndToEnd): + + _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" + + def test_create_delete_private_endpoint(self): + aiplatform.init( + project=e2e_base._PROJECT, + location=e2e_base._LOCATION, + ) + + private_endpoint = aiplatform.PrivateEndpoint.create( + display_name=_TEST_ENDPOINT_DISPLAY_NAME, + network=e2e_base._VPC_NETWORK_URI + ) + + try: + # Verify that the retrieved private Endpoint is the same + my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + assert private_endpoint.resource_name == my_private_endpoint.resource_name + assert private_endpoint.display_name == my_private_endpoint.display_name + + # Verify the endpoint is in the private Endpoint list + list_private_endpoint = aiplatform.PrivateEndpoint.list() + assert private_endpoint.resource_name in [ + private_endpoint.resource_name for private_endpoint in list_private_endpoint + ] + finally: + if private_endpoint is not None: + private_endpoint.delete() + + def test_create_deploy_delete_private_endpoint(self): + aiplatform.init( + project=e2e_base._PROJECT, + location=e2e_base._LOCATION, + ) + + private_endpoint = aiplatform.PrivateEndpoint.create( + display_name=_TEST_ENDPOINT_DISPLAY_NAME, + network=e2e_base._VPC_NETWORK_URI + ) + + try: + # Verify that the retrieved private Endpoint is the same + my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + assert private_endpoint.resource_name == my_private_endpoint.resource_name + assert private_endpoint.display_name == my_private_endpoint.display_name + + # Verify the endpoint is in the private Endpoint list + list_private_endpoint = aiplatform.PrivateEndpoint.list() + assert private_endpoint.resource_name in [ + private_endpoint.resource_name for private_endpoint in list_private_endpoint + ] + finally: + if private_endpoint is not None: + private_endpoint.delete() + + \ No newline at end of file From fa40d4da7a9c0dec11831a131d1c426d13cfadb8 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 31 May 2022 21:33:13 +0000 Subject: [PATCH 070/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .../aiplatform/test_private_endpoint.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index fb6b320b0d..32011b2299 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -24,10 +24,11 @@ # project _TEST_ENDPOINT_DISPLAY_NAME = "endpoint_display_name" + class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -35,25 +36,27 @@ def test_create_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, - network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint + private_endpoint.resource_name + for private_endpoint in list_private_endpoint ] finally: if private_endpoint is not None: private_endpoint.delete() - + def test_create_deploy_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -61,23 +64,23 @@ def test_create_deploy_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, - network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint + private_endpoint.resource_name + for private_endpoint in list_private_endpoint ] finally: if private_endpoint is not None: private_endpoint.delete() - - \ No newline at end of file From 24f3f7444418c00794c91894a5cd91fa6b96cc42 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 1 Jun 2022 15:38:00 -0700 Subject: [PATCH 071/108] adding system test changes --- .../aiplatform/test_private_endpoint.py | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 32011b2299..0129ccaede 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -21,14 +21,13 @@ from tests.system.aiplatform import e2e_base -# project _TEST_ENDPOINT_DISPLAY_NAME = "endpoint_display_name" - +_MODEL_ID = "6775300601518489600" class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -36,27 +35,25 @@ def test_create_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, + network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint( - endpoint_name=private_endpoint.resource_name - ) + my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name - for private_endpoint in list_private_endpoint + private_endpoint.resource_name for private_endpoint in list_private_endpoint ] finally: if private_endpoint is not None: private_endpoint.delete() - + def test_create_deploy_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -64,23 +61,32 @@ def test_create_deploy_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, + network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint( - endpoint_name=private_endpoint.resource_name - ) + my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name - for private_endpoint in list_private_endpoint + private_endpoint.resource_name for private_endpoint in list_private_endpoint ] + + my_model = aiplatform.Model(model_name=_MODEL_ID) + my_private_endpoint.deploy(model=my_model) + assert my_private_endpoint._gca_resource.deployed_models + + deployed_model_id = my_private_endpoint.list_models()[0].id + my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) + assert not my_private_endpoint._gca_resource.deployed_models + finally: if private_endpoint is not None: private_endpoint.delete() + + \ No newline at end of file From f1be9481356a7826373044e3ea7aec482514b2df Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 1 Jun 2022 22:40:31 +0000 Subject: [PATCH 072/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 562 +++++++++--------- .../aiplatform/test_private_endpoint.py | 27 +- 2 files changed, 296 insertions(+), 293 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 89c64e682d..0da68a50cf 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -803,90 +803,90 @@ def _deploy( ) -> None: """Deploys a Model to the Endpoint. - Args: - model (aiplatform.Model): - Required. Model to be deployed. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the larger value of min_replica_count or 1 will - be used. If value provided is smaller than min_replica_count, it - will automatically be increased to be min_replica_count. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. -<<<<<<< HEAD - -======= - autoscaling_target_cpu_utilization (int): - Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. ->>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Raises: - ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. + Args: + model (aiplatform.Model): + Required. Model to be deployed. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the larger value of min_replica_count or 1 will + be used. If value provided is smaller than min_replica_count, it + will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + <<<<<<< HEAD + + ======= + autoscaling_target_cpu_utilization (int): + Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. + >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b + Raises: + ValueError: If there is not current traffic split and traffic percentage + is not 0 or 100. """ _LOGGER.log_action_start_against_resource( f"Deploying Model {model.resource_name} to", "", self @@ -947,100 +947,100 @@ def _deploy_call( ): """Helper method to deploy model to endpoint. - Args: - api_client (endpoint_service_client.EndpointServiceClient): - Required. endpoint_service_client.EndpointServiceClient to make call. - endpoint_resource_name (str): - Required. Endpoint resource name to deploy model to. - model (aiplatform.Model): - Required. Model to be deployed. - endpoint_resource_traffic_split (proto.MapField): - Optional. Endpoint current resource traffic split. - network (str): - Optional. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". - Private services access must already be configured for the network. - If not set, network set in aiplatform.init will be used. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the larger value of min_replica_count or 1 will - be used. If value provided is smaller than min_replica_count, it - will automatically be increased to be min_replica_count. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. -<<<<<<< HEAD - -======= - autoscaling_target_cpu_utilization (int): - Optional. Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Optional. Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. ->>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Raises: - ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. - ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - ValueError: If model does not support deployment. + Args: + api_client (endpoint_service_client.EndpointServiceClient): + Required. endpoint_service_client.EndpointServiceClient to make call. + endpoint_resource_name (str): + Required. Endpoint resource name to deploy model to. + model (aiplatform.Model): + Required. Model to be deployed. + endpoint_resource_traffic_split (proto.MapField): + Optional. Endpoint current resource traffic split. + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + Private services access must already be configured for the network. + If not set, network set in aiplatform.init will be used. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the larger value of min_replica_count or 1 will + be used. If value provided is smaller than min_replica_count, it + will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + <<<<<<< HEAD + + ======= + autoscaling_target_cpu_utilization (int): + Optional. Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Optional. Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. + >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b + Raises: + ValueError: If there is not current traffic split and traffic percentage + is not 0 or 100. + ValueError: If only `explanation_metadata` or `explanation_parameters` + is specified. + ValueError: If model does not support deployment. """ max_replica_count = max(min_replica_count, max_replica_count) @@ -2949,7 +2949,7 @@ def deploy( Optional. Target Accelerator Duty Cycle. Must also set accelerator_type and accelerator_count if specified. A default value of 60 will be used if not specified. - + Returns: endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. @@ -3025,108 +3025,108 @@ def _deploy( ) -> Union[Endpoint, PrivateEndpoint]: """Deploys model to endpoint. Endpoint will be created if unspecified. - Args: - endpoint (Union[Endpoint, PrivateEndpoint]): - Optional. Public or private Endpoint to deploy model to. If not specified, - endpoint display name will be model display name+'_endpoint'. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the smaller value of min_replica_count or 1 will - be used. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - encryption_spec_key_name (Optional[str]): - Optional. The Cloud KMS resource identifier of the customer - managed encryption key used to protect the model. Has the - form: - ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. - The key needs to be in the same region as where the compute - resource is created. - - If set, this Model and all sub-resources of this Model will be secured by this key. - - Overrides encryption_spec_key_name set in aiplatform.init - network (str): - Optional. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". - Private services access must already be configured for the network. - - If set, a private Endpoint will be created. Read more about private - Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. -<<<<<<< HEAD - -======= - autoscaling_target_cpu_utilization (int): - Optional. Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Optional. Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. ->>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Returns: - endpoint (Union[Endpoint, PrivateEndpoint]): - Endpoint with the deployed model. + Args: + endpoint (Union[Endpoint, PrivateEndpoint]): + Optional. Public or private Endpoint to deploy model to. If not specified, + endpoint display name will be model display name+'_endpoint'. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the smaller value of min_replica_count or 1 will + be used. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + encryption_spec_key_name (Optional[str]): + Optional. The Cloud KMS resource identifier of the customer + managed encryption key used to protect the model. Has the + form: + ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. + The key needs to be in the same region as where the compute + resource is created. + + If set, this Model and all sub-resources of this Model will be secured by this key. + + Overrides encryption_spec_key_name set in aiplatform.init + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. + + If set, a private Endpoint will be created. Read more about private + Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + <<<<<<< HEAD + + ======= + autoscaling_target_cpu_utilization (int): + Optional. Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Optional. Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. + >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b + Returns: + endpoint (Union[Endpoint, PrivateEndpoint]): + Endpoint with the deployed model. """ if endpoint is None: diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 0129ccaede..c48c01d619 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -24,10 +24,11 @@ _TEST_ENDPOINT_DISPLAY_NAME = "endpoint_display_name" _MODEL_ID = "6775300601518489600" + class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -35,25 +36,27 @@ def test_create_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, - network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint + private_endpoint.resource_name + for private_endpoint in list_private_endpoint ] finally: if private_endpoint is not None: private_endpoint.delete() - + def test_create_deploy_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, @@ -61,20 +64,22 @@ def test_create_deploy_delete_private_endpoint(self): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, - network=e2e_base._VPC_NETWORK_URI + display_name=_TEST_ENDPOINT_DISPLAY_NAME, network=e2e_base._VPC_NETWORK_URI ) try: # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name # Verify the endpoint is in the private Endpoint list list_private_endpoint = aiplatform.PrivateEndpoint.list() assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint + private_endpoint.resource_name + for private_endpoint in list_private_endpoint ] my_model = aiplatform.Model(model_name=_MODEL_ID) @@ -88,5 +93,3 @@ def test_create_deploy_delete_private_endpoint(self): finally: if private_endpoint is not None: private_endpoint.delete() - - \ No newline at end of file From 4abb6398be8fed651666c213f7b0802ac2238876 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 3 Jun 2022 15:18:49 -0700 Subject: [PATCH 073/108] adding shared_state to system test --- .../aiplatform/test_private_endpoint.py | 73 +++++++------------ 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 0129ccaede..c556a48584 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -16,77 +16,54 @@ # import uuid +import pytest from google.cloud import aiplatform from tests.system.aiplatform import e2e_base -_TEST_ENDPOINT_DISPLAY_NAME = "endpoint_display_name" -_MODEL_ID = "6775300601518489600" +_MODEL_ID = "1494196719728984064" +@pytest.mark.usefixtures("tear_down_resources") class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - def test_create_delete_private_endpoint(self): - aiplatform.init( - project=e2e_base._PROJECT, - location=e2e_base._LOCATION, - ) - - private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, - network=e2e_base._VPC_NETWORK_URI - ) + def test_create_deploy_delete_private_endpoint(self, shared_state): + # Collection of resources generated by this test, to be deleted during teardown + shared_state["resources"] = [] - try: - # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) - assert private_endpoint.resource_name == my_private_endpoint.resource_name - assert private_endpoint.display_name == my_private_endpoint.display_name - - # Verify the endpoint is in the private Endpoint list - list_private_endpoint = aiplatform.PrivateEndpoint.list() - assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint - ] - finally: - if private_endpoint is not None: - private_endpoint.delete() - - def test_create_deploy_delete_private_endpoint(self): aiplatform.init( project=e2e_base._PROJECT, location=e2e_base._LOCATION, ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=_TEST_ENDPOINT_DISPLAY_NAME, + display_name=self._make_display_name("private_endpoint"), network=e2e_base._VPC_NETWORK_URI ) + shared_state["resources"].append(private_endpoint) - try: - # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) - assert private_endpoint.resource_name == my_private_endpoint.resource_name - assert private_endpoint.display_name == my_private_endpoint.display_name + # Verify that the retrieved private Endpoint is the same + my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + assert private_endpoint.resource_name == my_private_endpoint.resource_name + assert private_endpoint.display_name == my_private_endpoint.display_name - # Verify the endpoint is in the private Endpoint list - list_private_endpoint = aiplatform.PrivateEndpoint.list() - assert private_endpoint.resource_name in [ - private_endpoint.resource_name for private_endpoint in list_private_endpoint - ] + # Verify the endpoint is in the private Endpoint list + list_private_endpoint = aiplatform.PrivateEndpoint.list() + assert private_endpoint.resource_name in [ + private_endpoint.resource_name for private_endpoint in list_private_endpoint + ] - my_model = aiplatform.Model(model_name=_MODEL_ID) - my_private_endpoint.deploy(model=my_model) - assert my_private_endpoint._gca_resource.deployed_models + # Retrieve model, deploy to private Endpoint, then undeploy + my_model = aiplatform.Model(model_name=_MODEL_ID) + shared_state["resources"].append(my_model) - deployed_model_id = my_private_endpoint.list_models()[0].id - my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) - assert not my_private_endpoint._gca_resource.deployed_models + my_private_endpoint.deploy(model=my_model) + assert my_private_endpoint._gca_resource.deployed_models - finally: - if private_endpoint is not None: - private_endpoint.delete() + deployed_model_id = my_private_endpoint.list_models()[0].id + my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) + assert not my_private_endpoint._gca_resource.deployed_models \ No newline at end of file From 66a02d6ae30adba42ebcadc0fa1d2ea153cf5170 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 3 Jun 2022 22:22:56 +0000 Subject: [PATCH 074/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/system/aiplatform/test_private_endpoint.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 8602f7b50d..8069612225 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -24,11 +24,12 @@ _MODEL_ID = "1494196719728984064" + @pytest.mark.usefixtures("tear_down_resources") class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_deploy_delete_private_endpoint(self, shared_state): # Collection of resources generated by this test, to be deleted during teardown shared_state["resources"] = [] @@ -40,12 +41,14 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): private_endpoint = aiplatform.PrivateEndpoint.create( display_name=self._make_display_name("private_endpoint"), - network=e2e_base._VPC_NETWORK_URI + network=e2e_base._VPC_NETWORK_URI, ) shared_state["resources"].append(private_endpoint) # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name @@ -65,5 +68,3 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): deployed_model_id = my_private_endpoint.list_models()[0].id my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) assert not my_private_endpoint._gca_resource.deployed_models - - From 356c194d7eaf29385f36a04a2ce81ca5407eac31 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 3 Jun 2022 22:23:50 +0000 Subject: [PATCH 075/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/system/aiplatform/test_private_endpoint.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 8602f7b50d..8069612225 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -24,11 +24,12 @@ _MODEL_ID = "1494196719728984064" + @pytest.mark.usefixtures("tear_down_resources") class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_deploy_delete_private_endpoint(self, shared_state): # Collection of resources generated by this test, to be deleted during teardown shared_state["resources"] = [] @@ -40,12 +41,14 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): private_endpoint = aiplatform.PrivateEndpoint.create( display_name=self._make_display_name("private_endpoint"), - network=e2e_base._VPC_NETWORK_URI + network=e2e_base._VPC_NETWORK_URI, ) shared_state["resources"].append(private_endpoint) # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name @@ -65,5 +68,3 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): deployed_model_id = my_private_endpoint.list_models()[0].id my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) assert not my_private_endpoint._gca_resource.deployed_models - - From ba62506da356acb9f73bc6a9752965cc28cd8ca3 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 7 Jun 2022 01:07:52 -0700 Subject: [PATCH 076/108] trying outside import --- google/cloud/aiplatform/models.py | 25 ++++++------------- .../aiplatform/test_private_endpoint.py | 13 +++++----- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 0da68a50cf..24292d48e8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -21,6 +21,13 @@ import shutil import tempfile +try: + import urllib3 +except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) + from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation @@ -1690,12 +1697,6 @@ def __init__( Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. """ - try: - import urllib3 - except ImportError: - raise ImportError( - "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." - ) super().__init__( endpoint_name=endpoint_name, @@ -1872,12 +1873,6 @@ def _construct_sdk_resource_from_gapic( PrivateEndpoint: An initialized PrivateEndpoint resource. """ - try: - import urllib3 - except ImportError: - raise ImportError( - "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." - ) endpoint = cls._empty_constructor( project=project, location=location, credentials=credentials @@ -1916,12 +1911,6 @@ def _http_request( Raises: RuntimeError: If a HTTP request could not be made. """ - try: - import urllib3 - except ImportError: - raise ImportError( - "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." - ) try: response = self._http_client.request( diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 8602f7b50d..c097535b67 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -14,8 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -import uuid import pytest from google.cloud import aiplatform @@ -24,11 +22,12 @@ _MODEL_ID = "1494196719728984064" + @pytest.mark.usefixtures("tear_down_resources") class TestPrivateEndpoint(e2e_base.TestEndToEnd): _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" - + def test_create_deploy_delete_private_endpoint(self, shared_state): # Collection of resources generated by this test, to be deleted during teardown shared_state["resources"] = [] @@ -40,12 +39,14 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): private_endpoint = aiplatform.PrivateEndpoint.create( display_name=self._make_display_name("private_endpoint"), - network=e2e_base._VPC_NETWORK_URI + network=e2e_base._VPC_NETWORK_URI, ) shared_state["resources"].append(private_endpoint) # Verify that the retrieved private Endpoint is the same - my_private_endpoint = aiplatform.PrivateEndpoint(endpoint_name=private_endpoint.resource_name) + my_private_endpoint = aiplatform.PrivateEndpoint( + endpoint_name=private_endpoint.resource_name + ) assert private_endpoint.resource_name == my_private_endpoint.resource_name assert private_endpoint.display_name == my_private_endpoint.display_name @@ -65,5 +66,3 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): deployed_model_id = my_private_endpoint.list_models()[0].id my_private_endpoint.undeploy(deployed_model_id=deployed_model_id) assert not my_private_endpoint._gca_resource.deployed_models - - From add3ec11f4247855d0faaba295ca43216043af9a Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 7 Jun 2022 09:23:06 -0700 Subject: [PATCH 077/108] putting imports back inside --- google/cloud/aiplatform/models.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 24292d48e8..0da68a50cf 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -21,13 +21,6 @@ import shutil import tempfile -try: - import urllib3 -except ImportError: - raise ImportError( - "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." - ) - from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation @@ -1697,6 +1690,12 @@ def __init__( Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. """ + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) super().__init__( endpoint_name=endpoint_name, @@ -1873,6 +1872,12 @@ def _construct_sdk_resource_from_gapic( PrivateEndpoint: An initialized PrivateEndpoint resource. """ + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) endpoint = cls._empty_constructor( project=project, location=location, credentials=credentials @@ -1911,6 +1916,12 @@ def _http_request( Raises: RuntimeError: If a HTTP request could not be made. """ + try: + import urllib3 + except ImportError: + raise ImportError( + "Cannot import the urllib3 HTTP client. Please install google-cloud-aiplatform[private_endpoints]." + ) try: response = self._http_client.request( From ebd8710acd73de1bfba7323bf91d77beb295b481 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 8 Jun 2022 17:59:48 -0700 Subject: [PATCH 078/108] added system test changes --- tests/system/aiplatform/test_private_endpoint.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index c097535b67..c577641c5f 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -20,13 +20,14 @@ from tests.system.aiplatform import e2e_base -_MODEL_ID = "1494196719728984064" +# permanent_custom_mnist_model +_MODEL_ID = "6430031960164270080" @pytest.mark.usefixtures("tear_down_resources") class TestPrivateEndpoint(e2e_base.TestEndToEnd): - _temp_prefix = "temp_vertex_sdk_e2e_private_endpoint_test" + _temp_prefix = "temp_vertex_sdk_e2e" def test_create_deploy_delete_private_endpoint(self, shared_state): # Collection of resources generated by this test, to be deleted during teardown @@ -38,7 +39,7 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): ) private_endpoint = aiplatform.PrivateEndpoint.create( - display_name=self._make_display_name("private_endpoint"), + display_name=self._make_display_name("private_endpoint_test"), network=e2e_base._VPC_NETWORK_URI, ) shared_state["resources"].append(private_endpoint) @@ -56,9 +57,8 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): private_endpoint.resource_name for private_endpoint in list_private_endpoint ] - # Retrieve model, deploy to private Endpoint, then undeploy + # Retrieve permanent model, deploy to private Endpoint, then undeploy my_model = aiplatform.Model(model_name=_MODEL_ID) - shared_state["resources"].append(my_model) my_private_endpoint.deploy(model=my_model) assert my_private_endpoint._gca_resource.deployed_models From d1f3e95d9053348a314d9a09e9552055b46fdf12 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 8 Jun 2022 18:00:36 -0700 Subject: [PATCH 079/108] added system test changes --- tests/system/aiplatform/e2e_base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/system/aiplatform/e2e_base.py b/tests/system/aiplatform/e2e_base.py index 93cdd4fe9e..80759c8491 100644 --- a/tests/system/aiplatform/e2e_base.py +++ b/tests/system/aiplatform/e2e_base.py @@ -151,7 +151,6 @@ def tear_down_resources(self, shared_state: Dict[str, Any]): yield # TODO(b/218310362): Add resource deletion system tests - # Bring all Endpoints to the front of the list # Ensures Models are undeployed first before we attempt deletion shared_state["resources"].sort( From 595407acc627dde31d1621785cfc0c2367baea50 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Fri, 10 Jun 2022 15:10:47 -0700 Subject: [PATCH 080/108] adding fixes --- google/cloud/aiplatform/models.py | 305 +++++++++++++++--------------- 1 file changed, 152 insertions(+), 153 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 04f332ed53..0a5c9dab3b 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -20,7 +20,6 @@ import re import shutil import tempfile - from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Union from google.api_core import operation @@ -54,6 +53,7 @@ _DEFAULT_MACHINE_TYPE = "n1-standard-2" _DEPLOYING_MODEL_TRAFFIC_SPLIT_KEY = "0" +_SUCCESSFUL_HTTP_RESPONSE = 300 _LOGGER = base.Logger(__name__) @@ -369,8 +369,8 @@ def _create( this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - If set, this will be a private Endpoint. Read more about private - Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + If set, this will be a PrivateEndpoint. Read more about PrivateEndpoints + [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to create this endpoint synchronously. create_request_timeout (float): @@ -388,7 +388,7 @@ def _create( that are not included in either the URI or the body. Returns: - endpoint (Endpoint): + endpoint (aiplatform.Endpoint): Created endpoint. """ @@ -1461,7 +1461,7 @@ def predict( timeout (float): Optional. The timeout for this request in seconds. Returns: - prediction: Prediction with returned predictions and Model Id. + prediction: Prediction with returned predictions and Model ID. """ self.wait() @@ -1519,7 +1519,7 @@ def explain( timeout (float): Optional. The timeout for this request in seconds. Returns: - prediction: Prediction with returned predictions, explanations, and Model Id. + prediction: Prediction with returned predictions, explanations, and Model ID. """ self.wait() @@ -1644,7 +1644,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: class PrivateEndpoint(Endpoint): """ - Represents a Vertex AI private Endpoint resource. + Represents a Vertex AI PrivateEndpoint resource. Read more [about private endpoints in the documentation.](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) """ @@ -1656,7 +1656,7 @@ def __init__( location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, ): - """Retrieves a private Endpoint resource. + """Retrieves a PrivateEndpoint resource. Example usage: my_private_endpoint = aiplatform.PrivateEndpoint( @@ -1683,6 +1683,9 @@ def __init__( credentials (auth_credentials.Credentials): Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. + + Raises: + ImportError: If there is an issue importing the `urllib3` package. """ try: import urllib3 @@ -1700,7 +1703,7 @@ def __init__( if not self.network: raise ValueError( - "Please ensure the Endpoint being retrieved is a private Endpoint." + "Please ensure the Endpoint being retrieved is a PrivateEndpoint." ) self._http_client = urllib3.PoolManager() @@ -1739,7 +1742,7 @@ def create( encryption_spec_key_name: Optional[str] = None, sync=True, ) -> "PrivateEndpoint": - """Creates a new private Endpoint. + """Creates a new PrivateEndpoint. Example usage: my_private_endpoint = aiplatform.PrivateEndpoint.create( @@ -1806,6 +1809,9 @@ def create( Returns: endpoint (PrivateEndpoint): Created endpoint. + + Raises: + ValueError: A network must be instantiated when creating a PrivateEndpoint. """ api_client = cls._instantiate_client(location=location, credentials=credentials) @@ -1846,11 +1852,11 @@ def _construct_sdk_resource_from_gapic( location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, ) -> "PrivateEndpoint": - """Given a GAPIC private Endpoint object, return the SDK representation. + """Given a GAPIC PrivateEndpoint object, return the SDK representation. Args: gapic_resource (proto.Message): - A GAPIC representation of a private Endpoint resource, usually + A GAPIC representation of a PrivateEndpoint resource, usually retrieved by a get_* or in a list_* API call. project (str): Optional. Project to construct Endpoint object from. If not set, @@ -1865,6 +1871,9 @@ def _construct_sdk_resource_from_gapic( Returns: PrivateEndpoint: An initialized PrivateEndpoint resource. + + Raises: + ImportError: If there is an issue importing the `urllib3` package. """ try: import urllib3 @@ -1890,7 +1899,7 @@ def _http_request( body: Optional[Dict[Any, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> "urllib3.response.HTTPResponse": # type: ignore # noqa: F821 - """Helper function used to perform HTTP requests for private Endpoint. + """Helper function used to perform HTTP requests for PrivateEndpoint. Args: method (str): @@ -1898,8 +1907,8 @@ def _http_request( url (str): Required. The url used to send requests and get responses from. body (Dict[Any, Any]): - Optional. Data sent to the url in the HTTP request. For private - Endpoint, an instance is sent and a prediction response is expected. + Optional. Data sent to the url in the HTTP request. For a PrivateEndpoint, + an instance is sent and a prediction response is expected. headers (Dict[str, str]): Optional. Header in the HTTP request. @@ -1908,7 +1917,10 @@ def _http_request( A HTTP Response container. Raises: + ImportError: If there is an issue importing the `urllib3` package. RuntimeError: If a HTTP request could not be made. + RuntimeError: A connection could not be established with the PrivateEndpoint and + a HTTP request could not be made. """ try: import urllib3 @@ -1922,7 +1934,7 @@ def _http_request( method=method, url=url, body=body, headers=headers ) - if response.status < 300: + if response.status < _SUCCESSFUL_HTTP_RESPONSE: return response else: raise RuntimeError( @@ -1933,14 +1945,14 @@ def _http_request( except urllib3.exceptions.MaxRetryError as exc: raise RuntimeError( f"Failed to make a {method} request to this URI, make sure: " - " this call is being made inside the network this private Endpoint is peered to " + " this call is being made inside the network this PrivateEndpoint is peered to " f"({self._gca_resource.network}), calling health_check() returns True, " f"and that {url} is a valid URL." ) from exc def predict(self, instances: List, parameters: Optional[Dict] = None) -> Prediction: - """Make a prediction against this private Endpoint using a HTTP request. - This method must be called within the network the private Endpoint is peered to. + """Make a prediction against this PrivateEndpoint using a HTTP request. + This method must be called within the network the PrivateEndpoint is peered to. The predict() call will fail otherwise. To check, use `PrivateEndpoint.network`. Example usage: @@ -1950,7 +1962,8 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict Args: instances (List): Required. The instances that are the input to the - prediction call. A DeployedModel may have an upper limit + prediction call. Instance types mut be JSON serializable. + A DeployedModel may have an upper limit on the number of instances it supports per request, and when it is exceeded the prediction call errors in case of AutoML Models, or, in case of customer created @@ -1969,7 +1982,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ``parameters_schema_uri``. Returns: - prediction: Prediction with returned predictions and Model Id. + prediction (Prediction): Prediction object with returned predictions and Model ID. Raises: RuntimeError: If a model has not been deployed a request cannot be made. @@ -1979,7 +1992,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict if not self._gca_resource.deployed_models: raise RuntimeError( - "Cannot make a predict request because a model has not been deployed on this private " + "Cannot make a predict request because a model has not been deployed on this Private" "Endpoint. Please ensure a model has been deployed." ) @@ -2004,15 +2017,15 @@ def explain(self): def health_check(self) -> bool: """ - Makes a request to this private Endpoint's health check URI. Must be within network - that this private Endpoint is in. + Makes a request to this PrivateEndpoint's health check URI. Must be within network + that this PrivateEndpoint is in. Example Usage: if my_private_endpoint.health_check(): - print("Endpoint is healthy!") + print("PrivateEndpoint is healthy!") Returns: - bool: Checks if calls can be made to this private Endpoint. + bool: Checks if calls can be made to this PrivateEndpoint. Raises: RuntimeError: If a model has not been deployed a request cannot be made. @@ -2022,7 +2035,7 @@ def health_check(self) -> bool: if not self._gca_resource.deployed_models: raise RuntimeError( - "Cannot make a health check request because a model has not been deployed on this private " + "Cannot make a health check request because a model has not been deployed on this Private" "Endpoint. Please ensure a model has been deployed." ) @@ -2031,7 +2044,7 @@ def health_check(self) -> bool: url=self.health_http_uri, ) - return response.status == 200 + return response.status < _SUCCESSFUL_HTTP_RESPONSE @classmethod def list( @@ -2042,7 +2055,7 @@ def list( location: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, ) -> List["models.PrivateEndpoint"]: - """List all private Endpoint resource instances. + """List all PrivateEndpoint resource instances. Example Usage: my_private_endpoints = aiplatform.PrivateEndpoint.list() @@ -2078,7 +2091,7 @@ def list( return cls._list_with_local_order( cls_filter=lambda ep: bool( ep.network - ), # Only private Endpoints have a network set + ), # Only PrivateEndpoints have a network set filter=filter, order_by=order_by, project=project, @@ -2103,7 +2116,7 @@ def deploy( metadata: Optional[Sequence[Tuple[str, str]]] = (), sync=True, ) -> None: - """Deploys a Model to the private Endpoint. + """Deploys a Model to the PrivateEndpoint. Example Usage: my_private_endpoint.deploy( @@ -2166,11 +2179,11 @@ def deploy( Raises: ValueError: If a model has already been deployed another one cannot be - deployed with private Endpoint. + deployed with a PrivateEndpoint. """ if len(self._gca_resource.deployed_models): raise ValueError( - "A maximum of one model can be deployed to each private Endpoint. " + "A maximum of one model can be deployed to each PrivateEndpoint. " "Please call PrivateEndpoint.undeploy() before deploying another " "model." ) @@ -2206,10 +2219,9 @@ def deploy( def undeploy( self, deployed_model_id: str, - traffic_split: Optional[Dict[str, int]] = None, sync=True, ) -> None: - """Undeploys a deployed model from the private Endpoint. + """Undeploys a deployed model from the PrivateEndpoint. Example Usage: my_private_endpoint.undeploy( @@ -2226,36 +2238,26 @@ def undeploy( Args: deployed_model_id (str): Required. The ID of the DeployedModel to be undeployed from the - private Endpoint. Use PrivateEndpoint.list_models() to get the + PrivateEndpoint. Use PrivateEndpoint.list_models() to get the deployed model ID. sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. - - Raises: - ValueError: If the `traffic_split` argument is passed when undeploying - a private Endpoint. """ self._sync_gca_resource_if_skipped() - # TODO(b/211351292): Remove check once traffic split is supported - # Skip unallocating traffic and raise if new split is provided - if traffic_split: - raise ValueError( - "Traffic splitting is not yet supported by private Endpoints. " - "Undeploy the model without providing the `traffic_split` argument." - ) + # TODO(b/211351292): Add traffic splitting for PrivateEndpoint self._undeploy( deployed_model_id=deployed_model_id, - traffic_split=traffic_split, + traffic_split=None, sync=sync, ) def delete(self, force: bool = False, sync: bool = True) -> None: - """Deletes this Vertex AI private Endpoint resource. If force is set to True, - all models on this private Endpoint will be undeployed prior to deletion. + """Deletes this Vertex AI PrivateEndpoint resource. If force is set to True, + all models on this PrivateEndpoint will be undeployed prior to deletion. Args: force (bool): @@ -2928,8 +2930,8 @@ def deploy( this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - If set, a private Endpoint will be created. Read more about private - Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints + [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will @@ -2963,10 +2965,10 @@ def deploy( if isinstance(endpoint, PrivateEndpoint): if traffic_percentage or traffic_split: raise ValueError( - "Traffic splitting is not yet supported for private Endpoints. " + "Traffic splitting is not yet supported for PrivateEndpoints. " "Try calling deploy() without providing a `traffic_split` or " "`traffic_percentage`. A maximum of one model can be deployed " - "to each private Endpoint." + "to each PrivateEndpoint." ) return self._deploy( @@ -3019,108 +3021,105 @@ def _deploy( ) -> Union[Endpoint, PrivateEndpoint]: """Deploys model to endpoint. Endpoint will be created if unspecified. - Args: - endpoint (Union[Endpoint, PrivateEndpoint]): - Optional. Public or private Endpoint to deploy model to. If not specified, - endpoint display name will be model display name+'_endpoint'. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the smaller value of min_replica_count or 1 will - be used. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - encryption_spec_key_name (Optional[str]): - Optional. The Cloud KMS resource identifier of the customer - managed encryption key used to protect the model. Has the - form: - ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. - The key needs to be in the same region as where the compute - resource is created. + Args: + endpoint (Union[Endpoint, PrivateEndpoint]): + Optional. Public or private Endpoint to deploy model to. If not specified, + endpoint display name will be model display name+'_endpoint'. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the smaller value of min_replica_count or 1 will + be used. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + encryption_spec_key_name (Optional[str]): + Optional. The Cloud KMS resource identifier of the customer + managed encryption key used to protect the model. Has the + form: + ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``. + The key needs to be in the same region as where the compute + resource is created. - If set, this Model and all sub-resources of this Model will be secured by this key. + If set, this Model and all sub-resources of this Model will be secured by this key. - Overrides encryption_spec_key_name set in aiplatform.init - network (str): - Optional. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". - Private services access must already be configured for the network. + Overrides encryption_spec_key_name set in aiplatform.init + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". + Private services access must already be configured for the network. - If set, a private Endpoint will be created. Read more about private - Endpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. - <<<<<<< HEAD + If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints + [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + autoscaling_target_cpu_utilization (int): + Optional. Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Optional. Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. - ======= - autoscaling_target_cpu_utilization (int): - Optional. Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Optional. Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. - >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Returns: - endpoint (Union[Endpoint, PrivateEndpoint]): - Endpoint with the deployed model. + Returns: + endpoint (Union[Endpoint, PrivateEndpoint]): + Endpoint with the deployed model. """ if endpoint is None: @@ -3364,6 +3363,7 @@ def batch_predict( but too high value will result in a whole batch not fitting in a machine's memory, and the whole operation will fail. The default value is 64. + Returns: (jobs.BatchPredictionJob): Instantiated representation of the created batch prediction job. @@ -3511,7 +3511,6 @@ def export_model( Raises: ValueError: If model does not support exporting. - ValueError: If invalid arguments or export formats are provided. """ From e3f2686a61ca7da18725d24d4ce9bd80d3ba154b Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 10 Jun 2022 22:13:26 +0000 Subject: [PATCH 081/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 0a5c9dab3b..b3e8fd7742 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1683,7 +1683,7 @@ def __init__( credentials (auth_credentials.Credentials): Optional. Custom credentials to use to upload this model. Overrides credentials set in aiplatform.init. - + Raises: ImportError: If there is an issue importing the `urllib3` package. """ @@ -1809,7 +1809,7 @@ def create( Returns: endpoint (PrivateEndpoint): Created endpoint. - + Raises: ValueError: A network must be instantiated when creating a PrivateEndpoint. """ @@ -1871,7 +1871,7 @@ def _construct_sdk_resource_from_gapic( Returns: PrivateEndpoint: An initialized PrivateEndpoint resource. - + Raises: ImportError: If there is an issue importing the `urllib3` package. """ @@ -1907,7 +1907,7 @@ def _http_request( url (str): Required. The url used to send requests and get responses from. body (Dict[Any, Any]): - Optional. Data sent to the url in the HTTP request. For a PrivateEndpoint, + Optional. Data sent to the url in the HTTP request. For a PrivateEndpoint, an instance is sent and a prediction response is expected. headers (Dict[str, str]): Optional. Header in the HTTP request. @@ -1919,7 +1919,7 @@ def _http_request( Raises: ImportError: If there is an issue importing the `urllib3` package. RuntimeError: If a HTTP request could not be made. - RuntimeError: A connection could not be established with the PrivateEndpoint and + RuntimeError: A connection could not be established with the PrivateEndpoint and a HTTP request could not be made. """ try: @@ -2930,7 +2930,7 @@ def deploy( this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints + If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): Whether to execute this method synchronously. If False, this method From 6a002128a49f7a5a60dd5915da207ea58675c88a Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 13 Jun 2022 12:12:49 -0700 Subject: [PATCH 082/108] fixing system test and init fixes --- google/cloud/aiplatform/initializer.py | 2 +- tests/system/aiplatform/test_private_endpoint.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/initializer.py b/google/cloud/aiplatform/initializer.py index c75eca35e5..8cb85cdbec 100644 --- a/google/cloud/aiplatform/initializer.py +++ b/google/cloud/aiplatform/initializer.py @@ -86,7 +86,7 @@ def init( resource is created. If set, this resource and all sub-resources will be secured by this key. - network (Optional[str]): + network (str): Optional. The full name of the Compute Engine network to which jobs and resources should be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index c577641c5f..05a613b21f 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -22,6 +22,7 @@ # permanent_custom_mnist_model _MODEL_ID = "6430031960164270080" +_PRIVATE_ENDPOINT_NETWORK = 'projects/580378083368/global/networks/private-endpoint-vpc' @pytest.mark.usefixtures("tear_down_resources") @@ -40,7 +41,7 @@ def test_create_deploy_delete_private_endpoint(self, shared_state): private_endpoint = aiplatform.PrivateEndpoint.create( display_name=self._make_display_name("private_endpoint_test"), - network=e2e_base._VPC_NETWORK_URI, + network=_PRIVATE_ENDPOINT_NETWORK, ) shared_state["resources"].append(private_endpoint) From 91c0d7c3f08a9b2c219df1a5e7a9ddc052c1901a Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 13 Jun 2022 19:16:08 +0000 Subject: [PATCH 083/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- tests/system/aiplatform/test_private_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 05a613b21f..443b5b8e57 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -22,7 +22,7 @@ # permanent_custom_mnist_model _MODEL_ID = "6430031960164270080" -_PRIVATE_ENDPOINT_NETWORK = 'projects/580378083368/global/networks/private-endpoint-vpc' +_PRIVATE_ENDPOINT_NETWORK = "projects/580378083368/global/networks/private-endpoint-vpc" @pytest.mark.usefixtures("tear_down_resources") From ef05242372107ef1f3a731ca8c90dc5fbe7ae1a1 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 13 Jun 2022 19:39:53 +0000 Subject: [PATCH 084/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/model-builder/noxfile.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/samples/model-builder/noxfile.py b/samples/model-builder/noxfile.py index d13780d33a..38bb0a572b 100644 --- a/samples/model-builder/noxfile.py +++ b/samples/model-builder/noxfile.py @@ -171,11 +171,7 @@ def lint(session: nox.sessions.Session) -> None: def blacken(session: nox.sessions.Session) -> None: """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) - python_files = [ - path - for path in os.listdir(".") - if path.endswith(".py") or path.endswith("experiment_tracking") - ] + python_files = [path for path in os.listdir(".") if path.endswith(".py")] session.run("black", *python_files) @@ -184,7 +180,6 @@ def blacken(session: nox.sessions.Session) -> None: # format = isort + black # - @nox.session def format(session: nox.sessions.Session) -> None: """ @@ -234,7 +229,9 @@ def _session_tests( if os.path.exists("requirements-test.txt"): if os.path.exists("constraints-test.txt"): - session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) else: session.install("-r", "requirements-test.txt") with open("requirements-test.txt") as rtfile: @@ -247,9 +244,9 @@ def _session_tests( post_install(session) if "pytest-parallel" in packages: - concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) elif "pytest-xdist" in packages: - concurrent_args.extend(["-n", "auto"]) + concurrent_args.extend(['-n', 'auto']) session.run( "pytest", @@ -279,7 +276,7 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """Returns the root folder of the project.""" + """ Returns the root folder of the project. """ # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) for i in range(10): From 8106652527c40bab742a787dfab2b016a4887d9a Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 13 Jun 2022 14:45:39 -0700 Subject: [PATCH 085/108] adding fixes --- google/cloud/aiplatform/models.py | 371 +++++++++--------- .../aiplatform/test_private_endpoint.py | 2 +- 2 files changed, 180 insertions(+), 193 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index b3e8fd7742..12606b5b44 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -803,90 +803,87 @@ def _deploy( ) -> None: """Deploys a Model to the Endpoint. - Args: - model (aiplatform.Model): - Required. Model to be deployed. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the larger value of min_replica_count or 1 will - be used. If value provided is smaller than min_replica_count, it - will automatically be increased to be min_replica_count. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - sync (bool): - Whether to execute this method synchronously. If False, this method - will be executed in concurrent Future and any downstream object will - be immediately returned and synced when the Future has completed. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. - <<<<<<< HEAD - - ======= - autoscaling_target_cpu_utilization (int): - Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. - >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Raises: - ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. + Args: + model (aiplatform.Model): + Required. Model to be deployed. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the larger value of min_replica_count or 1 will + be used. If value provided is smaller than min_replica_count, it + will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + sync (bool): + Whether to execute this method synchronously. If False, this method + will be executed in concurrent Future and any downstream object will + be immediately returned and synced when the Future has completed. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + autoscaling_target_cpu_utilization (int): + Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. + + Raises: + ValueError: If there is not current traffic split and traffic percentage + is not 0 or 100. """ _LOGGER.log_action_start_against_resource( f"Deploying Model {model.resource_name} to", "", self @@ -947,100 +944,97 @@ def _deploy_call( ): """Helper method to deploy model to endpoint. - Args: - api_client (endpoint_service_client.EndpointServiceClient): - Required. endpoint_service_client.EndpointServiceClient to make call. - endpoint_resource_name (str): - Required. Endpoint resource name to deploy model to. - model (aiplatform.Model): - Required. Model to be deployed. - endpoint_resource_traffic_split (proto.MapField): - Optional. Endpoint current resource traffic split. - network (str): - Optional. The full name of the Compute Engine network to which - this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". - Private services access must already be configured for the network. - If not set, network set in aiplatform.init will be used. - deployed_model_display_name (str): - Optional. The display name of the DeployedModel. If not provided - upon creation, the Model's display_name is used. - traffic_percentage (int): - Optional. Desired traffic to newly deployed model. Defaults to - 0 if there are pre-existing deployed models. Defaults to 100 if - there are no pre-existing deployed models. Negative values should - not be provided. Traffic of previously deployed models at the endpoint - will be scaled down to accommodate new deployed model's traffic. - Should not be provided if traffic_split is provided. - traffic_split (Dict[str, int]): - Optional. A map from a DeployedModel's ID to the percentage of - this Endpoint's traffic that should be forwarded to that DeployedModel. - If a DeployedModel's ID is not listed in this map, then it receives - no traffic. The traffic percentage values must add up to 100, or - map must be empty if the Endpoint is to not accept any traffic at - the moment. Key for model being deployed is "0". Should not be - provided if traffic_percentage is provided. - machine_type (str): - Optional. The type of machine. Not specifying machine type will - result in model to be deployed with automatic resources. - min_replica_count (int): - Optional. The minimum number of machine replicas this deployed - model will be always deployed on. If traffic against it increases, - it may dynamically be deployed onto more replicas, and as traffic - decreases, some of these extra replicas may be freed. - max_replica_count (int): - Optional. The maximum number of replicas this deployed model may - be deployed on when the traffic against it increases. If requested - value is too large, the deployment will error, but if deployment - succeeds then the ability to scale the model to that many replicas - is guaranteed (barring service outages). If traffic against the - deployed model increases beyond what its replicas at maximum may - handle, a portion of the traffic will be dropped. If this value - is not provided, the larger value of min_replica_count or 1 will - be used. If value provided is smaller than min_replica_count, it - will automatically be increased to be min_replica_count. - accelerator_type (str): - Optional. Hardware accelerator type. Must also set accelerator_count if used. - One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, - NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 - accelerator_count (int): - Optional. The number of accelerators to attach to a worker replica. - service_account (str): - The service account that the DeployedModel's container runs as. Specify the - email address of the service account. If this service account is not - specified, the container runs as a service account that doesn't have access - to the resource project. - Users deploying the Model must have the `iam.serviceAccounts.actAs` - permission on this service account. - explanation_metadata (aiplatform.explain.ExplanationMetadata): - Optional. Metadata describing the Model's input and output for explanation. - Both `explanation_metadata` and `explanation_parameters` must be - passed together when used. For more details, see - `Ref docs ` - explanation_parameters (aiplatform.explain.ExplanationParameters): - Optional. Parameters to configure explaining for Model's predictions. - For more details, see `Ref docs ` - metadata (Sequence[Tuple[str, str]]): - Optional. Strings which should be sent along with the request as - metadata. - deploy_request_timeout (float): - Optional. The timeout for the deploy request in seconds. - <<<<<<< HEAD - - ======= - autoscaling_target_cpu_utilization (int): - Optional. Target CPU Utilization to use for Autoscaling Replicas. - A default value of 60 will be used if not specified. - autoscaling_target_accelerator_duty_cycle (int): - Optional. Target Accelerator Duty Cycle. - Must also set accelerator_type and accelerator_count if specified. - A default value of 60 will be used if not specified. - >>>>>>> 15fe100f6935268a39be009213b259fbfcfdee3b - Raises: - ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. - ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - ValueError: If model does not support deployment. + Args: + api_client (endpoint_service_client.EndpointServiceClient): + Required. endpoint_service_client.EndpointServiceClient to make call. + endpoint_resource_name (str): + Required. Endpoint resource name to deploy model to. + model (aiplatform.Model): + Required. Model to be deployed. + endpoint_resource_traffic_split (proto.MapField): + Optional. Endpoint current resource traffic split. + network (str): + Optional. The full name of the Compute Engine network to which + this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". + Private services access must already be configured for the network. + If not set, network set in aiplatform.init will be used. + deployed_model_display_name (str): + Optional. The display name of the DeployedModel. If not provided + upon creation, the Model's display_name is used. + traffic_percentage (int): + Optional. Desired traffic to newly deployed model. Defaults to + 0 if there are pre-existing deployed models. Defaults to 100 if + there are no pre-existing deployed models. Negative values should + not be provided. Traffic of previously deployed models at the endpoint + will be scaled down to accommodate new deployed model's traffic. + Should not be provided if traffic_split is provided. + traffic_split (Dict[str, int]): + Optional. A map from a DeployedModel's ID to the percentage of + this Endpoint's traffic that should be forwarded to that DeployedModel. + If a DeployedModel's ID is not listed in this map, then it receives + no traffic. The traffic percentage values must add up to 100, or + map must be empty if the Endpoint is to not accept any traffic at + the moment. Key for model being deployed is "0". Should not be + provided if traffic_percentage is provided. + machine_type (str): + Optional. The type of machine. Not specifying machine type will + result in model to be deployed with automatic resources. + min_replica_count (int): + Optional. The minimum number of machine replicas this deployed + model will be always deployed on. If traffic against it increases, + it may dynamically be deployed onto more replicas, and as traffic + decreases, some of these extra replicas may be freed. + max_replica_count (int): + Optional. The maximum number of replicas this deployed model may + be deployed on when the traffic against it increases. If requested + value is too large, the deployment will error, but if deployment + succeeds then the ability to scale the model to that many replicas + is guaranteed (barring service outages). If traffic against the + deployed model increases beyond what its replicas at maximum may + handle, a portion of the traffic will be dropped. If this value + is not provided, the larger value of min_replica_count or 1 will + be used. If value provided is smaller than min_replica_count, it + will automatically be increased to be min_replica_count. + accelerator_type (str): + Optional. Hardware accelerator type. Must also set accelerator_count if used. + One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100, + NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4 + accelerator_count (int): + Optional. The number of accelerators to attach to a worker replica. + service_account (str): + The service account that the DeployedModel's container runs as. Specify the + email address of the service account. If this service account is not + specified, the container runs as a service account that doesn't have access + to the resource project. + Users deploying the Model must have the `iam.serviceAccounts.actAs` + permission on this service account. + explanation_metadata (aiplatform.explain.ExplanationMetadata): + Optional. Metadata describing the Model's input and output for explanation. + Both `explanation_metadata` and `explanation_parameters` must be + passed together when used. For more details, see + `Ref docs ` + explanation_parameters (aiplatform.explain.ExplanationParameters): + Optional. Parameters to configure explaining for Model's predictions. + For more details, see `Ref docs ` + metadata (Sequence[Tuple[str, str]]): + Optional. Strings which should be sent along with the request as + metadata. + deploy_request_timeout (float): + Optional. The timeout for the deploy request in seconds. + autoscaling_target_cpu_utilization (int): + Optional. Target CPU Utilization to use for Autoscaling Replicas. + A default value of 60 will be used if not specified. + autoscaling_target_accelerator_duty_cycle (int): + Optional. Target Accelerator Duty Cycle. + Must also set accelerator_type and accelerator_count if specified. + A default value of 60 will be used if not specified. + + Raises: + ValueError: If there is not current traffic split and traffic percentage + is not 0 or 100. + ValueError: If only `explanation_metadata` or `explanation_parameters` + is specified. + ValueError: If model does not support deployment. """ max_replica_count = max(min_replica_count, max_replica_count) @@ -1155,7 +1149,8 @@ def _deploy_call( explanation_spec.parameters = explanation_parameters deployed_model.explanation_spec = explanation_spec - # TODO(b/221059294): Remove check for class once PrivateEndpoint supports traffic split + # Checking if traffic percentage is valid + # TODO(b/221059294) PrivateEndpoint should support traffic split if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: @@ -2181,13 +2176,6 @@ def deploy( ValueError: If a model has already been deployed another one cannot be deployed with a PrivateEndpoint. """ - if len(self._gca_resource.deployed_models): - raise ValueError( - "A maximum of one model can be deployed to each PrivateEndpoint. " - "Please call PrivateEndpoint.undeploy() before deploying another " - "model." - ) - self._validate_deploy_args( min_replica_count=min_replica_count, max_replica_count=max_replica_count, @@ -2248,7 +2236,6 @@ def undeploy( self._sync_gca_resource_if_skipped() # TODO(b/211351292): Add traffic splitting for PrivateEndpoint - self._undeploy( deployed_model_id=deployed_model_id, traffic_split=None, @@ -2947,7 +2934,7 @@ def deploy( A default value of 60 will be used if not specified. Returns: - endpoint (Union[Endpoint, PrivateEndpoint]): + endpoint (aiplatform.Endpoint): Endpoint with the deployed model. """ @@ -2965,10 +2952,10 @@ def deploy( if isinstance(endpoint, PrivateEndpoint): if traffic_percentage or traffic_split: raise ValueError( - "Traffic splitting is not yet supported for PrivateEndpoints. " + "Traffic splitting is not yet supported for private Endpoints. " "Try calling deploy() without providing a `traffic_split` or " "`traffic_percentage`. A maximum of one model can be deployed " - "to each PrivateEndpoint." + "to each private Endpoint." ) return self._deploy( @@ -3125,7 +3112,7 @@ def _deploy( if endpoint is None: display_name = self.display_name[:118] + "_endpoint" - if not network: + if not isinstance(endpoint, PrivateEndpoint): endpoint = Endpoint.create( display_name=display_name, project=self.project, @@ -3145,7 +3132,7 @@ def _deploy( _LOGGER.log_action_start_against_resource("Deploying model to", "", endpoint) - endpoint.__class__._deploy_call( + endpoint._deploy_call( endpoint.api_client, endpoint.resource_name, self, diff --git a/tests/system/aiplatform/test_private_endpoint.py b/tests/system/aiplatform/test_private_endpoint.py index 05a613b21f..443b5b8e57 100644 --- a/tests/system/aiplatform/test_private_endpoint.py +++ b/tests/system/aiplatform/test_private_endpoint.py @@ -22,7 +22,7 @@ # permanent_custom_mnist_model _MODEL_ID = "6430031960164270080" -_PRIVATE_ENDPOINT_NETWORK = 'projects/580378083368/global/networks/private-endpoint-vpc' +_PRIVATE_ENDPOINT_NETWORK = "projects/580378083368/global/networks/private-endpoint-vpc" @pytest.mark.usefixtures("tear_down_resources") From 3db7c64c05c15b0707392e0528d83827611865be Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 13 Jun 2022 14:48:31 -0700 Subject: [PATCH 086/108] adding fixes --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 12606b5b44..818b3e915d 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -2934,7 +2934,7 @@ def deploy( A default value of 60 will be used if not specified. Returns: - endpoint (aiplatform.Endpoint): + endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. """ From 91b81b48aaacd9ff505765fba0b61348a4a61f7d Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 13 Jun 2022 21:48:56 +0000 Subject: [PATCH 087/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 12606b5b44..8383504b06 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1028,7 +1028,7 @@ def _deploy_call( Optional. Target Accelerator Duty Cycle. Must also set accelerator_type and accelerator_count if specified. A default value of 60 will be used if not specified. - + Raises: ValueError: If there is not current traffic split and traffic percentage is not 0 or 100. @@ -1149,7 +1149,7 @@ def _deploy_call( explanation_spec.parameters = explanation_parameters deployed_model.explanation_spec = explanation_spec - # Checking if traffic percentage is valid + # Checking if traffic percentage is valid # TODO(b/221059294) PrivateEndpoint should support traffic split if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models From 2a7991bdd02d970c027c3f35ce69a3cbc5b7feb9 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 13 Jun 2022 21:52:05 +0000 Subject: [PATCH 088/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 818b3e915d..7c27d58788 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1028,7 +1028,7 @@ def _deploy_call( Optional. Target Accelerator Duty Cycle. Must also set accelerator_type and accelerator_count if specified. A default value of 60 will be used if not specified. - + Raises: ValueError: If there is not current traffic split and traffic percentage is not 0 or 100. @@ -1149,7 +1149,7 @@ def _deploy_call( explanation_spec.parameters = explanation_parameters deployed_model.explanation_spec = explanation_spec - # Checking if traffic percentage is valid + # Checking if traffic percentage is valid # TODO(b/221059294) PrivateEndpoint should support traffic split if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models From e96c2cdff7a6c2a6a41ac3722549317b7d83b850 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 13 Jun 2022 15:03:38 -0700 Subject: [PATCH 089/108] adding fixes --- google/cloud/aiplatform/models.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 818b3e915d..85b13ccfe5 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -611,7 +611,6 @@ def _validate_deploy_args( Raises: ValueError: if Min or Max replica is negative. Traffic percentage > 100 or < 0. Or if traffic_split does not sum to 100. - ValueError: if either explanation_metadata or explanation_parameters but not both are specified. """ @@ -883,7 +882,7 @@ def _deploy( Raises: ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. + is not 0 or 100. """ _LOGGER.log_action_start_against_resource( f"Deploying Model {model.resource_name} to", "", self @@ -2694,8 +2693,7 @@ def upload( Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - Also if model directory does not contain a supported model file. + is specified. Also if model directory does not contain a supported model file. """ if not display_name: display_name = cls._generate_display_name() @@ -2936,6 +2934,10 @@ def deploy( Returns: endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. + + Raises: + ValueError: If `traffic_split` or `traffic_percentage` is + set for PrivateEndpoint. """ Endpoint._validate_deploy_args( @@ -3711,8 +3713,7 @@ def upload_xgboost_model_file( Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - Also if model directory does not contain a supported model file. + is specified. Also if model directory does not contain a supported model file. """ if not display_name: display_name = cls._generate_display_name("XGBoost model") @@ -3921,8 +3922,7 @@ def upload_scikit_learn_model_file( Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - Also if model directory does not contain a supported model file. + is specified. Also if model directory does not contain a supported model file. """ if not display_name: display_name = cls._generate_display_name("Scikit-Learn model") @@ -4132,8 +4132,7 @@ def upload_tensorflow_saved_model( Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - Also if model directory does not contain a supported model file. + is specified. Also if model directory does not contain a supported model file. """ if not display_name: display_name = cls._generate_display_name("Tensorflow model") From 44818ada6b0f53c5e534349857f2a6893e97991a Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 10:05:11 -0700 Subject: [PATCH 090/108] adding fixes --- google/cloud/aiplatform/models.py | 57 +++++++++++++++++++------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 85b13ccfe5..764a331538 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -273,7 +273,7 @@ def create( that are not included in either the URI or the body. Returns: - endpoint (endpoint.Endpoint): + endpoint (aiplatform.Endpoint): Created endpoint. """ @@ -450,7 +450,7 @@ def _construct_sdk_resource_from_gapic( Overrides credentials set in aiplatform.init. Returns: - Endpoint: + Endpoint (aiplatform.Endpoint): An initialized Endpoint resource. """ endpoint = cls._empty_constructor( @@ -1374,7 +1374,8 @@ def update( Optional. The timeout for the update request in seconds. Returns: - Endpoint - Updated endpoint resource. + Endpoint (aiplatform.Prediction): + Updated endpoint resource. Raises: ValueError: If `labels` is not the correct format. @@ -1455,7 +1456,8 @@ def predict( timeout (float): Optional. The timeout for this request in seconds. Returns: - prediction: Prediction with returned predictions and Model ID. + prediction (aiplatform.Prediction): + Prediction with returned predictions and Model ID. """ self.wait() @@ -1513,7 +1515,8 @@ def explain( timeout (float): Optional. The timeout for this request in seconds. Returns: - prediction: Prediction with returned predictions, explanations, and Model ID. + prediction (aiplatform.Prediction): + Prediction with returned predictions, explanations, and Model ID. """ self.wait() @@ -1569,7 +1572,8 @@ def list( credentials set in aiplatform.init. Returns: - List[models.Endpoint]: A list of Endpoint resource objects + List[models.Endpoint]: + A list of Endpoint resource objects """ return cls._list_with_local_order( @@ -1801,7 +1805,7 @@ def create( be immediately returned and synced when the Future has completed. Returns: - endpoint (PrivateEndpoint): + endpoint (aiplatform.PrivateEndpoint): Created endpoint. Raises: @@ -1863,7 +1867,7 @@ def _construct_sdk_resource_from_gapic( Overrides credentials set in aiplatform.init. Returns: - PrivateEndpoint: + endpoint (aiplatform.PrivateEndpoint): An initialized PrivateEndpoint resource. Raises: @@ -1976,7 +1980,8 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ``parameters_schema_uri``. Returns: - prediction (Prediction): Prediction object with returned predictions and Model ID. + prediction (aiplatform.Prediction): + Prediction object with returned predictions and Model ID. Raises: RuntimeError: If a model has not been deployed a request cannot be made. @@ -2019,7 +2024,8 @@ def health_check(self) -> bool: print("PrivateEndpoint is healthy!") Returns: - bool: Checks if calls can be made to this PrivateEndpoint. + bool: + Checks if calls can be made to this PrivateEndpoint. Raises: RuntimeError: If a model has not been deployed a request cannot be made. @@ -2079,7 +2085,8 @@ def list( credentials set in aiplatform.init. Returns: - List[models.PrivateEndpoint] - A list of PrivateEndpoint resource objects. + List[models.PrivateEndpoint]: + A list of PrivateEndpoint resource objects. """ return cls._list_with_local_order( @@ -2474,7 +2481,8 @@ def update( and examples of labels. Returns: - model: Updated model resource. + model (aiplatform.Model): + Updated model resource. Raises: ValueError: If `labels` is not the correct format. @@ -2689,7 +2697,8 @@ def upload( Optional. The timeout for the upload request in seconds. Returns: - model: Instantiated representation of the uploaded model resource. + model (aiplatform.Model): + Instantiated representation of the uploaded model resource. Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` @@ -3354,7 +3363,7 @@ def batch_predict( The default value is 64. Returns: - (jobs.BatchPredictionJob): + job (jobs.BatchPredictionJob): Instantiated representation of the created batch prediction job. """ @@ -3421,7 +3430,8 @@ def list( credentials set in aiplatform.init. Returns: - List[models.Model] - A list of Model resource objects + List[models.Model]: + A list of Model resource objects """ return cls._list( @@ -3709,7 +3719,8 @@ def upload_xgboost_model_file( Optional. The timeout for the upload request in seconds. Returns: - model: Instantiated representation of the uploaded model resource. + model (aiplatform.Model): + Instantiated representation of the uploaded model resource. Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` @@ -3918,7 +3929,8 @@ def upload_scikit_learn_model_file( Optional. The timeout for the upload request in seconds. Returns: - model: Instantiated representation of the uploaded model resource. + model (aiplatform.Model): + Instantiated representation of the uploaded model resource. Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` @@ -4128,7 +4140,8 @@ def upload_tensorflow_saved_model( Optional. The timeout for the upload request in seconds. Returns: - model: Instantiated representation of the uploaded model resource. + model (aiplatform.Model): + Instantiated representation of the uploaded model resource. Raises: ValueError: If only `explanation_metadata` or `explanation_parameters` @@ -4177,8 +4190,8 @@ def list_model_evaluations( my_evaluations = my_model.list_model_evaluations() Returns: - List[model_evaluation.ModelEvaluation]: List of ModelEvaluation resources - for the model. + List[model_evaluation.ModelEvaluation]: + List of ModelEvaluation resources for the model. """ self.wait() @@ -4213,8 +4226,8 @@ def get_model_evaluation( Optional. The ID of the model evaluation to retrieve. Returns: - model_evaluation.ModelEvaluation: Instantiated representation of the - ModelEvaluation resource. + model_evaluation.ModelEvaluation: + Instantiated representation of the ModelEvaluation resource. """ evaluations = self.list_model_evaluations() From 0e646dc82669b705f8728bf641c81c545e9728ed Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Tue, 14 Jun 2022 17:08:30 +0000 Subject: [PATCH 091/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- google/cloud/aiplatform/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 8a2d540c0f..e45cfe4dde 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1515,7 +1515,7 @@ def explain( timeout (float): Optional. The timeout for this request in seconds. Returns: - prediction (aiplatform.Prediction): + prediction (aiplatform.Prediction): Prediction with returned predictions, explanations, and Model ID. """ self.wait() @@ -1980,7 +1980,7 @@ def predict(self, instances: List, parameters: Optional[Dict] = None) -> Predict ``parameters_schema_uri``. Returns: - prediction (aiplatform.Prediction): + prediction (aiplatform.Prediction): Prediction object with returned predictions and Model ID. Raises: @@ -2943,9 +2943,9 @@ def deploy( Returns: endpoint (Union[Endpoint, PrivateEndpoint]): Endpoint with the deployed model. - + Raises: - ValueError: If `traffic_split` or `traffic_percentage` is + ValueError: If `traffic_split` or `traffic_percentage` is set for PrivateEndpoint. """ @@ -4226,7 +4226,7 @@ def get_model_evaluation( Optional. The ID of the model evaluation to retrieve. Returns: - model_evaluation.ModelEvaluation: + model_evaluation.ModelEvaluation: Instantiated representation of the ModelEvaluation resource. """ From 74ceab7eee2c169c3bbbdc5e41d9c2c6d23790e6 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 12:10:23 -0700 Subject: [PATCH 092/108] added global network arg to associated classes --- google/cloud/aiplatform/jobs.py | 8 ++++---- .../matching_engine_index_endpoint.py | 2 +- google/cloud/aiplatform/models.py | 5 ++--- google/cloud/aiplatform/pipeline_jobs.py | 6 +++--- google/cloud/aiplatform/training_jobs.py | 16 ++++++++-------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/google/cloud/aiplatform/jobs.py b/google/cloud/aiplatform/jobs.py index 00d6f11780..4f343890a5 100644 --- a/google/cloud/aiplatform/jobs.py +++ b/google/cloud/aiplatform/jobs.py @@ -1474,8 +1474,8 @@ def run( if service_account: self._gca_resource.job_spec.service_account = service_account - if network: - self._gca_resource.job_spec.network = network + if network or initializer.global_config.network: + self._gca_resource.job_spec.network = network or initializer.global_config.network if timeout or restart_job_on_worker_restart: timeout = duration_pb2.Duration(seconds=timeout) if timeout else None @@ -1866,8 +1866,8 @@ def run( if service_account: self._gca_resource.trial_job_spec.service_account = service_account - if network: - self._gca_resource.trial_job_spec.network = network + if network or initializer.global_config.network: + self._gca_resource.trial_job_spec.network = network or initializer.global_config.network if timeout or restart_job_on_worker_restart: duration = duration_pb2.Duration(seconds=timeout) if timeout else None diff --git a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py index da155496ae..6ae6adc52f 100644 --- a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py +++ b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py @@ -185,7 +185,7 @@ def create( gapic_index_endpoint = gca_matching_engine_index_endpoint.IndexEndpoint( display_name=display_name, description=description, - network=network, + network=network or initializer.global_config.network, ) if labels: diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 8a2d540c0f..e304945b83 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1029,11 +1029,10 @@ def _deploy_call( A default value of 60 will be used if not specified. Raises: + ValueError: If only `accelerator_type` or `accelerator_count` is specified. + ValueError: If model does not support deployment. ValueError: If there is not current traffic split and traffic percentage is not 0 or 100. - ValueError: If only `explanation_metadata` or `explanation_parameters` - is specified. - ValueError: If model does not support deployment. """ max_replica_count = max(min_replica_count, max_replica_count) diff --git a/google/cloud/aiplatform/pipeline_jobs.py b/google/cloud/aiplatform/pipeline_jobs.py index a1ea72e8fd..153a3cf7a6 100644 --- a/google/cloud/aiplatform/pipeline_jobs.py +++ b/google/cloud/aiplatform/pipeline_jobs.py @@ -274,7 +274,7 @@ def run( """ self.submit( service_account=service_account, - network=network, + network=network or initializer.global_config.network, create_request_timeout=create_request_timeout, ) @@ -314,8 +314,8 @@ def submit( if service_account: self._gca_resource.service_account = service_account - if network: - self._gca_resource.network = network + if network or initializer.global_config.network: + self._gca_resource.network = network or initializer.global_config.network # Prevents logs from being supressed on TFX pipelines if self._gca_resource.pipeline_spec.get("sdkVersion", "").startswith("tfx"): diff --git a/google/cloud/aiplatform/training_jobs.py b/google/cloud/aiplatform/training_jobs.py index 2b246e113a..4a2f8ccc89 100644 --- a/google/cloud/aiplatform/training_jobs.py +++ b/google/cloud/aiplatform/training_jobs.py @@ -1460,8 +1460,8 @@ def _prepare_training_task_inputs_and_output_dir( if service_account: training_task_inputs["service_account"] = service_account - if network: - training_task_inputs["network"] = network + if network or initializer.global_config.network: + training_task_inputs["network"] = network or initializer.global_config.network if tensorboard: training_task_inputs["tensorboard"] = tensorboard if enable_web_access: @@ -2992,7 +2992,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, bigquery_destination=bigquery_destination, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, @@ -3245,7 +3245,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, @@ -3821,7 +3821,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, bigquery_destination=bigquery_destination, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, @@ -4063,7 +4063,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, @@ -5983,7 +5983,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, test_fraction_split=test_fraction_split, @@ -6212,7 +6212,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network, + network=network or initializer.global_config.network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, From b7e216838adc34fa331fe54e27137eea2c44d430 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 12:13:32 -0700 Subject: [PATCH 093/108] style formatting --- google/cloud/aiplatform/jobs.py | 8 ++++++-- google/cloud/aiplatform/training_jobs.py | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/google/cloud/aiplatform/jobs.py b/google/cloud/aiplatform/jobs.py index 4f343890a5..27e650ab27 100644 --- a/google/cloud/aiplatform/jobs.py +++ b/google/cloud/aiplatform/jobs.py @@ -1475,7 +1475,9 @@ def run( self._gca_resource.job_spec.service_account = service_account if network or initializer.global_config.network: - self._gca_resource.job_spec.network = network or initializer.global_config.network + self._gca_resource.job_spec.network = ( + network or initializer.global_config.network + ) if timeout or restart_job_on_worker_restart: timeout = duration_pb2.Duration(seconds=timeout) if timeout else None @@ -1867,7 +1869,9 @@ def run( self._gca_resource.trial_job_spec.service_account = service_account if network or initializer.global_config.network: - self._gca_resource.trial_job_spec.network = network or initializer.global_config.network + self._gca_resource.trial_job_spec.network = ( + network or initializer.global_config.network + ) if timeout or restart_job_on_worker_restart: duration = duration_pb2.Duration(seconds=timeout) if timeout else None diff --git a/google/cloud/aiplatform/training_jobs.py b/google/cloud/aiplatform/training_jobs.py index 4a2f8ccc89..e6efd24952 100644 --- a/google/cloud/aiplatform/training_jobs.py +++ b/google/cloud/aiplatform/training_jobs.py @@ -1461,7 +1461,9 @@ def _prepare_training_task_inputs_and_output_dir( if service_account: training_task_inputs["service_account"] = service_account if network or initializer.global_config.network: - training_task_inputs["network"] = network or initializer.global_config.network + training_task_inputs["network"] = ( + network or initializer.global_config.network + ) if tensorboard: training_task_inputs["tensorboard"] = tensorboard if enable_web_access: From 8de7d06f114bf224792dd5267bde7ff27b737847 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 15:27:13 -0700 Subject: [PATCH 094/108] docstring fixes --- google/cloud/aiplatform/models.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 509a5036ce..c9778300d8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -879,10 +879,6 @@ def _deploy( Target Accelerator Duty Cycle. Must also set accelerator_type and accelerator_count if specified. A default value of 60 will be used if not specified. - - Raises: - ValueError: If there is not current traffic split and traffic percentage - is not 0 or 100. """ _LOGGER.log_action_start_against_resource( f"Deploying Model {model.resource_name} to", "", self @@ -1682,6 +1678,7 @@ def __init__( credentials set in aiplatform.init. Raises: + ValueError: If the Endpoint being retrieved is not a PrivateEndpoint. ImportError: If there is an issue importing the `urllib3` package. """ try: @@ -2176,10 +2173,6 @@ def deploy( Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will be immediately returned and synced when the Future has completed. - - Raises: - ValueError: If a model has already been deployed another one cannot be - deployed with a PrivateEndpoint. """ self._validate_deploy_args( min_replica_count=min_replica_count, From 6a5fabe141621609d11585b97c3fa348ea37822b Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 16:07:18 -0700 Subject: [PATCH 095/108] adding extra info to network arg docstring --- google/cloud/aiplatform/jobs.py | 6 ++++-- .../matching_engine_index_endpoint.py | 4 ++-- google/cloud/aiplatform/models.py | 14 ++++++++----- google/cloud/aiplatform/training_jobs.py | 21 ++++++++++++------- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/google/cloud/aiplatform/jobs.py b/google/cloud/aiplatform/jobs.py index 27e650ab27..3f6d08da4f 100644 --- a/google/cloud/aiplatform/jobs.py +++ b/google/cloud/aiplatform/jobs.py @@ -1437,7 +1437,8 @@ def run( Optional. The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -1831,7 +1832,8 @@ def run( Optional. The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. timeout (int): Optional. The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): diff --git a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py index 6ae6adc52f..1c3739d57e 100644 --- a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py +++ b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py @@ -139,8 +139,8 @@ def create( to which the IndexEndpoint should be peered. Private services access must already be configured for the - network. If left unspecified, the Endpoint is not peered - with any network. + network. If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. `Format `__: projects/{project}/global/networks/{network}. Where diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index c9778300d8..3f66737471 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -368,7 +368,8 @@ def _create( Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. If set, this will be a PrivateEndpoint. Read more about PrivateEndpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): @@ -952,7 +953,8 @@ def _deploy_call( Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/123/global/networks/my_vpc". Private services access must already be configured for the network. - If not set, network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. deployed_model_display_name (str): Optional. The display name of the DeployedModel. If not provided upon creation, the Model's display_name is used. @@ -2915,9 +2917,10 @@ def deploy( Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints - [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) + [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints). sync (bool): Whether to execute this method synchronously. If False, this method will be executed in concurrent Future and any downstream object will @@ -3090,7 +3093,8 @@ def _deploy( Optional. The full name of the Compute Engine network to which this Endpoint will be peered. E.g. "projects/12345/global/networks/myVPC". Private services access must already be configured for the network. - + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. If set, a PrivateEndpoint will be created. Read more about PrivateEndpoints [in the documentation](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints) sync (bool): diff --git a/google/cloud/aiplatform/training_jobs.py b/google/cloud/aiplatform/training_jobs.py index e6efd24952..c7afe4d830 100644 --- a/google/cloud/aiplatform/training_jobs.py +++ b/google/cloud/aiplatform/training_jobs.py @@ -1415,7 +1415,8 @@ def _prepare_training_task_inputs_and_output_dir( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -2826,7 +2827,8 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -3102,7 +3104,8 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -3656,7 +3659,8 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -3927,7 +3931,8 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -5823,7 +5828,8 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -6090,7 +6096,8 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network. + If left unspecified, the job is not peered with any network or + the network set in aiplatform.init will be used. training_fraction_split (float): Optional. The fraction of the input data that is to be used to train the Model. This is ignored if Dataset is not provided. From 15cdb507bf0c2f5d32673ab55987d700413799ff Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 14 Jun 2022 16:08:45 -0700 Subject: [PATCH 096/108] adding extra info to network arg docstring --- .../matching_engine/matching_engine_index_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py index 1c3739d57e..0df7dabcd3 100644 --- a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py +++ b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py @@ -139,7 +139,7 @@ def create( to which the IndexEndpoint should be peered. Private services access must already be configured for the - network. If left unspecified, the job is not peered with any network or + network. If left unspecified, the Endpoint is not peered with any network or the network set in aiplatform.init will be used. `Format `__: From 2e6325e31117691372725adedc5264590365efcc Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 11:06:49 -0700 Subject: [PATCH 097/108] removing changes to noxfile --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 0956542a43..15385cbdb9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ ISORT_VERSION = "isort==5.10.1" LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -DEFAULT_PYTHON_VERSION = "3.8" +DEFAULT_PYTHON_VERSION = "3.7" UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] UNIT_TEST_STANDARD_DEPENDENCIES = [ From f9253d542122a8e2dfe9926e081a067ae6ed4370 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 11:09:05 -0700 Subject: [PATCH 098/108] reverting noxfile --- samples/model-builder/noxfile.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/samples/model-builder/noxfile.py b/samples/model-builder/noxfile.py index 38bb0a572b..d13780d33a 100644 --- a/samples/model-builder/noxfile.py +++ b/samples/model-builder/noxfile.py @@ -171,7 +171,11 @@ def lint(session: nox.sessions.Session) -> None: def blacken(session: nox.sessions.Session) -> None: """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) - python_files = [path for path in os.listdir(".") if path.endswith(".py")] + python_files = [ + path + for path in os.listdir(".") + if path.endswith(".py") or path.endswith("experiment_tracking") + ] session.run("black", *python_files) @@ -180,6 +184,7 @@ def blacken(session: nox.sessions.Session) -> None: # format = isort + black # + @nox.session def format(session: nox.sessions.Session) -> None: """ @@ -229,9 +234,7 @@ def _session_tests( if os.path.exists("requirements-test.txt"): if os.path.exists("constraints-test.txt"): - session.install( - "-r", "requirements-test.txt", "-c", "constraints-test.txt" - ) + session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") else: session.install("-r", "requirements-test.txt") with open("requirements-test.txt") as rtfile: @@ -244,9 +247,9 @@ def _session_tests( post_install(session) if "pytest-parallel" in packages: - concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) + concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) elif "pytest-xdist" in packages: - concurrent_args.extend(['-n', 'auto']) + concurrent_args.extend(["-n", "auto"]) session.run( "pytest", @@ -276,7 +279,7 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """ Returns the root folder of the project. """ + """Returns the root folder of the project.""" # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) for i in range(10): From 75fa4d7d68935682e868dac85742553121a89176 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 11:11:04 -0700 Subject: [PATCH 099/108] reverting noxfile --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 15385cbdb9..0956542a43 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ ISORT_VERSION = "isort==5.10.1" LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -DEFAULT_PYTHON_VERSION = "3.7" +DEFAULT_PYTHON_VERSION = "3.8" UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] UNIT_TEST_STANDARD_DEPENDENCIES = [ From ffa3a406f80b84ef3009c657eea56411291d56f8 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 15 Jun 2022 18:12:23 +0000 Subject: [PATCH 100/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- noxfile.py | 2 +- samples/model-builder/noxfile.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/noxfile.py b/noxfile.py index 15385cbdb9..0956542a43 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ ISORT_VERSION = "isort==5.10.1" LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -DEFAULT_PYTHON_VERSION = "3.7" +DEFAULT_PYTHON_VERSION = "3.8" UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] UNIT_TEST_STANDARD_DEPENDENCIES = [ diff --git a/samples/model-builder/noxfile.py b/samples/model-builder/noxfile.py index d13780d33a..38bb0a572b 100644 --- a/samples/model-builder/noxfile.py +++ b/samples/model-builder/noxfile.py @@ -171,11 +171,7 @@ def lint(session: nox.sessions.Session) -> None: def blacken(session: nox.sessions.Session) -> None: """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) - python_files = [ - path - for path in os.listdir(".") - if path.endswith(".py") or path.endswith("experiment_tracking") - ] + python_files = [path for path in os.listdir(".") if path.endswith(".py")] session.run("black", *python_files) @@ -184,7 +180,6 @@ def blacken(session: nox.sessions.Session) -> None: # format = isort + black # - @nox.session def format(session: nox.sessions.Session) -> None: """ @@ -234,7 +229,9 @@ def _session_tests( if os.path.exists("requirements-test.txt"): if os.path.exists("constraints-test.txt"): - session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) else: session.install("-r", "requirements-test.txt") with open("requirements-test.txt") as rtfile: @@ -247,9 +244,9 @@ def _session_tests( post_install(session) if "pytest-parallel" in packages: - concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) elif "pytest-xdist" in packages: - concurrent_args.extend(["-n", "auto"]) + concurrent_args.extend(['-n', 'auto']) session.run( "pytest", @@ -279,7 +276,7 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """Returns the root folder of the project.""" + """ Returns the root folder of the project. """ # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) for i in range(10): From 63cdee3ad2239f6c2a2eda257dfaaed7a43cc4c6 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Wed, 15 Jun 2022 18:13:39 +0000 Subject: [PATCH 101/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/model-builder/noxfile.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/samples/model-builder/noxfile.py b/samples/model-builder/noxfile.py index d13780d33a..38bb0a572b 100644 --- a/samples/model-builder/noxfile.py +++ b/samples/model-builder/noxfile.py @@ -171,11 +171,7 @@ def lint(session: nox.sessions.Session) -> None: def blacken(session: nox.sessions.Session) -> None: """Run black. Format code to uniform standard.""" session.install(BLACK_VERSION) - python_files = [ - path - for path in os.listdir(".") - if path.endswith(".py") or path.endswith("experiment_tracking") - ] + python_files = [path for path in os.listdir(".") if path.endswith(".py")] session.run("black", *python_files) @@ -184,7 +180,6 @@ def blacken(session: nox.sessions.Session) -> None: # format = isort + black # - @nox.session def format(session: nox.sessions.Session) -> None: """ @@ -234,7 +229,9 @@ def _session_tests( if os.path.exists("requirements-test.txt"): if os.path.exists("constraints-test.txt"): - session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") + session.install( + "-r", "requirements-test.txt", "-c", "constraints-test.txt" + ) else: session.install("-r", "requirements-test.txt") with open("requirements-test.txt") as rtfile: @@ -247,9 +244,9 @@ def _session_tests( post_install(session) if "pytest-parallel" in packages: - concurrent_args.extend(["--workers", "auto", "--tests-per-worker", "auto"]) + concurrent_args.extend(['--workers', 'auto', '--tests-per-worker', 'auto']) elif "pytest-xdist" in packages: - concurrent_args.extend(["-n", "auto"]) + concurrent_args.extend(['-n', 'auto']) session.run( "pytest", @@ -279,7 +276,7 @@ def py(session: nox.sessions.Session) -> None: def _get_repo_root() -> Optional[str]: - """Returns the root folder of the project.""" + """ Returns the root folder of the project. """ # Get root of this repository. Assume we don't have directories nested deeper than 10 items. p = Path(os.getcwd()) for i in range(10): From 75713d83846da39beaa3d12994c571bda3767bf6 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 11:35:03 -0700 Subject: [PATCH 102/108] reverting job files to not include global network --- google/cloud/aiplatform/jobs.py | 18 +++------ .../matching_engine_index_endpoint.py | 6 +-- google/cloud/aiplatform/pipeline_jobs.py | 6 +-- google/cloud/aiplatform/training_jobs.py | 39 +++++++------------ 4 files changed, 27 insertions(+), 42 deletions(-) diff --git a/google/cloud/aiplatform/jobs.py b/google/cloud/aiplatform/jobs.py index 49f4e8c5bd..2783eaaae6 100644 --- a/google/cloud/aiplatform/jobs.py +++ b/google/cloud/aiplatform/jobs.py @@ -1435,8 +1435,7 @@ def run( Optional. The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -1473,10 +1472,8 @@ def run( if service_account: self._gca_resource.job_spec.service_account = service_account - if network or initializer.global_config.network: - self._gca_resource.job_spec.network = ( - network or initializer.global_config.network - ) + if network: + self._gca_resource.job_spec.network = network if timeout or restart_job_on_worker_restart: timeout = duration_pb2.Duration(seconds=timeout) if timeout else None @@ -1830,8 +1827,7 @@ def run( Optional. The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. timeout (int): Optional. The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -1868,10 +1864,8 @@ def run( if service_account: self._gca_resource.trial_job_spec.service_account = service_account - if network or initializer.global_config.network: - self._gca_resource.trial_job_spec.network = ( - network or initializer.global_config.network - ) + if network: + self._gca_resource.trial_job_spec.network = network if timeout or restart_job_on_worker_restart: duration = duration_pb2.Duration(seconds=timeout) if timeout else None diff --git a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py index 0df7dabcd3..da155496ae 100644 --- a/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py +++ b/google/cloud/aiplatform/matching_engine/matching_engine_index_endpoint.py @@ -139,8 +139,8 @@ def create( to which the IndexEndpoint should be peered. Private services access must already be configured for the - network. If left unspecified, the Endpoint is not peered with any network or - the network set in aiplatform.init will be used. + network. If left unspecified, the Endpoint is not peered + with any network. `Format `__: projects/{project}/global/networks/{network}. Where @@ -185,7 +185,7 @@ def create( gapic_index_endpoint = gca_matching_engine_index_endpoint.IndexEndpoint( display_name=display_name, description=description, - network=network or initializer.global_config.network, + network=network, ) if labels: diff --git a/google/cloud/aiplatform/pipeline_jobs.py b/google/cloud/aiplatform/pipeline_jobs.py index 153a3cf7a6..a1ea72e8fd 100644 --- a/google/cloud/aiplatform/pipeline_jobs.py +++ b/google/cloud/aiplatform/pipeline_jobs.py @@ -274,7 +274,7 @@ def run( """ self.submit( service_account=service_account, - network=network or initializer.global_config.network, + network=network, create_request_timeout=create_request_timeout, ) @@ -314,8 +314,8 @@ def submit( if service_account: self._gca_resource.service_account = service_account - if network or initializer.global_config.network: - self._gca_resource.network = network or initializer.global_config.network + if network: + self._gca_resource.network = network # Prevents logs from being supressed on TFX pipelines if self._gca_resource.pipeline_spec.get("sdkVersion", "").startswith("tfx"): diff --git a/google/cloud/aiplatform/training_jobs.py b/google/cloud/aiplatform/training_jobs.py index a79435ea37..b2a93d952c 100644 --- a/google/cloud/aiplatform/training_jobs.py +++ b/google/cloud/aiplatform/training_jobs.py @@ -1417,8 +1417,7 @@ def _prepare_training_task_inputs_and_output_dir( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -1463,10 +1462,8 @@ def _prepare_training_task_inputs_and_output_dir( if service_account: training_task_inputs["service_account"] = service_account - if network or initializer.global_config.network: - training_task_inputs["network"] = ( - network or initializer.global_config.network - ) + if network: + training_task_inputs["network"] = network if tensorboard: training_task_inputs["tensorboard"] = tensorboard if enable_web_access: @@ -2829,8 +2826,7 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -2998,7 +2994,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, bigquery_destination=bigquery_destination, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, @@ -3106,8 +3102,7 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -3252,7 +3247,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, @@ -3661,8 +3656,7 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -3829,7 +3823,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, bigquery_destination=bigquery_destination, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, @@ -3933,8 +3927,7 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. timeout (int): The maximum job running time in seconds. The default is 7 days. restart_job_on_worker_restart (bool): @@ -4072,7 +4065,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, @@ -5830,8 +5823,7 @@ def run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. bigquery_destination (str): Provide this field if `dataset` is a BiqQuery dataset. The BigQuery project location where the training data is to @@ -5993,7 +5985,7 @@ def run( environment_variables=environment_variables, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, training_fraction_split=training_fraction_split, validation_fraction_split=validation_fraction_split, test_fraction_split=test_fraction_split, @@ -6098,8 +6090,7 @@ def _run( The full name of the Compute Engine network to which the job should be peered. For example, projects/12345/global/networks/myVPC. Private services access must already be configured for the network. - If left unspecified, the job is not peered with any network or - the network set in aiplatform.init will be used. + If left unspecified, the job is not peered with any network. training_fraction_split (float): Optional. The fraction of the input data that is to be used to train the Model. This is ignored if Dataset is not provided. @@ -6223,7 +6214,7 @@ def _run( worker_pool_specs=worker_pool_specs, base_output_dir=base_output_dir, service_account=service_account, - network=network or initializer.global_config.network, + network=network, timeout=timeout, restart_job_on_worker_restart=restart_job_on_worker_restart, enable_web_access=enable_web_access, From dbdd705019d7f399fe8bff265d0c74f5746f9c60 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 12:04:06 -0700 Subject: [PATCH 103/108] reverting initalizer to not include global network config --- google/cloud/aiplatform/initializer.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/google/cloud/aiplatform/initializer.py b/google/cloud/aiplatform/initializer.py index 14af31097d..800bf5b014 100644 --- a/google/cloud/aiplatform/initializer.py +++ b/google/cloud/aiplatform/initializer.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright 2022 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -51,7 +51,6 @@ def __init__(self): self._staging_bucket = None self._credentials = None self._encryption_spec_key_name = None - self._network = None def init( self, @@ -66,7 +65,6 @@ def init( staging_bucket: Optional[str] = None, credentials: Optional[auth_credentials.Credentials] = None, encryption_spec_key_name: Optional[str] = None, - network: Optional[str] = None, ): """Updates common initialization parameters with provided options. @@ -97,14 +95,6 @@ def init( resource is created. If set, this resource and all sub-resources will be secured by this key. - network (str): - Optional. The full name of the Compute Engine network to which jobs - and resources should be peered. E.g. "projects/12345/global/networks/myVPC". - Private services access must already be configured for the network. - - If specified, all eligible jobs and resources created will be peered - with this VPC. - Raises: ValueError: If experiment_description is provided but experiment is not. @@ -140,8 +130,6 @@ def init( self._credentials = credentials if encryption_spec_key_name: self._encryption_spec_key_name = encryption_spec_key_name - if network: - self._network = network if experiment: metadata._experiment_tracker.set_experiment( @@ -250,10 +238,6 @@ def encryption_spec_key_name(self) -> Optional[str]: return self._encryption_spec_key_name @property - def network(self) -> Optional[str]: - """Default Compute Engine network to peer to, if provided.""" - return self._network - def experiment_name(self) -> Optional[str]: """Default experiment name, if provided.""" return metadata._experiment_tracker.experiment_name From 1566b6176b290a880f98917eefee8a20c20947ce Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Wed, 15 Jun 2022 12:06:38 -0700 Subject: [PATCH 104/108] removing global network config --- google/cloud/aiplatform/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 3f66737471..235b3e23e8 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1817,7 +1817,7 @@ def create( project = project or initializer.global_config.project location = location or initializer.global_config.location - network = network or initializer.global_config.network + network = network if not network: raise ValueError( @@ -2980,7 +2980,7 @@ def deploy( metadata=metadata, encryption_spec_key_name=encryption_spec_key_name or initializer.global_config.encryption_spec_key_name, - network=network or initializer.global_config.network, + network=network, sync=sync, deploy_request_timeout=deploy_request_timeout, autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization, From fcda7bb5d44990884f14c49a303d4c6b50ff8b7e Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 16 Jun 2022 19:29:43 +0000 Subject: [PATCH 105/108] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= =?UTF-8?q?=20post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .kokoro/samples/python3.6/common.cfg | 40 +++++++++++++++++++ .kokoro/samples/python3.6/continuous.cfg | 7 ++++ .kokoro/samples/python3.6/periodic-head.cfg | 11 +++++ .kokoro/samples/python3.6/presubmit.cfg | 6 +++ samples/model-builder/noxfile.py | 2 +- samples/snippets/noxfile.py | 2 +- .../templates/install_deps.tmpl.rst | 2 +- 7 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 .kokoro/samples/python3.6/common.cfg create mode 100644 .kokoro/samples/python3.6/continuous.cfg create mode 100644 .kokoro/samples/python3.6/periodic-head.cfg create mode 100644 .kokoro/samples/python3.6/presubmit.cfg diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg new file mode 100644 index 0000000000..72bfadc9f4 --- /dev/null +++ b/.kokoro/samples/python3.6/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.6" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "ucaip-sample-tests" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-aiplatform/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-aiplatform/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.6/continuous.cfg b/.kokoro/samples/python3.6/continuous.cfg new file mode 100644 index 0000000000..7218af1499 --- /dev/null +++ b/.kokoro/samples/python3.6/continuous.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg new file mode 100644 index 0000000000..88d5235e34 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-aiplatform/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.6/presubmit.cfg b/.kokoro/samples/python3.6/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.6/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/samples/model-builder/noxfile.py b/samples/model-builder/noxfile.py index 5fcb9d7461..38bb0a572b 100644 --- a/samples/model-builder/noxfile.py +++ b/samples/model-builder/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 5fcb9d7461..38bb0a572b 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] +ALL_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst index 6f069c6c87..275d649890 100644 --- a/scripts/readme-gen/templates/install_deps.tmpl.rst +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -12,7 +12,7 @@ Install Dependencies .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup -#. Create a virtualenv. Samples are compatible with Python 3.7+. +#. Create a virtualenv. Samples are compatible with Python 3.6+. .. code-block:: bash From 0974ef531dd869a5647aae67b634a6ca91594d76 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 27 Jun 2022 09:15:49 -0700 Subject: [PATCH 106/108] added fix and test to models --- google/cloud/aiplatform/models.py | 17 +++++----- tests/unit/aiplatform/test_models.py | 50 +++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index eddd6018d6..3b4d11566d 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1147,6 +1147,7 @@ def _deploy_call( # Checking if traffic percentage is valid # TODO(b/221059294) PrivateEndpoint should support traffic split + print(traffic_split, network) if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: @@ -1817,7 +1818,6 @@ def create( project = project or initializer.global_config.project location = location or initializer.global_config.location - network = network if not network: raise ValueError( @@ -2940,8 +2940,7 @@ def deploy( Endpoint with the deployed model. Raises: - ValueError: If `traffic_split` or `traffic_percentage` is - set for PrivateEndpoint. + ValueError: If `traffic_split` is set for PrivateEndpoint. """ Endpoint._validate_deploy_args( @@ -2956,12 +2955,11 @@ def deploy( ) if isinstance(endpoint, PrivateEndpoint): - if traffic_percentage or traffic_split: + if traffic_split: raise ValueError( - "Traffic splitting is not yet supported for private Endpoints. " - "Try calling deploy() without providing a `traffic_split` or " - "`traffic_percentage`. A maximum of one model can be deployed " - "to each private Endpoint." + "Traffic splitting is not yet supported for PrivateEndpoint. " + "Try calling deploy() without providing `traffic_split`. " + "A maximum of one model can be deployed to each private Endpoint." ) return self._deploy( @@ -3119,7 +3117,7 @@ def _deploy( if endpoint is None: display_name = self.display_name[:118] + "_endpoint" - if not isinstance(endpoint, PrivateEndpoint): + if not network: endpoint = Endpoint.create( display_name=display_name, project=self.project, @@ -3144,6 +3142,7 @@ def _deploy( endpoint.resource_name, self, endpoint._gca_resource.traffic_split, + network=network, deployed_model_display_name=deployed_model_display_name, traffic_percentage=traffic_percentage, traffic_split=traffic_split, diff --git a/tests/unit/aiplatform/test_models.py b/tests/unit/aiplatform/test_models.py index 23f933128a..5998b20417 100644 --- a/tests/unit/aiplatform/test_models.py +++ b/tests/unit/aiplatform/test_models.py @@ -232,6 +232,11 @@ ), ] +_TEST_NETWORK = f"projects/{_TEST_PROJECT}/global/networks/{_TEST_ID}" +_TEST_ENDPOINT_NAME = ( + f"projects/{_TEST_PROJECT}/locations/{_TEST_LOCATION}/endpoints/{_TEST_ID}" +) + @pytest.fixture def mock_model(): @@ -280,7 +285,6 @@ def get_model_mock(): display_name=_TEST_MODEL_NAME, name=_TEST_MODEL_RESOURCE_NAME, ) - yield get_model_mock @@ -375,6 +379,18 @@ def get_model_with_unsupported_export_formats(): yield get_model_mock +@pytest.fixture +def get_model_mock_with_network(): + with mock.patch.object( + model_service_client.ModelServiceClient, "get_model" + ) as get_model_mock: + get_model_mock.return_value = gca_model.Model( + display_name=_TEST_MODEL_NAME, + name=_TEST_MODEL_RESOURCE_NAME, + ) + yield get_model_mock + + @pytest.fixture def upload_model_mock(): with mock.patch.object( @@ -1229,6 +1245,38 @@ def test_deploy_raises_with_impartial_explanation_spec(self): assert e.match(regexp=r"`explanation_parameters` should be specified or None.") + @pytest.mark.usefixtures( + "get_endpoint_mock", "get_model_mock", "create_endpoint_mock" + ) + def test_deploy_no_endpoint_with_network(self, deploy_model_mock): + test_model = models.Model(_TEST_ID) + test_model._gca_resource.supported_deployment_resources_types.append( + aiplatform.gapic.Model.DeploymentResourcesType.AUTOMATIC_RESOURCES + ) + + test_endpoint = test_model.deploy(network=_TEST_NETWORK) + # Ensure endpoint created with `network` is a PrivateEndpoint + assert isinstance(test_endpoint, models.PrivateEndpoint) + + automatic_resources = gca_machine_resources.AutomaticResources( + min_replica_count=1, + max_replica_count=1, + ) + deployed_model = gca_endpoint.DeployedModel( + automatic_resources=automatic_resources, + model=test_model.resource_name, + display_name=None, + ) + + # Ensure traffic_split is set to `None` for PrivateEndpoint + deploy_model_mock.assert_called_once_with( + endpoint=test_endpoint.resource_name, + deployed_model=deployed_model, + traffic_split=None, + metadata=(), + timeout=None, + ) + @pytest.mark.parametrize("sync", [True, False]) @pytest.mark.usefixtures("get_model_mock", "get_batch_prediction_job_mock") def test_init_aiplatform_with_encryption_key_name_and_batch_predict_gcs_source_and_dest( From 220277f797d2497e45cc82f025318964030e565d Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Mon, 27 Jun 2022 09:19:00 -0700 Subject: [PATCH 107/108] added fix and test to models --- google/cloud/aiplatform/models.py | 1 - tests/unit/aiplatform/test_models.py | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 3b4d11566d..02c01bcb67 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -1147,7 +1147,6 @@ def _deploy_call( # Checking if traffic percentage is valid # TODO(b/221059294) PrivateEndpoint should support traffic split - print(traffic_split, network) if traffic_split is None and not network: # new model traffic needs to be 100 if no pre-existing models if not endpoint_resource_traffic_split: diff --git a/tests/unit/aiplatform/test_models.py b/tests/unit/aiplatform/test_models.py index 5998b20417..20c4e6b909 100644 --- a/tests/unit/aiplatform/test_models.py +++ b/tests/unit/aiplatform/test_models.py @@ -233,9 +233,6 @@ ] _TEST_NETWORK = f"projects/{_TEST_PROJECT}/global/networks/{_TEST_ID}" -_TEST_ENDPOINT_NAME = ( - f"projects/{_TEST_PROJECT}/locations/{_TEST_LOCATION}/endpoints/{_TEST_ID}" -) @pytest.fixture @@ -379,18 +376,6 @@ def get_model_with_unsupported_export_formats(): yield get_model_mock -@pytest.fixture -def get_model_mock_with_network(): - with mock.patch.object( - model_service_client.ModelServiceClient, "get_model" - ) as get_model_mock: - get_model_mock.return_value = gca_model.Model( - display_name=_TEST_MODEL_NAME, - name=_TEST_MODEL_RESOURCE_NAME, - ) - yield get_model_mock - - @pytest.fixture def upload_model_mock(): with mock.patch.object( From ce337f6664932de1fd89c0eaa06f6d69b0002f27 Mon Sep 17 00:00:00 2001 From: nayaknishant Date: Tue, 28 Jun 2022 13:31:51 -0700 Subject: [PATCH 108/108] simplifying delete method --- google/cloud/aiplatform/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/aiplatform/models.py b/google/cloud/aiplatform/models.py index 02c01bcb67..e6e108d45f 100644 --- a/google/cloud/aiplatform/models.py +++ b/google/cloud/aiplatform/models.py @@ -2263,7 +2263,7 @@ def delete(self, force: bool = False, sync: bool = True) -> None: sync=sync, ) - super(Endpoint, self).delete(sync=sync) + super().delete(force=False, sync=sync) class Model(base.VertexAiResourceNounWithFutureManager):