Skip to content

Commit

Permalink
Aqua BYOM Release (#875)
Browse files Browse the repository at this point in the history
  • Loading branch information
VipulMascarenhas authored Jun 6, 2024
2 parents 79ab368 + a7b9ae0 commit 69222e8
Show file tree
Hide file tree
Showing 76 changed files with 4,819 additions and 1,669 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-unittests-py38-cov-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
run: |
pip install -e ".[feature-store-marketplace]"
- name: "Run unitary tests folder with maximum ADS dependencies"
timeout-minutes: 40
timeout-minutes: 60
shell: bash
env:
CONDA_PREFIX: /usr/share/miniconda
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-unittests-py39-py310.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
tests/unitary/with_extras/hpo
- name: "Run unitary tests folder with maximum ADS dependencies"
timeout-minutes: 30
timeout-minutes: 60
shell: bash
env:
CONDA_PREFIX: /usr/share/miniconda
Expand Down
2 changes: 1 addition & 1 deletion ads/aqua/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import os

from ads import logger, set_auth
from ads.aqua.utils import fetch_service_compartment
from ads.aqua.common.utils import fetch_service_compartment
from ads.config import OCI_RESOURCE_PRINCIPAL_VERSION

ENV_VAR_LOG_LEVEL = "ADS_AQUA_LOG_LEVEL"
Expand Down
34 changes: 27 additions & 7 deletions ads/aqua/base.py → ads/aqua/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

import os
from dataclasses import fields
from typing import Dict, Union

import oci
from oci.data_science.models import UpdateModelDetails, UpdateModelProvenanceDetails

from ads import set_auth
from ads.aqua import logger
from ads.aqua.data import Tags
from ads.aqua.exception import AquaRuntimeError, AquaValueError
from ads.aqua.utils import (
UNKNOWN,
from ads.aqua.common.enums import Tags
from ads.aqua.common.errors import AquaRuntimeError, AquaValueError
from ads.aqua.common.utils import (
_is_valid_mvs,
get_artifact_path,
is_valid_ocid,
load_config,
)
from ads.aqua.constants import UNKNOWN
from ads.common import oci_client as oc
from ads.common.auth import default_signer
from ads.common.utils import extract_region
Expand Down Expand Up @@ -160,7 +161,7 @@ def create_model_version_set(
"""
# TODO: tag should be selected based on which operation (eval/FT) invoke this method
# currently only used by fine-tuning flow.
tag = Tags.AQUA_FINE_TUNING.value
tag = Tags.AQUA_FINE_TUNING

if not model_version_set_id:
try:
Expand Down Expand Up @@ -277,8 +278,8 @@ def get_config(self, model_id: str, config_file_name: str) -> Dict:
oci_model = self.ds_client.get_model(model_id).data
oci_aqua = (
(
Tags.AQUA_TAG.value in oci_model.freeform_tags
or Tags.AQUA_TAG.value.lower() in oci_model.freeform_tags
Tags.AQUA_TAG in oci_model.freeform_tags
or Tags.AQUA_TAG.lower() in oci_model.freeform_tags
)
if oci_model.freeform_tags
else False
Expand Down Expand Up @@ -319,3 +320,22 @@ def telemetry(self):
bucket=AQUA_TELEMETRY_BUCKET, namespace=AQUA_TELEMETRY_BUCKET_NS
)
return self._telemetry


class CLIBuilderMixin:
"""
CLI builder from API interface. To be used with the DataClass only.
"""

def build_cli(self) -> str:
"""
Method to turn the dataclass attributes to CLI
"""
cmd = f"ads aqua {self._command}"
params = [
f"--{field.name} {getattr(self,field.name)}"
for field in fields(self.__class__)
if getattr(self, field.name)
]
cmd = f"{cmd} {' '.join(params)}"
return cmd
76 changes: 59 additions & 17 deletions ads/aqua/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
# Copyright (c) 2024 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
import os
import sys

from ads.aqua import (
ENV_VAR_LOG_LEVEL,
set_log_level,
ODSC_MODEL_COMPARTMENT_OCID,
logger,
set_log_level,
)
from ads.aqua.deployment import AquaDeploymentApp
from ads.aqua.common.errors import AquaCLIError, AquaConfigError
from ads.aqua.evaluation import AquaEvaluationApp
from ads.aqua.finetune import AquaFineTuningApp
from ads.aqua.finetuning import AquaFineTuningApp
from ads.aqua.model import AquaModelApp
from ads.config import NB_SESSION_OCID
from ads.aqua.modeldeployment import AquaDeploymentApp
from ads.common.utils import LOG_LEVELS
from ads.config import NB_SESSION_OCID


class AquaCommand:
Expand All @@ -35,6 +35,8 @@ class AquaCommand:

def __init__(
self,
debug: bool = None,
verbose: bool = None,
log_level: str = os.environ.get(ENV_VAR_LOG_LEVEL, "ERROR").upper(),
):
"""
Expand All @@ -44,24 +46,64 @@ def __init__(
-----
log_level (str):
Sets the logging level for the application.
Default is retrieved from environment variable `LOG_LEVEL`,
Default is retrieved from environment variable `ADS_AQUA_LOG_LEVEL`,
or 'ERROR' if not set. Example values include 'DEBUG', 'INFO',
'WARNING', 'ERROR', and 'CRITICAL'.
debug (bool):
Sets the logging level for the application to `DEBUG`.
verbose (bool):
Sets the logging level for the application to `INFO`.
Raises
------
AquaCLIError:
When `--verbose` and `--debug` being used together.
When missing required `ODSC_MODEL_COMPARTMENT_OCID` env var.
"""
if log_level.upper() not in LOG_LEVELS:
logger.error(
f"Log level should be one of {LOG_LEVELS}. Setting default to ERROR."
if verbose is not None and debug is not None:
raise AquaCLIError(
"Cannot use `--debug` and `--verbose` at the same time. "
"Please select either `--debug` for `DEBUG` level logging or "
"`--verbose` for `INFO` level logging."
)
log_level = "ERROR"
set_log_level(log_level)
# gracefully exit if env var is not set
elif verbose is not None:
self._validate_value("--verbose", verbose)
aqua_log_level = "INFO"
elif debug is not None:
self._validate_value("--debug", debug)
aqua_log_level = "DEBUG"
else:
if log_level.upper() not in LOG_LEVELS:
logger.warning(
f"Log level should be one of {LOG_LEVELS}. Setting default to ERROR."
)
log_level = "ERROR"
aqua_log_level = log_level.upper()

set_log_level(aqua_log_level)

if not ODSC_MODEL_COMPARTMENT_OCID:
logger.debug(
"ODSC_MODEL_COMPARTMENT_OCID environment variable is not set for Aqua."
)
if NB_SESSION_OCID:
logger.error(
raise AquaConfigError(
f"Aqua is not available for the notebook session {NB_SESSION_OCID}. For more information, "
f"please refer to the documentation."
)
sys.exit(1)
raise AquaConfigError(
"ODSC_MODEL_COMPARTMENT_OCID environment variable is not set for Aqua."
)

@staticmethod
def _validate_value(flag, value):
"""Check if the given value for bool flag is valid.
Raises
------
AquaCLIError:
When the given value for bool flag is invalid.
"""
if value not in [True, False]:
raise AquaCLIError(
f"Invalid input `{value}` for flag: {flag}, a boolean value is required. "
"If you intend to chain a function call to the result, please separate the "
"flag and the subsequent function call with separator `-`."
)
5 changes: 5 additions & 0 deletions ads/aqua/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*--

# Copyright (c) 2024 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
22 changes: 14 additions & 8 deletions ads/aqua/decorator.py → ads/aqua/common/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import sys
from functools import wraps
from typing import TYPE_CHECKING, Union

from oci.exceptions import (
ClientError,
Expand All @@ -19,9 +20,12 @@
)
from tornado.web import HTTPError

from ads.aqua.exception import AquaError
from ads.aqua.common.errors import AquaError
from ads.aqua.extension.base_handler import AquaAPIhandler

if TYPE_CHECKING:
from ads.aqua.extension.aqua_ws_msg_handler import AquaWSMsgHandler


def handle_exceptions(func):
"""Writes errors raised during call to JSON.
Expand Down Expand Up @@ -53,11 +57,13 @@ def handle_exceptions(func):
"""

@wraps(func)
def inner_function(self: AquaAPIhandler, *args, **kwargs):
def inner_function(
self: Union[AquaAPIhandler, "AquaWSMsgHandler"], *args, **kwargs
):
try:
return func(self, *args, **kwargs)
except ServiceError as error:
self.write_error(
return self.write_error(
status_code=error.status or 500,
message=error.message,
reason=error.message,
Expand All @@ -69,25 +75,25 @@ def inner_function(self: AquaAPIhandler, *args, **kwargs):
MissingEndpointForNonRegionalServiceClientError,
RequestException,
) as error:
self.write_error(
return self.write_error(
status_code=400,
reason=f"{type(error).__name__}: {str(error)}",
exc_info=sys.exc_info(),
)
except ConnectTimeout as error:
self.write_error(
return self.write_error(
status_code=408,
reason=f"{type(error).__name__}: {str(error)}",
exc_info=sys.exc_info(),
)
except (MultipartUploadError, CompositeOperationError) as error:
self.write_error(
return self.write_error(
status_code=500,
reason=f"{type(error).__name__}: {str(error)}",
exc_info=sys.exc_info(),
)
except AquaError as error:
self.write_error(
return self.write_error(
status_code=error.status,
reason=error.reason,
service_payload=error.service_payload,
Expand All @@ -100,7 +106,7 @@ def inner_function(self: AquaAPIhandler, *args, **kwargs):
exc_info=sys.exc_info(),
)
except Exception as ex:
self.write_error(
return self.write_error(
status_code=500,
reason=f"{type(ex).__name__}: {str(ex)}",
exc_info=sys.exc_info(),
Expand Down
69 changes: 69 additions & 0 deletions ads/aqua/common/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2024 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

"""
aqua.common.enums
~~~~~~~~~~~~~~
This module contains the set of enums used in AQUA.
"""
from ads.common.extended_enum import ExtendedEnumMeta


class DataScienceResource(str, metaclass=ExtendedEnumMeta):
MODEL_DEPLOYMENT = "datasciencemodeldeployment"
MODEL = "datasciencemodel"


class Resource(str, metaclass=ExtendedEnumMeta):
JOB = "jobs"
JOBRUN = "jobruns"
MODEL = "models"
MODEL_DEPLOYMENT = "modeldeployments"
MODEL_VERSION_SET = "model-version-sets"


class Tags(str, metaclass=ExtendedEnumMeta):
TASK = "task"
LICENSE = "license"
ORGANIZATION = "organization"
AQUA_TAG = "OCI_AQUA"
AQUA_SERVICE_MODEL_TAG = "aqua_service_model"
AQUA_FINE_TUNED_MODEL_TAG = "aqua_fine_tuned_model"
AQUA_MODEL_NAME_TAG = "aqua_model_name"
AQUA_EVALUATION = "aqua_evaluation"
AQUA_FINE_TUNING = "aqua_finetuning"
READY_TO_FINE_TUNE = "ready_to_fine_tune"
READY_TO_IMPORT = "ready_to_import"
BASE_MODEL_CUSTOM = "aqua_custom_base_model"
AQUA_EVALUATION_MODEL_ID = "evaluation_model_id"


class InferenceContainerType(str, metaclass=ExtendedEnumMeta):
CONTAINER_TYPE_VLLM = "vllm"
CONTAINER_TYPE_TGI = "tgi"


class InferenceContainerTypeFamily(str, metaclass=ExtendedEnumMeta):
AQUA_VLLM_CONTAINER_FAMILY = "odsc-vllm-serving"
AQUA_TGI_CONTAINER_FAMILY = "odsc-tgi-serving"


class InferenceContainerParamType(str, metaclass=ExtendedEnumMeta):
PARAM_TYPE_VLLM = "VLLM_PARAMS"
PARAM_TYPE_TGI = "TGI_PARAMS"


class HuggingFaceTags(str, metaclass=ExtendedEnumMeta):
TEXT_GENERATION_INFERENCE = "text-generation-inference"


class RqsAdditionalDetails(str, metaclass=ExtendedEnumMeta):
METADATA = "metadata"
CREATED_BY = "createdBy"
DESCRIPTION = "description"
MODEL_VERSION_SET_ID = "modelVersionSetId"
MODEL_VERSION_SET_NAME = "modelVersionSetName"
PROJECT_ID = "projectId"
VERSION_LABEL = "versionLabel"
Loading

0 comments on commit 69222e8

Please sign in to comment.