Skip to content

[DRCC][AQUA] Completely Removing OSS bucket config dependency #1147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 9, 2025
5 changes: 0 additions & 5 deletions ads/aqua/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
get_async_httpx_client,
get_httpx_client,
)
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 All @@ -39,7 +38,3 @@ def set_log_level(log_level: str):

if OCI_RESOURCE_PRINCIPAL_VERSION:
set_auth("resource_principal")

ODSC_MODEL_COMPARTMENT_OCID = (
os.environ.get("ODSC_MODEL_COMPARTMENT_OCID") or fetch_service_compartment()
)
17 changes: 2 additions & 15 deletions ads/aqua/cli.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*--

# Copyright (c) 2024 Oracle and/or its affiliates.
# Copyright (c) 2024, 2025 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

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


class AquaCommand:
Expand Down Expand Up @@ -82,16 +79,6 @@ def __init__(

set_log_level(aqua_log_level)

if not ODSC_MODEL_COMPARTMENT_OCID:
if NB_SESSION_OCID:
raise AquaConfigError(
f"Aqua is not available for the notebook session {NB_SESSION_OCID}. For more information, "
f"please refer to the documentation."
)
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.
Expand Down
32 changes: 0 additions & 32 deletions ads/aqua/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@
)
from ads.aqua.constants import (
AQUA_GA_LIST,
COMPARTMENT_MAPPING_KEY,
CONSOLE_LINK_RESOURCE_TYPE_MAPPING,
CONTAINER_INDEX,
DEPLOYMENT_CONFIG,
FINE_TUNING_CONFIG,
HF_LOGIN_DEFAULT_TIMEOUT,
Expand Down Expand Up @@ -561,36 +559,6 @@ def _build_job_identifier(
return AquaResourceIdentifier()


def service_config_path():
return f"oci://{AQUA_SERVICE_MODELS_BUCKET}@{CONDA_BUCKET_NS}/service_models/config"


def fetch_service_compartment() -> Union[str, None]:
"""
Loads the compartment mapping json from service bucket.
This json file has a service-model-compartment key which contains a dictionary of namespaces
and the compartment OCID of the service models in that namespace.
"""
config_file_name = (
f"oci://{AQUA_SERVICE_MODELS_BUCKET}@{CONDA_BUCKET_NS}/service_models/config"
)

try:
config = load_config(
file_path=config_file_name,
config_file_name=CONTAINER_INDEX,
)
except Exception as e:
logger.debug(
f"Config file {config_file_name}/{CONTAINER_INDEX} to fetch service compartment OCID "
f"could not be found. \n{str(e)}."
)
return
compartment_mapping = config.get(COMPARTMENT_MAPPING_KEY)
if compartment_mapping:
return compartment_mapping.get(CONDA_BUCKET_NS)


def get_max_version(versions):
"""Takes in a list of versions and returns the higher version."""
if not versions:
Expand Down
104 changes: 13 additions & 91 deletions ads/aqua/config/container_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from oci.data_science.models import ContainerSummary
from pydantic import Field

from ads.aqua.common.entities import ContainerSpec
from ads.aqua.config.utils.serializer import Serializable
from ads.aqua.constants import (
SERVICE_MANAGED_CONTAINER_URI_SCHEME,
Expand Down Expand Up @@ -185,24 +184,32 @@ def from_service_config(
0
].additional_configurations.get(
"MODEL_DEPLOY_PREDICT_ENDPOINT", UNKNOWN
),
)
},
{
"MODEL_DEPLOY_HEALTH_ENDPOINT": container.workload_configuration_details_list[
0
].additional_configurations.get(
"MODEL_DEPLOY_HEALTH_ENDPOINT", UNKNOWN
),
)
},
{
"MODEL_DEPLOY_ENABLE_STREAMING": container.workload_configuration_details_list[
0
].additional_configurations.get(
"MODEL_DEPLOY_ENABLE_STREAMING", UNKNOWN
),
)
},
{
"PORT": container.workload_configuration_details_list[
0
].additional_configurations.get("PORT", ""),
].additional_configurations.get("PORT", "")
},
{
"HEALTH_CHECK_PORT": container.workload_configuration_details_list[
0
].additional_configurations.get("HEALTH_CHECK_PORT", UNKNOWN),
}
},
]
container_spec = AquaContainerConfigSpec(
cli_param=container.workload_configuration_details_list[0].cmd,
Expand Down Expand Up @@ -239,88 +246,3 @@ def from_service_config(
return cls(
inference=inference_items, finetune=finetune_items, evaluate=evaluate_items
)

@classmethod
def from_container_index_json(
cls,
config: Dict,
enable_spec: Optional[bool] = False,
) -> "AquaContainerConfig":
"""
Creates an AquaContainerConfig instance from a container index JSON.

Parameters
----------
config (Optional[Dict]): The container index JSON.
enable_spec (Optional[bool]): If True, fetch container specification details.

Returns
-------
AquaContainerConfig: The constructed container configuration.
"""
# TODO: Return this logic back if necessary in the next iteraion.
# if not config:
# config = get_container_config()
inference_items: Dict[str, AquaContainerConfigItem] = {}
finetune_items: Dict[str, AquaContainerConfigItem] = {}
evaluate_items: Dict[str, AquaContainerConfigItem] = {}

for container_type, containers in config.items():
if isinstance(containers, list):
for container in containers:
platforms = container.get("platforms", [])
model_formats = container.get("modelFormats", [])
usages = container.get("usages", [])
container_spec = (
config.get(ContainerSpec.CONTAINER_SPEC, {}).get(
container_type, {}
)
if enable_spec
else None
)
container_item = AquaContainerConfigItem(
name=container.get("name", ""),
version=container.get("version", ""),
display_name=container.get(
"displayName", container.get("version", "")
),
family=container_type,
platforms=platforms,
model_formats=model_formats,
usages=usages,
spec=(
AquaContainerConfigSpec(
cli_param=container_spec.get(
ContainerSpec.CLI_PARM, ""
),
server_port=container_spec.get(
ContainerSpec.SERVER_PORT, ""
),
health_check_port=container_spec.get(
ContainerSpec.HEALTH_CHECK_PORT, ""
),
env_vars=container_spec.get(ContainerSpec.ENV_VARS, []),
restricted_params=container_spec.get(
ContainerSpec.RESTRICTED_PARAMS, []
),
)
if container_spec
else None
),
)
if container.get("type").lower() == "inference":
inference_items[container_type] = container_item
elif (
container.get("type").lower() == "fine-tune"
or container_type == "odsc-llm-fine-tuning"
):
finetune_items[container_type] = container_item
elif (
container.get("type").lower() in ("evaluation", "evaluate")
or container_type == "odsc-llm-evaluate"
):
evaluate_items[container_type] = container_item

return cls(
inference=inference_items, finetune=finetune_items, evaluate=evaluate_items
)
27 changes: 3 additions & 24 deletions ads/aqua/extension/common_ws_msg_handler.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
#!/usr/bin/env python

# Copyright (c) 2024 Oracle and/or its affiliates.
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

import json
from importlib import metadata
from typing import List, Union
from typing import List, Optional, Union

from ads.aqua.common.decorator import handle_exceptions
from ads.aqua.common.errors import AquaResourceAccessError
from ads.aqua.common.utils import known_realm
from ads.aqua.extension.aqua_ws_msg_handler import AquaWSMsgHandler
from ads.aqua.extension.models.ws_models import (
AdsVersionResponse,
CompatibilityCheckResponse,
RequestResponseType,
)
from ads.aqua.extension.utils import ui_compatability_check


class AquaCommonWsMsgHandler(AquaWSMsgHandler):
Expand All @@ -28,7 +24,7 @@ def __init__(self, message: Union[str, bytes]):
super().__init__(message)

@handle_exceptions
def process(self) -> Union[AdsVersionResponse, CompatibilityCheckResponse]:
def process(self) -> Optional[AdsVersionResponse]:
request = json.loads(self.message)
if request.get("kind") == "AdsVersion":
version = metadata.version("oracle_ads")
Expand All @@ -38,20 +34,3 @@ def process(self) -> Union[AdsVersionResponse, CompatibilityCheckResponse]:
data=version,
)
return response
if request.get("kind") == "CompatibilityCheck":
if ui_compatability_check():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this always returns ok, does UI need to call this? This won't be needed, right?

Copy link
Member Author

@kumar-shivam-ranjan kumar-shivam-ranjan Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is compatibility check handler implemented in websockets. We call the one implemented via HTTP. Both the methods should return ok always as we decide to enable AQUA everywhere.

Copy link
Member Author

@kumar-shivam-ranjan kumar-shivam-ranjan Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes , we can remove calling this API in UI. I kept it since may be in future we can add some sort of AQUA enabling logic based on CP config. Are you suggesting we removing this api alltogether from ADS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Thanks

return CompatibilityCheckResponse(
message_id=request.get("message_id"),
kind=RequestResponseType.CompatibilityCheck,
data={"status": "ok"},
)
elif known_realm():
return CompatibilityCheckResponse(
message_id=request.get("message_id"),
kind=RequestResponseType.CompatibilityCheck,
data={"status": "compatible"},
)
else:
raise AquaResourceAccessError(
"The AI Quick actions extension is not compatible in the given region."
)
4 changes: 2 additions & 2 deletions ads/aqua/extension/model_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ads.aqua.extension.errors import Errors
from ads.aqua.model import AquaModelApp
from ads.aqua.model.entities import AquaModelSummary, HFModelSummary
from ads.config import USER
from ads.config import SERVICE
from ads.model.common.utils import MetadataArtifactPathType


Expand Down Expand Up @@ -82,7 +82,7 @@ def list(self):
# project_id is no needed.
project_id = self.get_argument("project_id", default=None)
model_type = self.get_argument("model_type", default=None)
category = self.get_argument("category", default=USER)
category = self.get_argument("category", default=SERVICE)
return self.finish(
AquaModelApp().list(
compartment_id=compartment_id,
Expand Down
35 changes: 11 additions & 24 deletions ads/aqua/extension/utils.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
#!/usr/bin/env python
# Copyright (c) 2024 Oracle and/or its affiliates.
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

import re
import traceback
import uuid
from dataclasses import fields
from datetime import datetime, timedelta
from http.client import responses
from typing import Dict, Optional

from cachetools import TTLCache, cached
from tornado.web import HTTPError

from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID, logger
from ads.aqua.common.utils import fetch_service_compartment
from ads.aqua import logger
from ads.aqua.constants import (
AQUA_TROUBLESHOOTING_LINK,
OCI_OPERATION_FAILURES,
Expand All @@ -36,19 +33,11 @@ def validate_function_parameters(data_class, input_data: Dict):
)


@cached(cache=TTLCache(maxsize=1, ttl=timedelta(minutes=1), timer=datetime.now))
def ui_compatability_check():
"""This method caches the service compartment OCID details that is set by either the environment variable or if
fetched from the configuration. The cached result is returned when multiple calls are made in quick succession
from the UI to avoid multiple config file loads."""
return ODSC_MODEL_COMPARTMENT_OCID or fetch_service_compartment()


def get_default_error_messages(
service_payload: dict,
status_code: str,
default_msg: str = "Unknown HTTP Error.",
)-> str:
) -> str:
"""Method that maps the error messages based on the operation performed or the status codes encountered."""

if service_payload and "operation_name" in service_payload:
Expand All @@ -66,14 +55,13 @@ def get_documentation_link(key: str) -> str:
return f"{AQUA_TROUBLESHOOTING_LINK}#{github_header}"


def get_troubleshooting_tips(service_payload: dict,
status_code: str) -> str:
def get_troubleshooting_tips(service_payload: dict, status_code: str) -> str:
"""Maps authorization errors to potential solutions on Troubleshooting Page per Aqua Documentation on oci-data-science-ai-samples"""

tip = f"For general tips on troubleshooting: {AQUA_TROUBLESHOOTING_LINK}"

if status_code in (404, 400):
failed_operation = service_payload.get('operation_name')
failed_operation = service_payload.get("operation_name")

if failed_operation in OCI_OPERATION_FAILURES:
link = get_documentation_link(failed_operation)
Expand Down Expand Up @@ -118,14 +106,13 @@ def construct_error(status_code: int, **kwargs) -> ReplyDetails:

tips = get_troubleshooting_tips(service_payload, status_code)


reply = ReplyDetails(
status = status_code,
troubleshooting_tips = tips,
message = message,
service_payload = service_payload,
reason = reason,
request_id = str(uuid.uuid4()),
status=status_code,
troubleshooting_tips=tips,
message=message,
service_payload=service_payload,
reason=reason,
request_id=str(uuid.uuid4()),
)

exc_info = kwargs.get("exc_info")
Expand Down
Loading