# -*- coding: utf-8 -*-

# Copyright 2023 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 itertools
from typing import Dict, List, Optional, Sequence, Tuple, Union

from google.auth import credentials as auth_credentials
import proto

from google.cloud import aiplatform
from google.cloud.aiplatform import base
from google.cloud.aiplatform import compat
from google.cloud.aiplatform import initializer
from google.cloud.aiplatform import models
from google.cloud.aiplatform import utils
from google.cloud.aiplatform.utils import _explanation_utils
from google.cloud.aiplatform.utils import prediction_utils
from google.cloud.aiplatform.compat.services import (
    deployment_resource_pool_service_client_v1beta1,
    endpoint_service_client,
)
from google.cloud.aiplatform.compat.types import (
    prediction_service_v1beta1 as gca_prediction_service_compat,
    deployed_model_ref_v1beta1 as gca_deployed_model_ref_compat,
    deployment_resource_pool_v1beta1 as gca_deployment_resource_pool_compat,
    explanation_v1beta1 as gca_explanation_compat,
    endpoint_v1beta1 as gca_endpoint_compat,
    machine_resources_v1beta1 as gca_machine_resources_compat,
    model_v1 as gca_model_compat,
)
from google.protobuf import duration_pb2
from google.protobuf import json_format

_DEFAULT_MACHINE_TYPE = "n1-standard-2"

_LOGGER = base.Logger(__name__)


class Prediction(models.Prediction):
    """Prediction class envelopes returned Model predictions and the Model id.

    Attributes:
        concurrent_explanations:
            Map of explanations that were requested concurrently in addition to
            the default explanation for the Model's predictions. It has the same
            number of elements as instances to be explained. Default is None.
    """

    concurrent_explanations: Optional[
        Dict[str, Sequence[gca_explanation_compat.Explanation]]
    ] = None


class DeploymentResourcePool(base.VertexAiResourceNounWithFutureManager):
    client_class = utils.DeploymentResourcePoolClientWithOverride
    _resource_noun = "deploymentResourcePools"
    _getter_method = "get_deployment_resource_pool"
    _list_method = "list_deployment_resource_pools"
    _delete_method = "delete_deployment_resource_pool"
    _parse_resource_name_method = "parse_deployment_resource_pool_path"
    _format_resource_name_method = "deployment_resource_pool_path"

    def __init__(
        self,
        deployment_resource_pool_name: str,
        project: Optional[str] = None,
        location: Optional[str] = None,
        credentials: Optional[auth_credentials.Credentials] = None,
    ):
        """Retrieves a DeploymentResourcePool.

        Args:
            deployment_resource_pool_name (str):
                Required. The fully-qualified resource name or ID of the
                deployment resource pool. Example:
                "projects/123/locations/us-central1/deploymentResourcePools/456"
                or "456" when project and location are initialized or passed.
            project (str):
                Optional. Project containing the deployment resource pool to
                retrieve. If not set, the project given to `aiplatform.init`
                will be used.
            location (str):
                Optional. Location containing the deployment resource pool to
                retrieve. If not set, the location given to `aiplatform.init`
                will be used.
            credentials: Optional[auth_credentials.Credentials]=None,
                Custom credentials to use to retrieve the deployment resource
                pool. If not set, the credentials given to `aiplatform.init`
                will be used.
        """

        super().__init__(
            project=project,
            location=location,
            credentials=credentials,
            resource_name=deployment_resource_pool_name,
        )

        deployment_resource_pool_name = utils.full_resource_name(
            resource_name=deployment_resource_pool_name,
            resource_noun=self._resource_noun,
            parse_resource_name_method=self._parse_resource_name,
            format_resource_name_method=self._format_resource_name,
            project=project,
            location=location,
        )
        self.api_client = self.api_client.select_version(compat.V1BETA1)
        self._gca_resource = self._get_gca_resource(
            resource_name=deployment_resource_pool_name
        )

    @classmethod
    def create(
        cls,
        deployment_resource_pool_id: str,
        project: Optional[str] = None,
        location: Optional[str] = None,
        metadata: Sequence[Tuple[str, str]] = (),
        credentials: Optional[auth_credentials.Credentials] = 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,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        sync=True,
        create_request_timeout: Optional[float] = None,
        required_replica_count: Optional[int] = 0,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
    ) -> "DeploymentResourcePool":
        """Creates a new DeploymentResourcePool.

        Args:
            deployment_resource_pool_id (str):
                Required. User-specified name for the new deployment resource
                pool.
            project (str):
                Optional. Project containing the deployment resource pool to
                retrieve. If not set, the project given to `aiplatform.init`
                will be used.
            location (str):
                Optional. Location containing the deployment resource pool to
                retrieve. If not set, the location given to `aiplatform.init`
                will be used.
            metadata (Sequence[Tuple[str, str]]):
                Optional. Strings which should be sent along with the request as
                metadata.
            credentials: Optional[auth_credentials.Credentials]=None,
                Optional. Custom credentials to use to retrieve the deployment
                resource pool. If not set, the credentials given to
                `aiplatform.init` will be used.
            machine_type (str):
                Optional. Machine type to use for the deployment resource pool.
                If not set, the default machine type of `n1-standard-2` is
                used.
            min_replica_count (int):
                Optional. The minimum replica count of the new deployment
                resource pool. Each replica serves a copy of each model deployed
                on the deployment resource pool. If this value is less than
                `max_replica_count`, then autoscaling is enabled, and the actual
                number of replicas will be adjusted to bring resource usage in
                line with the autoscaling targets.
            max_replica_count (int):
                Optional. The maximum replica count of the new deployment
                resource pool.
            accelerator_type (str):
                Optional. Hardware accelerator type. Must also set accelerator_
                count if used. One of NVIDIA_TESLA_K80, NVIDIA_TESLA_P100,
                NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4, or
                NVIDIA_TESLA_A100.
            accelerator_count (int):
                Optional. The number of accelerators attached to each replica.
            autoscaling_target_cpu_utilization (int):
                Optional. Target CPU utilization value for autoscaling. A
                default value of 60 will be used if not specified.
            autoscaling_target_accelerator_duty_cycle (int):
                Optional. Target accelerator duty cycle percentage to use for
                autoscaling. Must also set accelerator_type and accelerator
                count if specified. A default value of 60 will be used if
                accelerators are requested and this is not specified.
            sync (bool):
                Optional. Whether to execute this method synchronously. If
                False, this method will be executed in a concurrent Future and
                any downstream object will be immediately returned and synced
                when the Future has completed.
            create_request_timeout (float):
                Optional. The create request timeout in seconds.
            required_replica_count (int):
                Optional. Number of required available replicas for the
                deployment to succeed. This field is only needed when partial
                model deployment/mutation is desired, with a value greater than
                or equal to 1 and fewer than or equal to min_replica_count. If
                set, the model deploy/mutate operation will succeed once
                available_replica_count reaches required_replica_count, and the
                rest of the replicas will be retried.
            multihost_gpu_node_count (int):
                Optional. The number of nodes per replica for multihost GPU
                deployments. Required for multihost GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use FlexStart VM powered by
                Dynamic Workload Scheduler to schedule the deployment workload.
                The FlexStart VM will be available for a maximum of 7 days or up
                to the max_runtime_duration specified, whichever is shorter.
                After this period, the model will be automatically undeployed.
                The value is in seconds.

        Returns:
            DeploymentResourcePool
        """

        api_client = cls._instantiate_client(location=location, credentials=credentials)

        project = project or initializer.global_config.project
        location = location or initializer.global_config.location

        return cls._create(
            api_client=api_client,
            deployment_resource_pool_id=deployment_resource_pool_id,
            project=project,
            location=location,
            metadata=metadata,
            credentials=credentials,
            machine_type=machine_type,
            min_replica_count=min_replica_count,
            max_replica_count=max_replica_count,
            accelerator_type=accelerator_type,
            accelerator_count=accelerator_count,
            autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization,
            autoscaling_target_accelerator_duty_cycle=autoscaling_target_accelerator_duty_cycle,
            sync=sync,
            create_request_timeout=create_request_timeout,
            required_replica_count=required_replica_count,
            multihost_gpu_node_count=multihost_gpu_node_count,
            max_runtime_duration=max_runtime_duration,
        )

    @classmethod
    @base.optional_sync()
    def _create(
        cls,
        api_client: deployment_resource_pool_service_client_v1beta1.DeploymentResourcePoolServiceClient,
        deployment_resource_pool_id: str,
        project: Optional[str] = None,
        location: Optional[str] = None,
        metadata: Sequence[Tuple[str, str]] = (),
        credentials: Optional[auth_credentials.Credentials] = 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,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        sync=True,
        create_request_timeout: Optional[float] = None,
        required_replica_count: Optional[int] = 0,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
    ) -> "DeploymentResourcePool":
        """Creates a new DeploymentResourcePool.

        Args:
            api_client (DeploymentResourcePoolServiceClient):
                Required. DeploymentResourcePoolServiceClient used to make the
                underlying CreateDeploymentResourcePool API call.
            deployment_resource_pool_id (str):
                Required. User-specified name for the new deployment resource
                pool.
            project (str):
                Optional. Project containing the deployment resource pool to
                retrieve. If not set, the project given to `aiplatform.init`
                will be used.
            location (str):
                Optional. Location containing the deployment resource pool to
                retrieve. If not set, the location given to `aiplatform.init`
                will be used.
            metadata (Sequence[Tuple[str, str]]):
                Optional. Strings which should be sent along with the request as
                metadata.
            credentials: Optional[auth_credentials.Credentials]=None,
                Optional. Custom credentials to use to retrieve the deployment
                resource pool. If not set, the credentials given to
                `aiplatform.init` will be used.
            machine_type (str):
                Optional. Machine type to use for the deployment resource pool.
                If not set, the default machine type of `n1-standard-2` is
                used.
            min_replica_count (int):
                Optional. The minimum replica count of the new deployment
                resource pool. Each replica serves a copy of each model deployed
                on the deployment resource pool. If this value is less than
                `max_replica_count`, then autoscaling is enabled, and the actual
                number of replicas will be adjusted to bring resource usage in
                line with the autoscaling targets.
            max_replica_count (int):
                Optional. The maximum replica count of the new deployment
                resource pool.
            accelerator_type (str):
                Optional. Hardware accelerator type. Must also set accelerator_
                count if used. One of NVIDIA_TESLA_K80, NVIDIA_TESLA_P100,
                NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4, or
                NVIDIA_TESLA_A100.
            accelerator_count (int):
                Optional. The number of accelerators attached to each replica.
            autoscaling_target_cpu_utilization (int):
                Optional. Target CPU utilization value for autoscaling. A
                default value of 60 will be used if not specified.
            autoscaling_target_accelerator_duty_cycle (int):
                Optional. Target accelerator duty cycle percentage to use for
                autoscaling. Must also set accelerator_type and accelerator
                count if specified. A default value of 60 will be used if
                accelerators are requested and this is not specified.
            sync (bool):
                Optional. Whether to execute this method synchronously. If
                False, this method will be executed in a concurrent Future and
                any downstream object will be immediately returned and synced
                when the Future has completed.
            create_request_timeout (float):
                Optional. The create request timeout in seconds.
            required_replica_count (int):
                Optional. Number of required available replicas for the
                deployment to succeed. This field is only needed when partial
                model deployment/mutation is desired, with a value greater than
                or equal to 1 and fewer than or equal to min_replica_count. If
                set, the model deploy/mutate operation will succeed once
                available_replica_count reaches required_replica_count, and the
                rest of the replicas will be retried.
            multihost_gpu_node_count (int):
                Optional. The number of nodes per replica for multihost GPU
                deployments. Required for multihost GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use FlexStart VM powered by Dynamic
                Workload Scheduler to schedule the deployment workload. The DWS
                resource will be available for a maximum of 7 days or up to the
                max_runtime_duration specified, whichever is shorter. After this
                period, the model will be automatically undeployed. The value is
                in seconds.
        Returns:
            DeploymentResourcePool
        """

        parent = initializer.global_config.common_location_path(
            project=project, location=location
        )

        dedicated_resources = gca_machine_resources_compat.DedicatedResources(
            min_replica_count=min_replica_count,
            max_replica_count=max_replica_count,
            required_replica_count=required_replica_count,
        )

        prediction_utils.add_flex_start_to_dedicated_resources(
            dedicated_resources, max_runtime_duration
        )

        machine_spec = gca_machine_resources_compat.MachineSpec(
            machine_type=machine_type,
            multihost_gpu_node_count=multihost_gpu_node_count,
        )

        if autoscaling_target_cpu_utilization:
            autoscaling_metric_spec = (
                gca_machine_resources_compat.AutoscalingMetricSpec(
                    metric_name=(
                        "aiplatform.googleapis.com/prediction/online/cpu/utilization"
                    ),
                    target=autoscaling_target_cpu_utilization,
                )
            )
            dedicated_resources.autoscaling_metric_specs.extend(
                [autoscaling_metric_spec]
            )

        if accelerator_type and accelerator_count:
            utils.validate_accelerator_type(accelerator_type)
            machine_spec.accelerator_type = accelerator_type
            machine_spec.accelerator_count = accelerator_count

            if autoscaling_target_accelerator_duty_cycle:
                autoscaling_metric_spec = gca_machine_resources_compat.AutoscalingMetricSpec(
                    metric_name="aiplatform.googleapis.com/prediction/online/accelerator/duty_cycle",
                    target=autoscaling_target_accelerator_duty_cycle,
                )
                dedicated_resources.autoscaling_metric_specs.extend(
                    [autoscaling_metric_spec]
                )

        if multihost_gpu_node_count:
            machine_spec.multihost_gpu_node_count = multihost_gpu_node_count

        dedicated_resources.machine_spec = machine_spec

        gapic_drp = gca_deployment_resource_pool_compat.DeploymentResourcePool(
            dedicated_resources=dedicated_resources
        )

        operation_future = api_client.create_deployment_resource_pool(
            parent=parent,
            deployment_resource_pool=gapic_drp,
            deployment_resource_pool_id=deployment_resource_pool_id,
            metadata=metadata,
            timeout=create_request_timeout,
        )

        _LOGGER.log_create_with_lro(cls, operation_future)

        created_drp = operation_future.result()

        _LOGGER.log_create_complete(cls, created_drp, "deployment resource pool")

        return cls._construct_sdk_resource_from_gapic(
            gapic_resource=created_drp,
            project=project,
            location=location,
            credentials=credentials,
        )

    def query_deployed_models(
        self,
        project: Optional[str] = None,
        location: Optional[str] = None,
        credentials: Optional[auth_credentials.Credentials] = None,
    ) -> List[gca_deployed_model_ref_compat.DeployedModelRef]:
        """Lists the deployed models using this resource pool.

        Args:
            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 of DeployedModelRef objects containing the endpoint ID and
            deployed model ID of the deployed models using this resource pool.
        """
        project = project or initializer.global_config.project
        location = location or initializer.global_config.location
        api_client = DeploymentResourcePool._instantiate_client(
            location=location, credentials=credentials
        )
        response = api_client.query_deployed_models(
            deployment_resource_pool=self.resource_name
        )
        return list(
            itertools.chain(page.deployed_model_refs for page in response.pages)
        )

    @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.DeploymentResourcePool"]:
        """Lists the deployment resource pools.

        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 of deployment resource pools.
        """
        return cls._list(
            filter=filter,
            order_by=order_by,
            project=project,
            location=location,
            credentials=credentials,
        )


class RolloutOptions(object):
    """RolloutOptions contains configurations for rolling deployments.

    Attributes:
        previous_deployed_model:
            The ID of the previous deployed model.
        max_surge_percentage:
            Maximum additional replicas to create during the deployment,
            specified as a percentage of the current replica count.
        max_surge_replicas:
            Maximum number of additional replicas to create during the
            deployment.
        max_unavailable_percentage:
            Maximum amount of replicas that can be unavailable during the
            deployment, specified as a percentage of the current replica count.
        max_unavailable_replicas:
            Maximum number of replicas that can be unavailable during the
            deployment.
    """

    def __init__(
        self,
        previous_deployed_model: int,
        max_surge_percentage: Optional[int] = None,
        max_surge_replicas: Optional[int] = None,
        max_unavailable_percentage: Optional[int] = None,
        max_unavailable_replicas: Optional[int] = None,
    ):
        self.previous_deployed_model = previous_deployed_model
        self.max_surge_percentage = max_surge_percentage
        self.max_surge_replicas = max_surge_replicas
        self.max_unavailable_percentage = max_unavailable_percentage
        self.max_unavailable_replicas = max_unavailable_replicas

    @classmethod
    def from_gapic(cls, opts: gca_endpoint_compat.RolloutOptions) -> "RolloutOptions":
        return cls(
            previous_deployed_model=int(opts.previous_deployed_model),
            max_surge_percentage=opts.max_surge_percentage,
            max_surge_replicas=opts.max_surge_replicas,
            max_unavailable_percentage=opts.max_unavailable_percentage,
            max_unavailable_replicas=opts.max_unavailable_replicas,
        )

    def to_gapic(self) -> gca_endpoint_compat.RolloutOptions:
        """Converts RolloutOptions class to gapic RolloutOptions proto."""
        result = gca_endpoint_compat.RolloutOptions(
            previous_deployed_model=str(self.previous_deployed_model),
        )
        if self.max_surge_percentage:
            if self.max_surge_replicas:
                raise ValueError(
                    "max_surge_percentage and max_surge_replicas cannot both be" " set."
                )
            result.max_surge_percentage = self.max_surge_percentage
        elif self.max_surge_replicas:
            result.max_surge_replicas = self.max_surge_replicas
        else:
            result.max_surge_replicas = 0
        if self.max_unavailable_percentage:
            if self.max_unavailable_replicas:
                raise ValueError(
                    "max_unavailable_percentage and max_unavailable_replicas"
                    " cannot both be set."
                )
            result.max_unavailable_percentage = self.max_unavailable_percentage
        elif self.max_unavailable_replicas:
            result.max_unavailable_replicas = self.max_unavailable_replicas
        else:
            result.max_unavailable_replicas = 0

        return result


class Endpoint(aiplatform.Endpoint):
    @staticmethod
    def _validate_deploy_args(
        min_replica_count: Optional[int],
        max_replica_count: Optional[int],
        accelerator_type: Optional[str],
        deployed_model_display_name: Optional[str],
        traffic_split: Optional[Dict[str, int]],
        traffic_percentage: Optional[int],
        deployment_resource_pool: Optional[DeploymentResourcePool],
        required_replica_count: Optional[int],
        initial_replica_count: Optional[int],
        min_scaleup_period: Optional[int],
        idle_scaledown_period: Optional[int],
    ):
        """Helper method to validate deploy arguments.

        Args:
            min_replica_count (int): Required. 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): Required. 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): Required. Hardware accelerator type. One of
              ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100,
              NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4
            deployed_model_display_name (str): Required. The display name of the
              DeployedModel. If not provided upon creation, the Model's display_name
              is used.
            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.
            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.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            required_replica_count (int):
                Optional. Number of required available replicas for the
                deployment to succeed. This field is only needed when partial
                model deployment/mutation is desired, with a value greater than
                or equal to 1 and fewer than or equal to min_replica_count. If
                set, the model deploy/mutate operation will succeed once
                available_replica_count reaches required_replica_count, and the
                rest of the replicas will be retried.
            initial_replica_count (int):
                Optional. The number of replicas to deploy the model with.
                Only applicable for scale-to-zero deployments where
                min_replica_count is 0.
            min_scaleup_period (int):
                Optional. For scale-to-zero deployments, minimum duration that
                a deployment will be scaled up before traffic is
                evaluated for potential scale-down.
            idle_scaledown_period (int):
                Optional. For scale-to-zero deployments, duration of no traffic
                before scaling to zero.

        Raises:
            ValueError: if min/max replica or accelerator type are specified
              along with a deployment resource pool, or if min/max replicas are
              omitted or negative without specifying a deployment resource pool,
              or if traffic percentage > 100 or < 0, or if traffic_split does
              not sum to 100, or if the provided accelerator type is invalid.
        """
        if not deployment_resource_pool:
            if min_replica_count is None or max_replica_count is None:
                raise ValueError(
                    "Minimum and maximum replica counts must not be specified"
                    "if not using a shared resource pool."
                )
            # Validate STZ parameters
            if min_replica_count != 0:
                if initial_replica_count:
                    raise ValueError(
                        "Initial replica count cannot be set for non-STZ models."
                    )
                if min_scaleup_period:
                    raise ValueError(
                        "Min scaleup period cannot be set for non-STZ models."
                    )
                if idle_scaledown_period:
                    raise ValueError(
                        "Idle scaledown period cannot be set for non-STZ models."
                    )
            if min_replica_count == 0 and initial_replica_count:
                if initial_replica_count < 0:
                    raise ValueError("Initial replica count must be at least 0.")
                if initial_replica_count > max_replica_count:
                    raise ValueError(
                        "Initial replica count cannot be "
                        "greater than max replica count."
                    )
            if min_replica_count == 0 and min_scaleup_period:
                if min_scaleup_period < 300:
                    raise ValueError(
                        "Min scaleup period cannot be less than 300 (5 minutes)."
                    )
                if min_scaleup_period > 28800:
                    raise ValueError(
                        "Min scaleup period cannot be greater than 28800 (8 hours)."
                    )
            if min_replica_count == 0 and idle_scaledown_period:
                if idle_scaledown_period < 300:
                    raise ValueError(
                        "Idle scaledown period cannot be less than 300 (5 minutes)."
                    )
                if idle_scaledown_period > 28800:
                    raise ValueError(
                        "Idle scaledown period cannot be greater than 28800 (8 hours)."
                    )
            return aiplatform.Endpoint._validate_deploy_args(
                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,
                deployment_resource_pool=deployment_resource_pool,
                required_replica_count=required_replica_count,
            )

        if (
            min_replica_count
            and min_replica_count != 1
            or max_replica_count
            and max_replica_count != 1
            or required_replica_count
            and required_replica_count != 0
            or initial_replica_count
            or min_scaleup_period
            or idle_scaledown_period
        ):
            _LOGGER.warning(
                "Ignoring explicitly specified replica counts, "
                "since deployment_resource_pool was also given."
            )
        if accelerator_type:
            raise ValueError(
                "Conflicting deployment parameters were given."
                "deployment_resource_pool may not be specified at the same time"
                "as accelerator_type."
            )
        if initial_replica_count or min_scaleup_period or idle_scaledown_period:
            raise ValueError(
                "Scale-to-zero parameters may not be specified at the same time"
                "as deployment_resource_pool."
            )
        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:
            if sum(traffic_split.values()) != 100:
                raise ValueError(
                    "Sum of all traffic within traffic split needs to be 100."
                )
        if deployed_model_display_name is not None:
            utils.validate_display_name(deployed_model_display_name)

    def deploy(
        self,
        model: "Model",
        deployed_model_display_name: Optional[str] = None,
        traffic_percentage: int = 0,
        traffic_split: Optional[Dict[str, int]] = None,
        machine_type: Optional[str] = None,
        min_replica_count: Optional[int] = 1,
        max_replica_count: Optional[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: Sequence[Tuple[str, str]] = (),
        sync=True,
        deploy_request_timeout: Optional[float] = None,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        autoscaling_target_request_count_per_minute: Optional[int] = None,
        autoscaling_target_pubsub_num_undelivered_messages: Optional[int] = None,
        autoscaling_pubsub_subscription_labels: Optional[Dict[str, str]] = None,
        deployment_resource_pool: Optional[DeploymentResourcePool] = None,
        disable_container_logging: bool = False,
        fast_tryout_enabled: bool = False,
        system_labels: Optional[Dict[str, str]] = None,
        required_replica_count: Optional[int] = 0,
        rollout_options: Optional[RolloutOptions] = None,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
        initial_replica_count: Optional[int] = None,
        min_scaleup_period: Optional[int] = None,
        idle_scaledown_period: Optional[int] = None,
    ) -> 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.
              `explanation_metadata` is optional while `explanation_parameters` must
              be specified when used. For more details, see `Ref docs
              <http://tinyurl.com/1igh60kt>`
            explanation_parameters (aiplatform.explain.ExplanationParameters):
              Optional. Parameters to configure explaining for Model's predictions.
              For more details, see `Ref docs <http://tinyurl.com/1an4zake>`
            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.
            autoscaling_target_request_count_per_minute (int): Target request
              count per minute per instance.
            autoscaling_target_pubsub_num_undelivered_messages (int): Target
              number of pubsub undelivered messages per instance.
            autoscaling_pubsub_subscription_labels (Dict[str, str]): Optional.
              Monitored resource labels as key value pairs for metric filtering
              for pubsub_num_undelivered_messages.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            disable_container_logging (bool):
              If True, container logs from the deployed model will not be
              written to Cloud Logging. Defaults to False.
            fast_tryout_enabled (bool): Optional.
              If True, model will be deployed using faster deployment path.
              Useful for quick experiments. Not for production workloads. Only
              available for most popular models and machine types. Defaults to False.
            system_labels (Dict[str, str]):
              Optional. System labels to apply to Model Garden deployments.
              System labels are managed by Google for internal use only.
            required_replica_count (int):
                Optional. Number of required available replicas for the
                deployment to succeed. This field is only needed when partial
                model deployment/mutation is desired, with a value greater than
                or equal to 1 and fewer than or equal to min_replica_count. If
                set, the model deploy/mutate operation will succeed once
                available_replica_count reaches required_replica_count, and the
                rest of the replicas will be retried.
            rollout_options (RolloutOptions):
                Optional. Options to configure a rolling deployment.
            multihost_gpu_node_count (int): Optional. The number of nodes per
              replica for multihost GPU deployments. Required for multihost GPU
              deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use DWS resource
                to schedule the deployment workload. The DWS resource will be available
                for a maximum of 7 days or up to the max_runtime_duration specified,
                whichever is shorter. After this period, the model will be
                automatically undeployed. The value is in seconds.
            initial_replica_count (int):
                Optional. The number of replicas to deploy the model with.
                Only applicable for scale-to-zero deployments where
                min_replica_count is 0.
            min_scaleup_period (int):
                Optional. For scale-to-zero deployments, minimum duration that
                a deployment will be scaled up before traffic is
                evaluated for potential scale-down.
            idle_scaledown_period (int):
                Optional. For scale-to-zero deployments, duration of no traffic
                before scaling to zero.
        """
        self._sync_gca_resource_if_skipped()

        self._validate_deploy_args(
            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,
            deployment_resource_pool=deployment_resource_pool,
            required_replica_count=required_replica_count,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period,
        )

        explanation_spec = _explanation_utils.create_and_validate_explanation_spec(
            explanation_metadata=explanation_metadata,
            explanation_parameters=explanation_parameters,
        )

        self._deploy(
            model=model,
            deployed_model_display_name=deployed_model_display_name,
            traffic_percentage=traffic_percentage,
            traffic_split=traffic_split,
            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_spec=explanation_spec,
            metadata=metadata,
            sync=sync,
            deploy_request_timeout=deploy_request_timeout,
            autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization,
            autoscaling_target_accelerator_duty_cycle=autoscaling_target_accelerator_duty_cycle,
            autoscaling_target_request_count_per_minute=autoscaling_target_request_count_per_minute,
            autoscaling_target_pubsub_num_undelivered_messages=autoscaling_target_pubsub_num_undelivered_messages,
            autoscaling_pubsub_subscription_labels=autoscaling_pubsub_subscription_labels,
            deployment_resource_pool=deployment_resource_pool,
            disable_container_logging=disable_container_logging,
            fast_tryout_enabled=fast_tryout_enabled,
            system_labels=system_labels,
            required_replica_count=required_replica_count,
            rollout_options=rollout_options,
            multihost_gpu_node_count=multihost_gpu_node_count,
            max_runtime_duration=max_runtime_duration,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period,
        )

    @base.optional_sync()
    def _deploy(
        self,
        model: "Model",
        deployed_model_display_name: Optional[str] = None,
        traffic_percentage: Optional[int] = 0,
        traffic_split: Optional[Dict[str, int]] = None,
        machine_type: Optional[str] = None,
        min_replica_count: Optional[int] = 1,
        max_replica_count: Optional[int] = 1,
        accelerator_type: Optional[str] = None,
        accelerator_count: Optional[int] = None,
        service_account: Optional[str] = None,
        explanation_spec: Optional[aiplatform.explain.ExplanationSpec] = None,
        metadata: Sequence[Tuple[str, str]] = (),
        sync=True,
        deploy_request_timeout: Optional[float] = None,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        autoscaling_target_request_count_per_minute: Optional[int] = None,
        autoscaling_target_pubsub_num_undelivered_messages: Optional[int] = None,
        autoscaling_pubsub_subscription_labels: Optional[Dict[str, str]] = None,
        deployment_resource_pool: Optional[DeploymentResourcePool] = None,
        disable_container_logging: bool = False,
        fast_tryout_enabled: bool = False,
        system_labels: Optional[Dict[str, str]] = None,
        required_replica_count: Optional[int] = 0,
        rollout_options: Optional[RolloutOptions] = None,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
        initial_replica_count: Optional[int] = None,
        min_scaleup_period: Optional[int] = None,
        idle_scaledown_period: Optional[int] = None,
    ) -> 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_spec (aiplatform.explain.ExplanationSpec): Optional.
              Specification of Model explanation.
            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.
            autoscaling_target_request_count_per_minute (int): Target request
              count per minute per instance.
            autoscaling_target_pubsub_num_undelivered_messages (int): Target
              number of pubsub undelivered messages per instance.
            autoscaling_pubsub_subscription_labels (Dict[str, str]): Optional.
              Monitored resource labels as key value pairs for metric filtering
              for pubsub_num_undelivered_messages.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            disable_container_logging (bool):
              If True, container logs from the deployed model will not be
              written to Cloud Logging. Defaults to False.
            fast_tryout_enabled (bool): Optional.
              If True, model will be deployed using faster deployment path.
              Useful for quick experiments. Not for production workloads. Only
              available for most popular models and machine types. Defaults to False.
            system_labels (Dict[str, str]):
              Optional. System labels to apply to Model Garden deployments.
              System labels are managed by Google for internal use only.
            required_replica_count (int):
              Optional. Number of required available replicas for the
              deployment to succeed. This field is only needed when partial
              model deployment/mutation is desired, with a value greater than
              or equal to 1 and fewer than or equal to min_replica_count. If
              set, the model deploy/mutate operation will succeed once
              available_replica_count reaches required_replica_count, and the
              rest of the replicas will be retried.
            rollout_options (RolloutOptions): Optional.
              Options to configure a rolling deployment.
            multihost_gpu_node_count (int): Optional. The number of nodes per
              replica for multihost GPU deployments. Required for multihost
              GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use DWS resource
                to schedule the deployment workload. The DWS resource will be available
                for a maximum of 7 days or up to the max_runtime_duration specified,
                whichever is shorter. After this period, the model will be
                automatically undeployed. The value is in seconds.
            initial_replica_count (int):
                Optional. The number of replicas to deploy the model with.
                Only applicable for scale-to-zero deployments where
                min_replica_count is 0.
            min_scaleup_period (int):
                Optional. For scale-to-zero deployments, minimum duration that
                a deployment will be scaled up before traffic is
                evaluated for potential scale-down.
            idle_scaledown_period (int):
                Optional. For scale-to-zero deployments, duration of no traffic
                before scaling to zero.
        """
        _LOGGER.log_action_start_against_resource(
            f"Deploying Model {model.resource_name} to", "", self
        )

        self._deploy_call(
            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,
            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_spec=explanation_spec,
            metadata=metadata,
            deploy_request_timeout=deploy_request_timeout,
            autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization,
            autoscaling_target_accelerator_duty_cycle=autoscaling_target_accelerator_duty_cycle,
            autoscaling_target_request_count_per_minute=autoscaling_target_request_count_per_minute,
            autoscaling_target_pubsub_num_undelivered_messages=autoscaling_target_pubsub_num_undelivered_messages,
            autoscaling_pubsub_subscription_labels=autoscaling_pubsub_subscription_labels,
            deployment_resource_pool=deployment_resource_pool,
            disable_container_logging=disable_container_logging,
            fast_tryout_enabled=fast_tryout_enabled,
            system_labels=system_labels,
            required_replica_count=required_replica_count,
            rollout_options=rollout_options,
            multihost_gpu_node_count=multihost_gpu_node_count,
            max_runtime_duration=max_runtime_duration,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period,
        )

        _LOGGER.log_action_completed_against_resource("model", "deployed", self)

        self._sync_gca_resource()

    @classmethod
    def _deploy_call(
        cls,
        api_client: endpoint_service_client.EndpointServiceClient,
        endpoint_resource_name: str,
        model: "Model",
        endpoint_resource_traffic_split: Optional[proto.MapField] = None,
        network: Optional[str] = None,
        deployed_model_display_name: Optional[str] = None,
        traffic_percentage: Optional[int] = 0,
        traffic_split: Optional[Dict[str, int]] = 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_spec: Optional[aiplatform.explain.ExplanationSpec] = None,
        metadata: Sequence[Tuple[str, str]] = (),
        deploy_request_timeout: Optional[float] = None,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        autoscaling_target_request_count_per_minute: Optional[int] = None,
        autoscaling_target_pubsub_num_undelivered_messages: Optional[int] = None,
        autoscaling_pubsub_subscription_labels: Optional[Dict[str, str]] = None,
        deployment_resource_pool: Optional[DeploymentResourcePool] = None,
        disable_container_logging: bool = False,
        fast_tryout_enabled: bool = False,
        system_labels: Optional[Dict[str, str]] = None,
        required_replica_count: Optional[int] = 0,
        rollout_options: Optional[RolloutOptions] = None,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
        initial_replica_count: Optional[int] = None,
        min_scaleup_period: Optional[int] = None,
        idle_scaledown_period: Optional[int] = None,
    ) -> None:
        """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.
            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_spec (aiplatform.explain.ExplanationSpec): Optional.
              Specification of Model explanation.
            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.
            autoscaling_target_request_count_per_minute (int): Optional. Target
              request count per minute per instance.
            autoscaling_target_pubsub_num_undelivered_messages (int): Optional.
              Target number of pubsub undelivered messages per instance.
            autoscaling_pubsub_subscription_labels (Dict[str, str]): Optional.
              Monitored resource labels as key value pairs for metric filtering
              for pubsub_num_undelivered_messages.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            disable_container_logging (bool):
              If True, container logs from the deployed model will not be
              written to Cloud Logging. Defaults to False.
            fast_tryout_enabled (bool): Optional.
              If True, model will be deployed using faster deployment path.
              Useful for quick experiments. Not for production workloads. Only
              available for most popular models and machine types. Defaults to False.
            system_labels (Dict[str, str]):
              Optional. System labels to apply to Model Garden deployments.
              System labels are managed by Google for internal use only.
            required_replica_count (int):
              Optional. Number of required available replicas for the
              deployment to succeed. This field is only needed when partial
              model deployment/mutation is desired, with a value greater than
              or equal to 1 and fewer than or equal to min_replica_count. If
              set, the model deploy/mutate operation will succeed once
              available_replica_count reaches required_replica_count, and the
              rest of the replicas will be retried.
            rollout_options (RolloutOptions): Optional. Options to configure a
              rolling deployment.
            multihost_gpu_node_count (int):
              Optional. The number of nodes per replica for multihost GPU
              deployments. Required for multihost GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use DWS resource
                to schedule the deployment workload. The DWS resource will be available
                for a maximum of 7 days or up to the max_runtime_duration specified,
                whichever is shorter. After this period, the model will be
                automatically undeployed. The value is in seconds.
            initial_replica_count (int): Optional. The number of replicas to
              deploy the model with. Only applicable for scale-to-zero
              deployments where min_replica_count is 0.
            min_scaleup_period (int): Optional. For scale-to-zero deployments,
              minimum duration that a deployment will be scaled up before traffic
              is evaluated for potential scale-down.
            idle_scaledown_period (int): Optional. For scale-to-zero deployments,
              duration of no traffic before scaling to zero.

        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 `deployment_resource_pool` and a custom machine spec
                are both present.
            ValueError: If both `explanation_spec` and `deployment_resource_pool`
                are present.
        """
        if not deployment_resource_pool:
            max_replica_count = max(min_replica_count, max_replica_count)

            if bool(accelerator_type) != bool(accelerator_count):
                raise ValueError(
                    "Both `accelerator_type` and `accelerator_count` should be specified or None."
                )

            if autoscaling_target_accelerator_duty_cycle is not None and (
                not accelerator_type or not accelerator_count
            ):
                raise ValueError(
                    "Both `accelerator_type` and `accelerator_count` should be set "
                    "when specifying autoscaling_target_accelerator_duty_cycle`"
                )

            deployed_model = gca_endpoint_compat.DeployedModel(
                model=model.versioned_resource_name,
                display_name=deployed_model_display_name,
                service_account=service_account,
                enable_container_logging=not disable_container_logging,
            )

            if system_labels:
                deployed_model.system_labels = system_labels

            supports_automatic_resources = (
                gca_model_compat.Model.DeploymentResourcesType.AUTOMATIC_RESOURCES
                in model.supported_deployment_resources_types
            )
            supports_dedicated_resources = (
                gca_model_compat.Model.DeploymentResourcesType.DEDICATED_RESOURCES
                in model.supported_deployment_resources_types
            )
            provided_custom_machine_spec = (
                machine_type
                or accelerator_type
                or accelerator_count
                or autoscaling_target_accelerator_duty_cycle
                or autoscaling_target_request_count_per_minute
                or autoscaling_target_cpu_utilization
                or autoscaling_target_pubsub_num_undelivered_messages
                or autoscaling_pubsub_subscription_labels
            )

            # If the model supports both automatic and dedicated deployment resources,
            # decide based on the presence of machine spec customizations
            use_dedicated_resources = supports_dedicated_resources and (
                not supports_automatic_resources or provided_custom_machine_spec
            )

            if provided_custom_machine_spec and not use_dedicated_resources:
                _LOGGER.info(
                    "Model does not support dedicated deployment resources. "
                    "The machine_type, accelerator_type and accelerator_count, "
                    "autoscaling_target_accelerator_duty_cycle, "
                    "autoscaling_target_cpu_utilization, "
                    "autoscaling_target_request_count_per_minute, "
                    "autoscaling_target_pubsub_num_undelivered_messages, "
                    "autoscaling_pubsub_subscription_labels parameters "
                    "are ignored."
                )

            if use_dedicated_resources and not machine_type:
                machine_type = _DEFAULT_MACHINE_TYPE
                _LOGGER.info(f"Using default machine_type: {machine_type}")

            if use_dedicated_resources and not rollout_options:
                dedicated_resources = gca_machine_resources_compat.DedicatedResources(
                    min_replica_count=min_replica_count,
                    max_replica_count=max_replica_count,
                    required_replica_count=required_replica_count,
                )

                # If min_replica_count is 0, set Scale to Zero fields.
                if dedicated_resources.min_replica_count == 0:
                    # Set initial replica count
                    dedicated_resources.initial_replica_count = initial_replica_count
                    # Set scale to zero spec.
                    stz_spec = (
                        gca_machine_resources_compat.DedicatedResources.ScaleToZeroSpec()
                    )
                    if min_scaleup_period is not None:
                        stz_spec.min_scaleup_period = duration_pb2.Duration(
                            seconds=min_scaleup_period
                        )
                    if idle_scaledown_period is not None:
                        stz_spec.idle_scaledown_period = duration_pb2.Duration(
                            seconds=idle_scaledown_period
                        )
                    dedicated_resources.scale_to_zero_spec = stz_spec

                prediction_utils.add_flex_start_to_dedicated_resources(
                    dedicated_resources, max_runtime_duration
                )

                machine_spec = gca_machine_resources_compat.MachineSpec(
                    machine_type=machine_type,
                    multihost_gpu_node_count=multihost_gpu_node_count,
                )

                if autoscaling_target_cpu_utilization:
                    autoscaling_metric_spec = gca_machine_resources_compat.AutoscalingMetricSpec(
                        metric_name="aiplatform.googleapis.com/prediction/online/cpu/utilization",
                        target=autoscaling_target_cpu_utilization,
                    )
                    dedicated_resources.autoscaling_metric_specs.extend(
                        [autoscaling_metric_spec]
                    )

                if accelerator_type and accelerator_count:
                    utils.validate_accelerator_type(accelerator_type)
                    machine_spec.accelerator_type = accelerator_type
                    machine_spec.accelerator_count = accelerator_count

                    if autoscaling_target_accelerator_duty_cycle:
                        autoscaling_metric_spec = gca_machine_resources_compat.AutoscalingMetricSpec(
                            metric_name="aiplatform.googleapis.com/prediction/online/accelerator/duty_cycle",
                            target=autoscaling_target_accelerator_duty_cycle,
                        )
                        dedicated_resources.autoscaling_metric_specs.extend(
                            [autoscaling_metric_spec]
                        )

                if autoscaling_target_request_count_per_minute:
                    autoscaling_metric_spec = (
                        gca_machine_resources_compat.AutoscalingMetricSpec(
                            metric_name=(
                                "aiplatform.googleapis.com/prediction/online/"
                                "request_count"
                            ),
                            target=autoscaling_target_request_count_per_minute,
                        )
                    )
                    dedicated_resources.autoscaling_metric_specs.extend(
                        [autoscaling_metric_spec]
                    )

                if autoscaling_target_pubsub_num_undelivered_messages:
                    autoscaling_metric_spec = gca_machine_resources_compat.AutoscalingMetricSpec(
                        metric_name=(
                            "pubsub.googleapis.com/subscription/"
                            "num_undelivered_messages"
                        ),
                        target=autoscaling_target_pubsub_num_undelivered_messages,
                        monitored_resource_labels=autoscaling_pubsub_subscription_labels,
                    )
                    dedicated_resources.autoscaling_metric_specs.extend(
                        [autoscaling_metric_spec]
                    )

                dedicated_resources.machine_spec = machine_spec

                # Checking if flag fast_tryout_enabled is set, only in v1beta1
                deployed_model.faster_deployment_config = (
                    gca_endpoint_compat.FasterDeploymentConfig(
                        fast_tryout_enabled=fast_tryout_enabled
                    )
                )
                deployed_model.dedicated_resources = dedicated_resources
            elif rollout_options:
                deployed_model.rollout_options = rollout_options.to_gapic()
            elif supports_automatic_resources:
                deployed_model.automatic_resources = (
                    gca_machine_resources_compat.AutomaticResources(
                        min_replica_count=min_replica_count,
                        max_replica_count=max_replica_count,
                    )
                )
        else:
            deployed_model = gca_endpoint_compat.DeployedModel(
                model=model.versioned_resource_name,
                display_name=deployed_model_display_name,
                service_account=service_account,
                enable_container_logging=not disable_container_logging,
            )

            if system_labels:
                deployed_model.system_labels = system_labels

            supports_shared_resources = (
                gca_model_compat.Model.DeploymentResourcesType.SHARED_RESOURCES
                in model.supported_deployment_resources_types
            )

            if not supports_shared_resources:
                raise ValueError(
                    "`deployment_resource_pool` may only be specified for models "
                    " which support shared resources."
                )

            provided_custom_machine_spec = (
                machine_type
                or accelerator_type
                or accelerator_count
                or autoscaling_target_accelerator_duty_cycle
                or autoscaling_target_cpu_utilization
                or autoscaling_target_request_count_per_minute
                or autoscaling_target_pubsub_num_undelivered_messages
                or autoscaling_pubsub_subscription_labels
            )

            if provided_custom_machine_spec:
                raise ValueError(
                    "Conflicting parameters in deployment request. "
                    "The machine_type, accelerator_type and accelerator_count, "
                    "autoscaling_target_accelerator_duty_cycle, "
                    "autoscaling_target_cpu_utilization, "
                    "autoscaling_target_request_count_per_minute, "
                    "autoscaling_target_pubsub_num_undelivered_messages, "
                    "autoscaling_pubsub_subscription_labels parameters "
                    "may not be set when `deployment_resource_pool` is "
                    "specified."
                )

            deployed_model.shared_resources = deployment_resource_pool.resource_name

            if explanation_spec:
                raise ValueError(
                    "Model explanation is not supported for deployments using "
                    "shared resources."
                )

        # 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:
                # default scenario
                if traffic_percentage == 0:
                    traffic_percentage = 100
                # verify user specified 100
                elif traffic_percentage < 100:
                    raise ValueError(
                        """There are currently no deployed models so the traffic
                        percentage for this deployed model needs to be 100."""
                    )
            traffic_split = cls._allocate_traffic(
                traffic_split=dict(endpoint_resource_traffic_split),
                traffic_percentage=traffic_percentage,
            )

        operation_future = api_client.select_version("v1beta1").deploy_model(
            endpoint=endpoint_resource_name,
            deployed_model=deployed_model,
            traffic_split=traffic_split,
            metadata=metadata,
            timeout=deploy_request_timeout,
        )

        _LOGGER.log_action_started_against_resource_with_lro(
            "Deploy", "model", cls, operation_future
        )

        operation_future.result(timeout=None)

    def explain(
        self,
        instances: List[Dict],
        parameters: Optional[Dict] = None,
        deployed_model_id: Optional[str] = None,
        timeout: Optional[float] = None,
        explanation_spec_override: Optional[Dict] = None,
        concurrent_explanation_spec_override: Optional[Dict] = None,
    ) -> Prediction:
        """Make a prediction with explanations against this Endpoint.

        Example usage:
            response = my_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``.
            deployed_model_id (str):
                Optional. If specified, this ExplainRequest will be served by the
                chosen DeployedModel, overriding this Endpoint's traffic split.
            timeout (float): Optional. The timeout for this request in seconds.
            explanation_spec_override (Dict):
                Optional. Represents overrides to the explaination
                specification used when the model was deployed.
                The Explanation Override will
                be merged with model's existing [Explanation Spec
                ][google.cloud.aiplatform.v1beta1.ExplanationSpec].
            concurrent_explanation_spec_override (Dict):
                Optional. The ``explain`` endpoint supports multiple
                explanations in parallel. To request concurrent explanation in
                addition to the configured explaination method, use this field.

        Returns:
            prediction (aiplatform.Prediction):
                Prediction with returned predictions, explanations, and Model ID.
        """
        self.wait()
        request = gca_prediction_service_compat.ExplainRequest()
        request.endpoint = self.resource_name

        if instances is not None:
            request.instances.extend(instances)
        if parameters is not None:
            request.parameters = parameters
        if deployed_model_id is not None:
            request.deployed_model_id = deployed_model_id
        if explanation_spec_override is not None:
            request.explanation_spec_override = explanation_spec_override
        if concurrent_explanation_spec_override is not None:
            request.concurrent_explanation_spec_override = (
                concurrent_explanation_spec_override
            )

        explain_response = self._prediction_client.select_version("v1beta1").explain(
            request, timeout=timeout
        )

        prediction = Prediction(
            predictions=[
                json_format.MessageToDict(item)
                for item in explain_response.predictions.pb
            ],
            deployed_model_id=explain_response.deployed_model_id,
            explanations=explain_response.explanations,
        )

        concurrent_explanation = {}
        for k, e in explain_response.concurrent_explanations.items():
            concurrent_explanation[k] = e.explanations

        prediction.concurrent_explanations = concurrent_explanation

        return prediction

    async def explain_async(
        self,
        instances: List[Dict],
        *,
        parameters: Optional[Dict] = None,
        deployed_model_id: Optional[str] = None,
        timeout: Optional[float] = None,
        explanation_spec_override: Optional[Dict] = None,
        concurrent_explanation_spec_override: Optional[Dict] = None,
    ) -> Prediction:
        """Make a prediction with explanations against this Endpoint.

        Example usage:
            ```
            response = await my_endpoint.explain_async(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``.
            deployed_model_id (str):
                Optional. If specified, this ExplainRequest will be served by the
                chosen DeployedModel, overriding this Endpoint's traffic split.
            timeout (float): Optional. The timeout for this request in seconds.
            explanation_spec_override (Dict):
                Optional. Represents overrides to the explaination
                specification used when the model was deployed.
                The Explanation Override will
                be merged with model's existing [Explanation Spec
                ][google.cloud.aiplatform.v1beta1.ExplanationSpec].
            concurrent_explanation_spec_override (Dict):
                Optional. The ``explain`` endpoint supports multiple
                explanations in parallel. To request concurrent explanation in
                addition to the configured explaination method, use this field.

        Returns:
            prediction (aiplatform.Prediction):
                Prediction with returned predictions, explanations, and Model ID.
        """
        self.wait()

        request = gca_prediction_service_compat.ExplainRequest()
        request.endpoint = self.resource_name

        if instances is not None:
            request.instances.extend(instances)
        if parameters is not None:
            request.parameters = parameters
        if deployed_model_id is not None:
            request.deployed_model_id = deployed_model_id
        if explanation_spec_override is not None:
            request.explanation_spec_override = explanation_spec_override
        if concurrent_explanation_spec_override is not None:
            request.concurrent_explanation_spec_override = (
                concurrent_explanation_spec_override
            )

        explain_response = await self._prediction_async_client.select_version(
            "v1beta1"
        ).explain(request, timeout=timeout)

        prediction = Prediction(
            predictions=[
                json_format.MessageToDict(item)
                for item in explain_response.predictions.pb
            ],
            deployed_model_id=explain_response.deployed_model_id,
            explanations=explain_response.explanations,
        )

        concurrent_explanation = {}
        for k, e in explain_response.concurrent_explanations.items():
            concurrent_explanation[k] = e.explanations

        prediction.concurrent_explanations = concurrent_explanation

        return prediction


class Model(aiplatform.Model):
    def deploy(
        self,
        endpoint: Optional[Union["Endpoint", models.PrivateEndpoint]] = None,
        deployed_model_display_name: Optional[str] = None,
        traffic_percentage: Optional[int] = 0,
        traffic_split: Optional[Dict[str, int]] = None,
        machine_type: Optional[str] = None,
        min_replica_count: Optional[int] = 1,
        max_replica_count: Optional[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: Sequence[Tuple[str, str]] = (),
        encryption_spec_key_name: Optional[str] = None,
        network: Optional[str] = None,
        sync=True,
        deploy_request_timeout: Optional[float] = None,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        autoscaling_target_request_count_per_minute: Optional[int] = None,
        autoscaling_target_pubsub_num_undelivered_messages: Optional[int] = None,
        autoscaling_pubsub_subscription_labels: Optional[Dict[str, str]] = None,
        deployment_resource_pool: Optional[DeploymentResourcePool] = None,
        disable_container_logging: bool = False,
        fast_tryout_enabled: bool = False,
        system_labels: Optional[Dict[str, str]] = None,
        required_replica_count: Optional[int] = 0,
        rollout_options: Optional[RolloutOptions] = None,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
        initial_replica_count: Optional[int] = None,
        min_scaleup_period: Optional[int] = None,
        idle_scaledown_period: Optional[int] = None,
    ) -> Union[Endpoint, models.PrivateEndpoint]:
        """Deploys model to endpoint.

        Endpoint will be created if unspecified.

        Args:
            endpoint (Union[Endpoint, models.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.
              `explanation_metadata` is optional while `explanation_parameters` must
              be specified when used. For more details, see `Ref docs
              <http://tinyurl.com/1igh60kt>`
            explanation_parameters (aiplatform.explain.ExplanationParameters):
              Optional. Parameters to configure explaining for Model's predictions.
              For more details, see `Ref docs <http://tinyurl.com/1an4zake>`
            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 Endpoint and all sub-resources
                  of this Endpoint 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 the Endpoint, if created, will be peered to. E.g.
              "projects/12345/global/networks/myVPC". Private services access must
              already be configured for the network. If set or
              aiplatform.init(network=...) has been set, a PrivateEndpoint will be
              created. If left unspecified, an Endpoint 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_request_count_per_minute (int): Optional. Target
              request count per minute per instance.
            autoscaling_target_pubsub_num_undelivered_messages (int): Optional. Target
              number of pubsub undelivered messages per instance.
            autoscaling_pubsub_subscription_labels (Dict[str, str]): Optional.
              Monitored resource labels as key value pairs for metric filtering
              for pubsub_num_undelivered_messages.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            disable_container_logging (bool):
              If True, container logs from the deployed model will not be
              written to Cloud Logging. Defaults to False.
            fast_tryout_enabled (bool): Optional.
              If True, model will be deployed using faster deployment path.
              Useful for quick experiments. Not for production workloads. Only
              available for most popular models and machine types. Defaults to False.
            system_labels (Dict[str, str]):
              Optional. System labels to apply to Model Garden deployments.
              System labels are managed by Google for internal use only.
            required_replica_count (int):
                Optional. Number of required available replicas for the
                deployment to succeed. This field is only needed when partial
                model deployment/mutation is desired, with a value greater than
                or equal to 1 and fewer than or equal to min_replica_count. If
                set, the model deploy/mutate operation will succeed once
                available_replica_count reaches required_replica_count, and the
                rest of the replicas will be retried.
            rollout_options (RolloutOptions):
              Optional. Options to configure a rolling deployment.
            multihost_gpu_node_count (int):
              Optional. The number of nodes per replica for multihost GPU
              deployments. Required for multihost GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use DWS resource
                to schedule the deployment workload. The DWS resource will be available
                for a maximum of 7 days or up to the max_runtime_duration specified,
                whichever is shorter. After this period, the model will be
                automatically undeployed. The value is in seconds.
            initial_replica_count (int): Optional. The number of replicas to
              deploy the model with. Only applicable for scale-to-zero
              deployments where min_replica_count is 0.
            min_scaleup_period (int): Optional. For scale-to-zero deployments,
              minimum duration that a deployment will be scaled up before traffic
              is evaluated for potential scale-down.
            idle_scaledown_period (int): Optional. For scale-to-zero deployments,
              duration of no traffic before scaling to zero.
        Returns:
            endpoint (Union[Endpoint, models.PrivateEndpoint]):
                Endpoint with the deployed model.

        Raises:
            ValueError: If `traffic_split` is set for PrivateEndpoint.
        """
        network = network or initializer.global_config.network

        Endpoint._validate_deploy_args(
            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,
            deployment_resource_pool=deployment_resource_pool,
            required_replica_count=required_replica_count,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period,
        )

        if isinstance(endpoint, models.PrivateEndpoint):
            if traffic_split:
                raise ValueError(
                    "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."
                )

        explanation_spec = _explanation_utils.create_and_validate_explanation_spec(
            explanation_metadata=explanation_metadata,
            explanation_parameters=explanation_parameters,
        )

        return self._deploy(
            endpoint=endpoint,
            deployed_model_display_name=deployed_model_display_name,
            traffic_percentage=traffic_percentage,
            traffic_split=traffic_split,
            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_spec=explanation_spec,
            metadata=metadata,
            encryption_spec_key_name=encryption_spec_key_name,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period
            or initializer.global_config.encryption_spec_key_name,
            network=network,
            sync=sync,
            deploy_request_timeout=deploy_request_timeout,
            autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization,
            autoscaling_target_accelerator_duty_cycle=autoscaling_target_accelerator_duty_cycle,
            autoscaling_target_request_count_per_minute=autoscaling_target_request_count_per_minute,
            autoscaling_target_pubsub_num_undelivered_messages=autoscaling_target_pubsub_num_undelivered_messages,
            autoscaling_pubsub_subscription_labels=autoscaling_pubsub_subscription_labels,
            deployment_resource_pool=deployment_resource_pool,
            disable_container_logging=disable_container_logging,
            fast_tryout_enabled=fast_tryout_enabled,
            system_labels=system_labels,
            required_replica_count=required_replica_count,
            rollout_options=rollout_options,
            multihost_gpu_node_count=multihost_gpu_node_count,
            max_runtime_duration=max_runtime_duration,
        )

    def _should_enable_dedicated_endpoint(self, fast_tryout_enabled: bool) -> bool:
        """Check if dedicated endpoint should be enabled for this endpoint.

        Returns True if endpoint should be a dedicated endpoint.
        """
        return fast_tryout_enabled

    @base.optional_sync(return_input_arg="endpoint", bind_future_to_self=False)
    def _deploy(
        self,
        endpoint: Optional[Union["Endpoint", models.PrivateEndpoint]] = None,
        deployed_model_display_name: Optional[str] = None,
        traffic_percentage: Optional[int] = 0,
        traffic_split: Optional[Dict[str, int]] = None,
        machine_type: Optional[str] = None,
        min_replica_count: Optional[int] = 1,
        max_replica_count: Optional[int] = 1,
        accelerator_type: Optional[str] = None,
        accelerator_count: Optional[int] = None,
        service_account: Optional[str] = None,
        explanation_spec: Optional[aiplatform.explain.ExplanationSpec] = None,
        metadata: Sequence[Tuple[str, str]] = (),
        encryption_spec_key_name: Optional[str] = None,
        network: Optional[str] = None,
        sync: bool = True,
        deploy_request_timeout: Optional[float] = None,
        autoscaling_target_cpu_utilization: Optional[int] = None,
        autoscaling_target_accelerator_duty_cycle: Optional[int] = None,
        autoscaling_target_request_count_per_minute: Optional[int] = None,
        autoscaling_target_pubsub_num_undelivered_messages: Optional[int] = None,
        autoscaling_pubsub_subscription_labels: Optional[Dict[str, str]] = None,
        deployment_resource_pool: Optional[DeploymentResourcePool] = None,
        disable_container_logging: bool = False,
        fast_tryout_enabled: bool = False,
        system_labels: Optional[Dict[str, str]] = None,
        required_replica_count: Optional[int] = 0,
        rollout_options: Optional[RolloutOptions] = None,
        multihost_gpu_node_count: Optional[int] = None,
        max_runtime_duration: Optional[int] = None,
        initial_replica_count: Optional[int] = None,
        min_scaleup_period: Optional[int] = None,
        idle_scaledown_period: Optional[int] = None,
    ) -> Union[Endpoint, models.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_spec (aiplatform.explain.ExplanationSpec): Optional.
              Specification of Model explanation.
            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 the Endpoint, if created, will be peered to. E.g.
              "projects/12345/global/networks/myVPC". Private services access must
              already be configured for the network. 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_request_count_per_minute (int): Optional. Target
              request count per minute per instance.
            autoscaling_target_pubsub_num_undelivered_messages (int): Optional. Target
              number of pubsub undelivered messages per instance.
            autoscaling_pubsub_subscription_labels (Dict[str, str]): Optional.
              Monitored resource labels as key value pairs for metric filtering
              for pubsub_num_undelivered_messages.
            deployment_resource_pool (DeploymentResourcePool): Optional.
              Resource pool where the model will be deployed. All models that
              are deployed to the same DeploymentResourcePool will be hosted in
              a shared model server. If provided, will override replica count
              arguments.
            disable_container_logging (bool):
              If True, container logs from the deployed model will not be
              written to Cloud Logging. Defaults to False.
            fast_tryout_enabled (bool):
              Optional. Whether to enable fast deployment. Defaults to False.
              Useful for quick experiments. Not for production workloads.
            system_labels (Dict[str, str]):
              Optional. System labels to apply to Model Garden deployments.
              System labels are managed by Google for internal use only.
            required_replica_count (int):
              Optional. Number of required available replicas for the
              deployment to succeed. This field is only needed when partial
              model deployment/mutation is desired, with a value greater than
              or equal to 1 and fewer than or equal to min_replica_count. If
              set, the model deploy/mutate operation will succeed once
              available_replica_count reaches required_replica_count, and the
              rest of the replicas will be retried.
            rollout_options (RolloutOptions):
              Optional. Options to configure a rolling deployment.
            multihost_gpu_node_count (int):
              Optional. The number of nodes per replica for multihost GPU
              deployments. Required for multihost GPU deployments.
            max_runtime_duration (int):
                Optional. Immutable. If set, use DWS resource
                to schedule the deployment workload. The DWS resource will be available
                for a maximum of 7 days or up to the max_runtime_duration specified,
                whichever is shorter. After this period, the model will be
                automatically undeployed. The value is in seconds.
            initial_replica_count (int):
                Optional. The number of replicas to deploy the model with.
                Only applicable for scale-to-zero deployments where
                min_replica_count is 0.
            min_scaleup_period (int):
                Optional. For scale-to-zero deployments, minimum duration that
                a deployment will be scaled up before traffic is
                evaluated for potential scale-down.
            idle_scaledown_period (int):
                Optional. For scale-to-zero deployments, duration of no traffic
                before scaling to zero.
        Returns:
            endpoint (Union[Endpoint, models.PrivateEndpoint]):
                Endpoint with the deployed model.
        """

        if endpoint is None:
            display_name = self.display_name[:118] + "_endpoint"
            if rollout_options is not None:
                raise ValueError(
                    "Rollout options may only be used when deploying to an existing endpoint."
                )

            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,
                    dedicated_endpoint_enabled=self._should_enable_dedicated_endpoint(
                        fast_tryout_enabled
                    ),
                )
            else:
                endpoint = models.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,
                )
        if isinstance(endpoint, Endpoint):
            preview_kwargs = {"rollout_options": rollout_options}
        else:
            preview_kwargs = {}

        _LOGGER.log_action_start_against_resource("Deploying model to", "", endpoint)

        endpoint._deploy_call(
            endpoint.api_client,
            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,
            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_spec=explanation_spec,
            metadata=metadata,
            deploy_request_timeout=deploy_request_timeout,
            autoscaling_target_cpu_utilization=autoscaling_target_cpu_utilization,
            autoscaling_target_accelerator_duty_cycle=autoscaling_target_accelerator_duty_cycle,
            autoscaling_target_request_count_per_minute=autoscaling_target_request_count_per_minute,
            autoscaling_target_pubsub_num_undelivered_messages=autoscaling_target_pubsub_num_undelivered_messages,
            autoscaling_pubsub_subscription_labels=autoscaling_pubsub_subscription_labels,
            deployment_resource_pool=deployment_resource_pool,
            disable_container_logging=disable_container_logging,
            fast_tryout_enabled=fast_tryout_enabled,
            system_labels=system_labels,
            required_replica_count=required_replica_count,
            multihost_gpu_node_count=multihost_gpu_node_count,
            max_runtime_duration=max_runtime_duration,
            initial_replica_count=initial_replica_count,
            min_scaleup_period=min_scaleup_period,
            idle_scaledown_period=idle_scaledown_period,
            **preview_kwargs,
        )

        _LOGGER.log_action_completed_against_resource("model", "deployed", endpoint)

        endpoint._sync_gca_resource()

        return endpoint
