From d681e17b3b93640af654bf811adac4de78873626 Mon Sep 17 00:00:00 2001 From: rvikas Date: Mon, 2 Dec 2024 19:37:34 +0530 Subject: [PATCH 01/44] Added methods for aqua-model store apis change --- ads/model/datascience_model.py | 260 ++++++++++++- ads/model/model_version_set.py | 4 +- ads/model/service/oci_datascience_model.py | 341 +++++++++++++++++- .../metadata_test_artifact_test.json | 4 + .../model/test_oci_datascience_model.py | 67 ++++ 5 files changed, 668 insertions(+), 8 deletions(-) create mode 100644 tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 4a2f209c4..c248233dc 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -10,8 +10,10 @@ import os import shutil import tempfile +import uuid from copy import deepcopy from typing import Dict, List, Optional, Tuple, Union +from zipfile import ZipFile import pandas import yaml @@ -1571,7 +1573,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None, **kwargs + cls, compartment_id: str = None, project_id: str = None,category: str = "USER", **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1581,6 +1583,8 @@ def list( The compartment OCID. project_id: (str, optional). Defaults to `None`. The project OCID. + category: (str, optional). Defaults to `USER`. + The category of Model. kwargs Additional keyword arguments for filtering models. @@ -1592,13 +1596,13 @@ def list( return [ cls()._update_from_oci_dsc_model(model) for model in OCIDataScienceModel.list_resource( - compartment_id, project_id=project_id, **kwargs + compartment_id, project_id=project_id, category=category, **kwargs ) ] @classmethod def list_df( - cls, compartment_id: str = None, project_id: str = None, **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs ) -> "pandas.DataFrame": """Lists datascience models in a given compartment. @@ -1608,6 +1612,8 @@ def list_df( The compartment OCID. project_id: (str, optional). Defaults to `None`. The project OCID. + category: (str, optional). Defaults to `None`. + The category of Model. kwargs Additional keyword arguments for filtering models. @@ -1618,7 +1624,7 @@ def list_df( """ records = [] for model in OCIDataScienceModel.list_resource( - compartment_id, project_id=project_id, **kwargs + compartment_id, project_id=project_id, category=category, **kwargs ): records.append( { @@ -2193,3 +2199,249 @@ def find_model_idx(): else: # model found case self.model_file_description["models"].pop(modelSearchIdx) + + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.create_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + The model defined metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.create_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.update_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model defined metadata artifact path to be upload. + Returns + ------- + Dict + The model defined metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.update_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + """Downloads model custom metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + custom metadata artifact content + + """ + file_content = self.dsc_model.get_custom_metadata_artifact(model_ocid=model_ocid, + metadata_key_name=metadata_key_name) + artifact_file_path = os.path.join( + target_dir, f"{metadata_key_name}" + ) + with open(artifact_file_path, "wb") as _file: + _file.write(file_content) + print(f"Artifact downloaded to location - {artifact_file_path}") + + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + """Downloads model defined metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + Defined metadata artifact content + + """ + file_content = self.dsc_model.get_defined_metadata_artifact(model_ocid=model_ocid, + metadata_key_name=metadata_key_name) + artifact_file_path = os.path.join( + target_dir, f"{metadata_key_name}" + ) + with open(artifact_file_path, "wb") as _file: + _file.write(file_content) + print(f"Artifact downloaded to location - {artifact_file_path}") + + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model custom metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.delete_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) + + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model defined metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.delete_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index 459a75635..ba2a5f290 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. @@ -473,7 +473,7 @@ def list(cls, compartment_id: str = None, **kwargs) -> List["ModelVersionSet"]: return [ cls.from_dsc_model_version_set(model_version_set) for model_version_set in DataScienceModelVersionSet.list_resource( - compartment_id, **kwargs + compartment_id, category, **kwargs ) ] diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 44ba091a6..d1e746130 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -56,6 +56,9 @@ class ModelNotSavedError(Exception): # pragma: no cover class ModelWithActiveDeploymentError(Exception): # pragma: no cover pass +class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover + pass + def check_for_model_id(msg: str = MODEL_NEEDS_TO_BE_SAVED): """The decorator helping to check if the ID attribute sepcified for a datascience model. @@ -89,6 +92,12 @@ def wrapper(self, *args, **kwargs): return decorator +def convert_response_to_dict(headers:dict,status:int): + response_dict: dict = headers + response_dict['status'] = str(status) + return response_dict + + class OCIDataScienceModel( OCIDataScienceMixin, OCIWorkRequestMixin, @@ -184,7 +193,7 @@ def create(self) -> "OCIDataScienceModel": msg="Model needs to be saved to the Model Catalog before the provenance metadata can be created." ) def create_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Creates model provenance metadata. @@ -204,7 +213,7 @@ def create_model_provenance( msg="Model needs to be saved to the Model Catalog before the provenance metadata can be updated." ) def update_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Updates model provenance metadata. @@ -596,3 +605,331 @@ def is_model_by_reference(self): ): return True return False + + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + print(contents) + + response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form' + '-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers,response.status) + return response_data + + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + The model defined metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers,response.status) + return response_data + + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model defined metadata artifact path to be upload. + Returns + ------- + Dict + The model defined metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form' + '-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + """Downloads model custom metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + custom metadata artifact content + + """ + try: + return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + except ServiceError as ex: + if ex.status == 404: + raise ModelMetadataArtifactNotFoundError() + + + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + """Downloads model defined metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + Defined metadata artifact content + + """ + try: + return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + except ServiceError as ex: + if ex.status == 404: + raise ModelMetadataArtifactNotFoundError() + + + def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Gets custom metadata artifact metadata for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact head call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.head_model_custom_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Gets defined metadata artifact metadata for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact head call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.head_model_defined_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model custom metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.delete_model_custom_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model defined metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.delete_model_defined_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data diff --git a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json new file mode 100644 index 000000000..48bc203df --- /dev/null +++ b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json @@ -0,0 +1,4 @@ +{ + "type": "metadata_test_artifact_file", + "version": "1.0" +} diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 7a44019e1..9db40aadc 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -484,3 +484,70 @@ def test_is_model_by_reference(self): self.mock_model.custom_metadata_list = [metadata_item] assert self.mock_model.is_model_by_reference() + + @patch.object(OCIDataScienceModel, "client") + def test_create_defined_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.create_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + "./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_create_custom_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.create_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + "./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_update_defined_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.update_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_update_custom_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.update_model_custom_metadatum_artifact.return_value = response + data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_delete_defined_metadata_artifact(self, mock_client): + """Tests delete defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.delete_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.delete_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_delete_custom_metadata_artifact(self, mock_client): + """Tests delete defined metadata artifact for specified model.""" + response = Response(headers={}, status=204,data=None,request=None) + mock_client.delete_model_custom_metadatum_artifact.return_value = response + data = self.mock_model.delete_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_get_custom_metadata_artifact(self, mock_client): + """Tests gets defined metadata artifact for specified model.""" + mock_client.get_model_custom_metadatum_artifact_content.return_value.data.content = b"some file" + data = self.mock_model.get_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data == b"some file" + + @patch.object(OCIDataScienceModel, "client") + def test_get_defined_metadata_artifact(self, mock_client): + """Tests gets defined metadata artifact for specified model.""" + mock_client.get_model_defined_metadatum_artifact_content.return_value.data.content = b"some file" + data = self.mock_model.get_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data == b"some file" + + + From 77783cefe34d66f86d38c042bbc0b1b11a3b282a Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 5 Dec 2024 12:11:05 +0530 Subject: [PATCH 02/44] update modela and mvs list api --- ads/model/datascience_model.py | 4 ++-- ads/model/model_version_set.py | 6 ++++-- tests/unitary/default_setup/model/test_model_version_set.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index c248233dc..6d14f15f1 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1573,7 +1573,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None,category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1584,7 +1584,7 @@ def list( project_id: (str, optional). Defaults to `None`. The project OCID. category: (str, optional). Defaults to `USER`. - The category of Model. + The category of Model. Allowed values are: "USER", "SERVICE" kwargs Additional keyword arguments for filtering models. diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index ba2a5f290..6c0c7f5bb 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: str = "USER", **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. @@ -462,6 +462,8 @@ def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kw ---------- compartment_id: str The OCID of compartment. + category: (str, optional). Defaults to `USER`. + The category of Model. Allowed values are: "USER", "SERVICE" kwargs Additional keyword arguments for filtering model version sets. @@ -473,7 +475,7 @@ def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kw return [ cls.from_dsc_model_version_set(model_version_set) for model_version_set in DataScienceModelVersionSet.list_resource( - compartment_id, category, **kwargs + compartment_id, category=category, **kwargs ) ] diff --git a/tests/unitary/default_setup/model/test_model_version_set.py b/tests/unitary/default_setup/model/test_model_version_set.py index ea9cea19a..a452d753c 100644 --- a/tests/unitary/default_setup/model/test_model_version_set.py +++ b/tests/unitary/default_setup/model/test_model_version_set.py @@ -322,7 +322,7 @@ def test_list(self): test_result = mvs.list(compartment_id="test_compartment_id") for i, item in enumerate(test_result): assert item.to_dict() == expected_result[i].to_dict() - mock_list_resource.assert_called_with("test_compartment_id") + mock_list_resource.assert_called_with("test_compartment_id", category="USER") @patch.object(DataScienceModel, "list") def test_models(self, mock_list_models): From 804d17f58406d127683fd1f8c104446c5a52803e Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 14:54:20 +0530 Subject: [PATCH 03/44] update the test file --- .../model/test_files/metadata_test_artifact_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json index 48bc203df..1f2b1b954 100644 --- a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json +++ b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json @@ -1,4 +1,4 @@ { - "type": "metadata_test_artifact_file", + "type": "metadata_test_artifact_files", "version": "1.0" } From 09f2f9b33317fc9a0a2c7d63655eb537992ee013 Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 16:02:02 +0530 Subject: [PATCH 04/44] addressed review comments --- ads/config.py | 3 + ads/model/datascience_model.py | 69 ++++++++++++------- ads/model/model_version_set.py | 4 +- ads/model/service/oci_datascience_model.py | 53 ++++++++------ .../model/test_oci_datascience_model.py | 10 +-- 5 files changed, 88 insertions(+), 51 deletions(-) diff --git a/ads/config.py b/ads/config.py index cd4ee2b07..53342988f 100644 --- a/ads/config.py +++ b/ads/config.py @@ -80,6 +80,9 @@ DEBUG_TELEMETRY = os.environ.get("DEBUG_TELEMETRY", None) AQUA_SERVICE_NAME = "aqua" DATA_SCIENCE_SERVICE_NAME = "data-science" +USER = "USER" +SERVICE = "SERVICE" + THREADED_DEFAULT_TIMEOUT = 5 diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 6d14f15f1..fe40fcd67 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8; -*- # Copyright (c) 2022, 2024 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -10,10 +9,8 @@ import os import shutil import tempfile -import uuid from copy import deepcopy from typing import Dict, List, Optional, Tuple, Union -from zipfile import ZipFile import pandas import yaml @@ -23,10 +20,13 @@ from ads.common import utils from ads.common.extended_enum import ExtendedEnumMeta from ads.common.object_storage_details import ObjectStorageDetails +from ads.config import ( + AQUA_SERVICE_MODELS_BUCKET as SERVICE_MODELS_BUCKET, +) from ads.config import ( COMPARTMENT_OCID, PROJECT_OCID, - AQUA_SERVICE_MODELS_BUCKET as SERVICE_MODELS_BUCKET, + USER, ) from ads.feature_engineering.schema import Schema from ads.jobs.builders.base import Builder @@ -43,6 +43,7 @@ ModelTaxonomyMetadata, ) from ads.model.service.oci_datascience_model import ( + ModelMetadataArtifactDetails, ModelProvenanceNotFoundError, OCIDataScienceModel, ) @@ -1044,7 +1045,7 @@ def with_model_file_description( elif json_string: json_data = json.loads(json_string) elif json_uri: - with open(json_uri, "r") as json_file: + with open(json_uri) as json_file: json_data = json.load(json_file) else: raise ValueError("Must provide either a valid json string or URI location.") @@ -1573,7 +1574,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1602,7 +1603,7 @@ def list( @classmethod def list_df( - cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs ) -> "pandas.DataFrame": """Lists datascience models in a given compartment. @@ -2200,7 +2201,7 @@ def find_model_idx(): # model found case self.model_file_description["models"].pop(modelSearchIdx) - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters @@ -2213,7 +2214,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2235,7 +2236,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st artifact_path=artifact_path) - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters @@ -2248,7 +2249,7 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model defined metadata artifact local file path to be upload. Returns ------- The model defined metadata artifact creation info. @@ -2269,7 +2270,7 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s artifact_path=artifact_path) - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters @@ -2282,7 +2283,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2304,7 +2305,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st artifact_path=artifact_path) - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters @@ -2317,7 +2318,7 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. artifact_path: str - The model defined metadata artifact path to be upload. + The model defined metadata artifact local file path to be upload. Returns ------- Dict @@ -2338,7 +2339,8 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s return self.dsc_model.update_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, artifact_path=artifact_path) - def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, + override: bool = False) -> None: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -2349,6 +2351,12 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, metadata_key_name: str The name of the model metadatum in the metadata. + target_dir: str + The local file path where downloaded model custom metadata artifact saved. + override: bool + A boolean flag that controls downloaded metadata artifact file overwriting + - If True, overwrites the file if it already exists. + - If False (default), raises a `FileExistsError` if the file exists. Returns ------- BytesIO @@ -2356,15 +2364,20 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, """ file_content = self.dsc_model.get_custom_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) + metadata_key_name=metadata_key_name) artifact_file_path = os.path.join( target_dir, f"{metadata_key_name}" ) + + if not override and os.path.exists(artifact_file_path): + raise FileExistsError(f"File already exists: {artifact_file_path}") + with open(artifact_file_path, "wb") as _file: _file.write(file_content) - print(f"Artifact downloaded to location - {artifact_file_path}") + logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, + override: bool = False) -> None: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -2375,6 +2388,12 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, metadata_key_name: str The name of the model metadatum in the metadata. + target_dir: str + The local file path where downloaded model defined metadata artifact saved. + override: bool + A boolean flag that controls downloaded metadata artifact file overwriting + - If True, overwrites the file if it already exists. + - If False (default), raises a `FileExistsError` if the file exists. Returns ------- BytesIO @@ -2382,15 +2401,19 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, """ file_content = self.dsc_model.get_defined_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) + metadata_key_name=metadata_key_name) artifact_file_path = os.path.join( target_dir, f"{metadata_key_name}" ) + + if not override and os.path.exists(artifact_file_path): + raise FileExistsError(f"File already exists: {artifact_file_path}") + with open(artifact_file_path, "wb") as _file: _file.write(file_content) - print(f"Artifact downloaded to location - {artifact_file_path}") + logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters @@ -2418,7 +2441,7 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ return self.dsc_model.delete_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index 6c0c7f5bb..82dce320a 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -14,7 +14,7 @@ import oci.data_science from ads.common.utils import batch_convert_case, get_value, snake_to_camel -from ads.config import COMPARTMENT_OCID, OCI_REGION_METADATA, PROJECT_OCID +from ads.config import COMPARTMENT_OCID, OCI_REGION_METADATA, PROJECT_OCID, USER from ads.jobs.builders.base import Builder from ads.model.datascience_model import DataScienceModel from ads.model.service.oci_datascience_model_version_set import ( @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, category: str = "USER", **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: str = USER, **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index d1e746130..5bbc1728a 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -6,16 +6,20 @@ import logging import time +from dataclasses import dataclass from functools import wraps from io import BytesIO -from typing import Callable, Dict, List, Optional +from typing import Callable, Dict, List, Optional, Union import oci.data_science +from requests.structures import CaseInsensitiveDict + from ads.common import utils from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_datascience import OCIDataScienceMixin from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource +from ads.common.serializer import DataClassSerializable from ads.common.utils import extract_region from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment @@ -59,6 +63,13 @@ class ModelWithActiveDeploymentError(Exception): # pragma: no cover class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover pass +@dataclass(repr=False) +class ModelMetadataArtifactDetails(DataClassSerializable): + """Represents a details of Model Metadata .""" + + headers: Union[Dict,CaseInsensitiveDict] + status: str + def check_for_model_id(msg: str = MODEL_NEEDS_TO_BE_SAVED): """The decorator helping to check if the ID attribute sepcified for a datascience model. @@ -92,10 +103,8 @@ def wrapper(self, *args, **kwargs): return decorator -def convert_response_to_dict(headers:dict,status:int): - response_dict: dict = headers - response_dict['status'] = str(status) - return response_dict +def convert_model_metadata_response(headers: Union[Dict,CaseInsensitiveDict], status: int) -> ModelMetadataArtifactDetails: + return ModelMetadataArtifactDetails(headers=headers, status=str(status)) class OCIDataScienceModel( @@ -606,7 +615,7 @@ def is_model_by_reference(self): return True return False - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters @@ -644,10 +653,10 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers,response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters @@ -681,10 +690,10 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s contents = f.read() response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers,response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters @@ -719,11 +728,11 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s contents = f.read() response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters @@ -759,7 +768,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: @@ -783,6 +792,7 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: + logger.error(f"The metadata with keyname - {metadata_key_name} not found") raise ModelMetadataArtifactNotFoundError() @@ -807,10 +817,11 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: + logger.error(f"The metadata with keyname - {metadata_key_name} not found") raise ModelMetadataArtifactNotFoundError() - def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Gets custom metadata artifact metadata for specified model metadata key. Parameters @@ -839,10 +850,10 @@ def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) """ response = self.client.head_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Gets defined metadata artifact metadata for specified model metadata key. Parameters @@ -871,10 +882,10 @@ def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str """ response = self.client.head_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters @@ -901,10 +912,10 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ response = self.client.delete_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters @@ -931,5 +942,5 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s """ response = self.client.delete_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 9db40aadc..c17a39eac 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -501,7 +501,7 @@ def test_create_custom_metadata_artifact(self, mock_client): mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", "./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_defined_metadata_artifact(self, mock_client): @@ -509,7 +509,7 @@ def test_update_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_defined_metadatum_artifact.return_value = response data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_custom_metadata_artifact(self, mock_client): @@ -517,7 +517,7 @@ def test_update_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_custom_metadatum_artifact.return_value = response data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_delete_defined_metadata_artifact(self, mock_client): @@ -525,7 +525,7 @@ def test_delete_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.delete_model_defined_metadatum_artifact.return_value = response data = self.mock_model.delete_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_delete_custom_metadata_artifact(self, mock_client): @@ -533,7 +533,7 @@ def test_delete_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204,data=None,request=None) mock_client.delete_model_custom_metadatum_artifact.return_value = response data = self.mock_model.delete_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_get_custom_metadata_artifact(self, mock_client): From 3dd7b859eed459fa888adf95d394cfcd640b79ae Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 16:26:41 +0530 Subject: [PATCH 05/44] fix unit test --- tests/unitary/default_setup/model/test_oci_datascience_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index c17a39eac..998bfe1b9 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -492,7 +492,7 @@ def test_create_defined_metadata_artifact(self, mock_client): mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", "./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_create_custom_metadata_artifact(self, mock_client): From 0aa0df0c670659580a66ae4cc95d438395190acc Mon Sep 17 00:00:00 2001 From: rvikas Date: Fri, 10 Jan 2025 13:00:42 +0530 Subject: [PATCH 06/44] addressed review comments --- ads/model/service/oci_datascience_model.py | 48 ++++++++++++++----- .../model/test_oci_datascience_model.py | 27 +++++++++-- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 5bbc1728a..cd1c281f5 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -61,6 +61,10 @@ class ModelWithActiveDeploymentError(Exception): # pragma: no cover pass class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover + def __init__(self, model_ocid, metadata_key: str): + super().__init__( + f"The model {model_ocid} does not contain the metadata with key {metadata_key}." + ) pass @dataclass(repr=False) @@ -631,7 +635,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The model custom metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact creation info. Example: { @@ -646,9 +650,13 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() - print(contents) + logger.info(f"The metadata artifact content - {contents}") response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' @@ -672,7 +680,8 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The model custom metadata artifact path to be upload. Returns ------- - The model defined metadata artifact creation info. + ModelMetadataArtifactDetails + The model defined metadata artifact creation info. Example: { 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', @@ -686,8 +695,13 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The metadata artifact content - {contents}") response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') response_data = convert_model_metadata_response(response.headers, response.status) @@ -709,7 +723,7 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The model defined metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact update info. Example: { @@ -724,8 +738,13 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') response_data = convert_model_metadata_response(response.headers, response.status) @@ -748,7 +767,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The model custom metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact update info. Example: { @@ -763,8 +782,13 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') @@ -792,8 +816,7 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: - logger.error(f"The metadata with keyname - {metadata_key_name} not found") - raise ModelMetadataArtifactNotFoundError() + raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: @@ -817,8 +840,7 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: - logger.error(f"The metadata with keyname - {metadata_key_name} not found") - raise ModelMetadataArtifactNotFoundError() + raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: @@ -834,7 +856,7 @@ def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact head call info. Example: { @@ -866,7 +888,7 @@ def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact head call info. Example: { @@ -898,7 +920,7 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact delete call info. Example: { @@ -928,7 +950,7 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact delete call info. Example: { diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 998bfe1b9..7c974e8d4 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -2,7 +2,8 @@ # Copyright (c) 2022, 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 logging +import os from unittest.mock import MagicMock, patch, call, PropertyMock import pytest @@ -91,6 +92,7 @@ def setup_class(cls): cls.mock_delete_model_response = Response( data=None, status=None, headers=None, request=None ) + cls.curr_dir = os.path.dirname(os.path.abspath(__file__)) # Mock create/update model response cls.mock_create_model_response = Response( @@ -488,35 +490,50 @@ def test_is_model_by_reference(self): @patch.object(OCIDataScienceModel, "client") def test_create_defined_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", - "./test_files/metadata_test_artifact_test.json") + self.mock_artifact_file_path) assert data.status == '204' + @patch.object(OCIDataScienceModel, "client") def test_create_custom_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", - "./test_files/metadata_test_artifact_test.json") + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_defined_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_defined_metadatum_artifact.return_value = response - data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_custom_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_custom_metadatum_artifact.return_value = response - data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name", + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") From 663e7b211aa1774d7f8826d0b96b6a094f8d256d Mon Sep 17 00:00:00 2001 From: rvikas Date: Fri, 10 Jan 2025 13:19:47 +0530 Subject: [PATCH 07/44] fix unit test erro --- tests/unitary/default_setup/model/test_datascience_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index f331576db..94563e2a6 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -385,6 +385,7 @@ def test_list(self, mock_list_resource, mock__update_from_oci_dsc_model): mock_list_resource.assert_called_with( "test_compartment_id", project_id="test_project_id", + category="USER", **{"extra_tag": "test_cvalue"}, ) assert len(result) == 1 @@ -419,6 +420,7 @@ def test_list_df(self, mock_list_resource): mock_list_resource.assert_called_with( "test_compartment_id", project_id="test_project_id", + category='USER', **{"extra_tag": "test_cvalue"}, ) assert expected_result.equals(result) From 8f7da9ea56450b75001e654f6e452d913532c24b Mon Sep 17 00:00:00 2001 From: rvikas Date: Mon, 2 Dec 2024 19:37:34 +0530 Subject: [PATCH 08/44] Added methods for aqua-model store apis change --- ads/model/datascience_model.py | 260 ++++++++++++- ads/model/model_version_set.py | 4 +- ads/model/service/oci_datascience_model.py | 341 +++++++++++++++++- .../metadata_test_artifact_test.json | 4 + .../model/test_oci_datascience_model.py | 67 ++++ 5 files changed, 668 insertions(+), 8 deletions(-) create mode 100644 tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 4a2f209c4..c248233dc 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -10,8 +10,10 @@ import os import shutil import tempfile +import uuid from copy import deepcopy from typing import Dict, List, Optional, Tuple, Union +from zipfile import ZipFile import pandas import yaml @@ -1571,7 +1573,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None, **kwargs + cls, compartment_id: str = None, project_id: str = None,category: str = "USER", **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1581,6 +1583,8 @@ def list( The compartment OCID. project_id: (str, optional). Defaults to `None`. The project OCID. + category: (str, optional). Defaults to `USER`. + The category of Model. kwargs Additional keyword arguments for filtering models. @@ -1592,13 +1596,13 @@ def list( return [ cls()._update_from_oci_dsc_model(model) for model in OCIDataScienceModel.list_resource( - compartment_id, project_id=project_id, **kwargs + compartment_id, project_id=project_id, category=category, **kwargs ) ] @classmethod def list_df( - cls, compartment_id: str = None, project_id: str = None, **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs ) -> "pandas.DataFrame": """Lists datascience models in a given compartment. @@ -1608,6 +1612,8 @@ def list_df( The compartment OCID. project_id: (str, optional). Defaults to `None`. The project OCID. + category: (str, optional). Defaults to `None`. + The category of Model. kwargs Additional keyword arguments for filtering models. @@ -1618,7 +1624,7 @@ def list_df( """ records = [] for model in OCIDataScienceModel.list_resource( - compartment_id, project_id=project_id, **kwargs + compartment_id, project_id=project_id, category=category, **kwargs ): records.append( { @@ -2193,3 +2199,249 @@ def find_model_idx(): else: # model found case self.model_file_description["models"].pop(modelSearchIdx) + + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.create_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + The model defined metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.create_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.update_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model defined metadata artifact path to be upload. + Returns + ------- + Dict + The model defined metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.update_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, + artifact_path=artifact_path) + + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + """Downloads model custom metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + custom metadata artifact content + + """ + file_content = self.dsc_model.get_custom_metadata_artifact(model_ocid=model_ocid, + metadata_key_name=metadata_key_name) + artifact_file_path = os.path.join( + target_dir, f"{metadata_key_name}" + ) + with open(artifact_file_path, "wb") as _file: + _file.write(file_content) + print(f"Artifact downloaded to location - {artifact_file_path}") + + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + """Downloads model defined metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + Defined metadata artifact content + + """ + file_content = self.dsc_model.get_defined_metadata_artifact(model_ocid=model_ocid, + metadata_key_name=metadata_key_name) + artifact_file_path = os.path.join( + target_dir, f"{metadata_key_name}" + ) + with open(artifact_file_path, "wb") as _file: + _file.write(file_content) + print(f"Artifact downloaded to location - {artifact_file_path}") + + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model custom metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.delete_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) + + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model defined metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + return self.dsc_model.delete_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index 459a75635..ba2a5f290 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. @@ -473,7 +473,7 @@ def list(cls, compartment_id: str = None, **kwargs) -> List["ModelVersionSet"]: return [ cls.from_dsc_model_version_set(model_version_set) for model_version_set in DataScienceModelVersionSet.list_resource( - compartment_id, **kwargs + compartment_id, category, **kwargs ) ] diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 44ba091a6..d1e746130 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -56,6 +56,9 @@ class ModelNotSavedError(Exception): # pragma: no cover class ModelWithActiveDeploymentError(Exception): # pragma: no cover pass +class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover + pass + def check_for_model_id(msg: str = MODEL_NEEDS_TO_BE_SAVED): """The decorator helping to check if the ID attribute sepcified for a datascience model. @@ -89,6 +92,12 @@ def wrapper(self, *args, **kwargs): return decorator +def convert_response_to_dict(headers:dict,status:int): + response_dict: dict = headers + response_dict['status'] = str(status) + return response_dict + + class OCIDataScienceModel( OCIDataScienceMixin, OCIWorkRequestMixin, @@ -184,7 +193,7 @@ def create(self) -> "OCIDataScienceModel": msg="Model needs to be saved to the Model Catalog before the provenance metadata can be created." ) def create_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Creates model provenance metadata. @@ -204,7 +213,7 @@ def create_model_provenance( msg="Model needs to be saved to the Model Catalog before the provenance metadata can be updated." ) def update_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Updates model provenance metadata. @@ -596,3 +605,331 @@ def is_model_by_reference(self): ): return True return False + + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + print(contents) + + response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form' + '-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers,response.status) + return response_data + + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Creates model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + The model defined metadata artifact creation info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers,response.status) + return response_data + + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model defined metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model defined metadata artifact path to be upload. + Returns + ------- + Dict + The model defined metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + """Update model custom metadata artifact for specified model. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + + artifact_path: str + The model custom metadata artifact path to be upload. + Returns + ------- + Dict + The model custom metadata artifact update info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + with open(artifact_path, 'rb') as f: + contents = f.read() + response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, + content_disposition='form' + '-data; name="file"; filename="readme.*"') + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + """Downloads model custom metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + custom metadata artifact content + + """ + try: + return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + except ServiceError as ex: + if ex.status == 404: + raise ModelMetadataArtifactNotFoundError() + + + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + """Downloads model defined metadata artifact content for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + BytesIO + Defined metadata artifact content + + """ + try: + return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + except ServiceError as ex: + if ex.status == 404: + raise ModelMetadataArtifactNotFoundError() + + + def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Gets custom metadata artifact metadata for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact head call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.head_model_custom_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Gets defined metadata artifact metadata for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact head call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'ETag': '77156317-8bb9-4c4a-882b-0d85f8140d93', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '4029958', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.head_model_defined_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model custom metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model custom metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.delete_model_custom_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data + + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + """Deletes model defined metadata artifact for specified model metadata key. + + Parameters + ---------- + model_ocid: str + The `OCID`__ of the model. + __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm + + metadata_key_name: str + The name of the model metadatum in the metadata. + Returns + ------- + Dict + The model defined metadata artifact delete call info. + Example: + { + 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', + 'opc-request-id': 'E4F7', + 'X-Content-Type-Options': 'nosniff', + 'Vary': 'Origin', + 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', + 'status': 204 + } + + """ + response = self.client.delete_model_defined_metadatum_artifact(model_ocid, metadata_key_name) + response_data = convert_response_to_dict(response.headers, response.status) + return response_data diff --git a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json new file mode 100644 index 000000000..48bc203df --- /dev/null +++ b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json @@ -0,0 +1,4 @@ +{ + "type": "metadata_test_artifact_file", + "version": "1.0" +} diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 7a44019e1..9db40aadc 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -484,3 +484,70 @@ def test_is_model_by_reference(self): self.mock_model.custom_metadata_list = [metadata_item] assert self.mock_model.is_model_by_reference() + + @patch.object(OCIDataScienceModel, "client") + def test_create_defined_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.create_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + "./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_create_custom_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.create_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + "./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_update_defined_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.update_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_update_custom_metadata_artifact(self, mock_client): + """Tests create defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.update_model_custom_metadatum_artifact.return_value = response + data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_delete_defined_metadata_artifact(self, mock_client): + """Tests delete defined metadata artifact for specified model.""" + response = Response(headers={}, status=204, data=None, request=None) + mock_client.delete_model_defined_metadatum_artifact.return_value = response + data = self.mock_model.delete_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_delete_custom_metadata_artifact(self, mock_client): + """Tests delete defined metadata artifact for specified model.""" + response = Response(headers={}, status=204,data=None,request=None) + mock_client.delete_model_custom_metadatum_artifact.return_value = response + data = self.mock_model.delete_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data['status'] == '204' + + @patch.object(OCIDataScienceModel, "client") + def test_get_custom_metadata_artifact(self, mock_client): + """Tests gets defined metadata artifact for specified model.""" + mock_client.get_model_custom_metadatum_artifact_content.return_value.data.content = b"some file" + data = self.mock_model.get_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data == b"some file" + + @patch.object(OCIDataScienceModel, "client") + def test_get_defined_metadata_artifact(self, mock_client): + """Tests gets defined metadata artifact for specified model.""" + mock_client.get_model_defined_metadatum_artifact_content.return_value.data.content = b"some file" + data = self.mock_model.get_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") + assert data == b"some file" + + + From 9207ccec15bfab43bb32477fb4ed3f82cd63aa6e Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 5 Dec 2024 12:11:05 +0530 Subject: [PATCH 09/44] update modela and mvs list api --- ads/model/datascience_model.py | 4 ++-- ads/model/model_version_set.py | 6 ++++-- tests/unitary/default_setup/model/test_model_version_set.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index c248233dc..6d14f15f1 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1573,7 +1573,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None,category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1584,7 +1584,7 @@ def list( project_id: (str, optional). Defaults to `None`. The project OCID. category: (str, optional). Defaults to `USER`. - The category of Model. + The category of Model. Allowed values are: "USER", "SERVICE" kwargs Additional keyword arguments for filtering models. diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index ba2a5f290..6c0c7f5bb 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: str = "USER", **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. @@ -462,6 +462,8 @@ def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kw ---------- compartment_id: str The OCID of compartment. + category: (str, optional). Defaults to `USER`. + The category of Model. Allowed values are: "USER", "SERVICE" kwargs Additional keyword arguments for filtering model version sets. @@ -473,7 +475,7 @@ def list(cls, compartment_id: str = None, category: Optional[str] = "USER", **kw return [ cls.from_dsc_model_version_set(model_version_set) for model_version_set in DataScienceModelVersionSet.list_resource( - compartment_id, category, **kwargs + compartment_id, category=category, **kwargs ) ] diff --git a/tests/unitary/default_setup/model/test_model_version_set.py b/tests/unitary/default_setup/model/test_model_version_set.py index ea9cea19a..a452d753c 100644 --- a/tests/unitary/default_setup/model/test_model_version_set.py +++ b/tests/unitary/default_setup/model/test_model_version_set.py @@ -322,7 +322,7 @@ def test_list(self): test_result = mvs.list(compartment_id="test_compartment_id") for i, item in enumerate(test_result): assert item.to_dict() == expected_result[i].to_dict() - mock_list_resource.assert_called_with("test_compartment_id") + mock_list_resource.assert_called_with("test_compartment_id", category="USER") @patch.object(DataScienceModel, "list") def test_models(self, mock_list_models): From 32d5695d092b8d6a2f4e4a9a54bea3c3aed4d622 Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 14:54:20 +0530 Subject: [PATCH 10/44] update the test file --- .../model/test_files/metadata_test_artifact_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json index 48bc203df..1f2b1b954 100644 --- a/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json +++ b/tests/unitary/default_setup/model/test_files/metadata_test_artifact_test.json @@ -1,4 +1,4 @@ { - "type": "metadata_test_artifact_file", + "type": "metadata_test_artifact_files", "version": "1.0" } From 446df190561fac819add78583aaedfca16cce12c Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 16:02:02 +0530 Subject: [PATCH 11/44] addressed review comments --- ads/config.py | 3 + ads/model/datascience_model.py | 69 ++++++++++++------- ads/model/model_version_set.py | 4 +- ads/model/service/oci_datascience_model.py | 53 ++++++++------ .../model/test_oci_datascience_model.py | 10 +-- 5 files changed, 88 insertions(+), 51 deletions(-) diff --git a/ads/config.py b/ads/config.py index cd4ee2b07..53342988f 100644 --- a/ads/config.py +++ b/ads/config.py @@ -80,6 +80,9 @@ DEBUG_TELEMETRY = os.environ.get("DEBUG_TELEMETRY", None) AQUA_SERVICE_NAME = "aqua" DATA_SCIENCE_SERVICE_NAME = "data-science" +USER = "USER" +SERVICE = "SERVICE" + THREADED_DEFAULT_TIMEOUT = 5 diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 6d14f15f1..fe40fcd67 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8; -*- # Copyright (c) 2022, 2024 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -10,10 +9,8 @@ import os import shutil import tempfile -import uuid from copy import deepcopy from typing import Dict, List, Optional, Tuple, Union -from zipfile import ZipFile import pandas import yaml @@ -23,10 +20,13 @@ from ads.common import utils from ads.common.extended_enum import ExtendedEnumMeta from ads.common.object_storage_details import ObjectStorageDetails +from ads.config import ( + AQUA_SERVICE_MODELS_BUCKET as SERVICE_MODELS_BUCKET, +) from ads.config import ( COMPARTMENT_OCID, PROJECT_OCID, - AQUA_SERVICE_MODELS_BUCKET as SERVICE_MODELS_BUCKET, + USER, ) from ads.feature_engineering.schema import Schema from ads.jobs.builders.base import Builder @@ -43,6 +43,7 @@ ModelTaxonomyMetadata, ) from ads.model.service.oci_datascience_model import ( + ModelMetadataArtifactDetails, ModelProvenanceNotFoundError, OCIDataScienceModel, ) @@ -1044,7 +1045,7 @@ def with_model_file_description( elif json_string: json_data = json.loads(json_string) elif json_uri: - with open(json_uri, "r") as json_file: + with open(json_uri) as json_file: json_data = json.load(json_file) else: raise ValueError("Must provide either a valid json string or URI location.") @@ -1573,7 +1574,7 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1602,7 +1603,7 @@ def list( @classmethod def list_df( - cls, compartment_id: str = None, project_id: str = None, category: str = "USER", **kwargs + cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs ) -> "pandas.DataFrame": """Lists datascience models in a given compartment. @@ -2200,7 +2201,7 @@ def find_model_idx(): # model found case self.model_file_description["models"].pop(modelSearchIdx) - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters @@ -2213,7 +2214,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2235,7 +2236,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st artifact_path=artifact_path) - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters @@ -2248,7 +2249,7 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model defined metadata artifact local file path to be upload. Returns ------- The model defined metadata artifact creation info. @@ -2269,7 +2270,7 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s artifact_path=artifact_path) - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters @@ -2282,7 +2283,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. artifact_path: str - The model custom metadata artifact path to be upload. + The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2304,7 +2305,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st artifact_path=artifact_path) - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters @@ -2317,7 +2318,7 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. artifact_path: str - The model defined metadata artifact path to be upload. + The model defined metadata artifact local file path to be upload. Returns ------- Dict @@ -2338,7 +2339,8 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s return self.dsc_model.update_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, artifact_path=artifact_path) - def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, + override: bool = False) -> None: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -2349,6 +2351,12 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, metadata_key_name: str The name of the model metadatum in the metadata. + target_dir: str + The local file path where downloaded model custom metadata artifact saved. + override: bool + A boolean flag that controls downloaded metadata artifact file overwriting + - If True, overwrites the file if it already exists. + - If False (default), raises a `FileExistsError` if the file exists. Returns ------- BytesIO @@ -2356,15 +2364,20 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, """ file_content = self.dsc_model.get_custom_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) + metadata_key_name=metadata_key_name) artifact_file_path = os.path.join( target_dir, f"{metadata_key_name}" ) + + if not override and os.path.exists(artifact_file_path): + raise FileExistsError(f"File already exists: {artifact_file_path}") + with open(artifact_file_path, "wb") as _file: _file.write(file_content) - print(f"Artifact downloaded to location - {artifact_file_path}") + logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str) -> None: + def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, + override: bool = False) -> None: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -2375,6 +2388,12 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, metadata_key_name: str The name of the model metadatum in the metadata. + target_dir: str + The local file path where downloaded model defined metadata artifact saved. + override: bool + A boolean flag that controls downloaded metadata artifact file overwriting + - If True, overwrites the file if it already exists. + - If False (default), raises a `FileExistsError` if the file exists. Returns ------- BytesIO @@ -2382,15 +2401,19 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, """ file_content = self.dsc_model.get_defined_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) + metadata_key_name=metadata_key_name) artifact_file_path = os.path.join( target_dir, f"{metadata_key_name}" ) + + if not override and os.path.exists(artifact_file_path): + raise FileExistsError(f"File already exists: {artifact_file_path}") + with open(artifact_file_path, "wb") as _file: _file.write(file_content) - print(f"Artifact downloaded to location - {artifact_file_path}") + logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters @@ -2418,7 +2441,7 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ return self.dsc_model.delete_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters diff --git a/ads/model/model_version_set.py b/ads/model/model_version_set.py index 6c0c7f5bb..82dce320a 100644 --- a/ads/model/model_version_set.py +++ b/ads/model/model_version_set.py @@ -14,7 +14,7 @@ import oci.data_science from ads.common.utils import batch_convert_case, get_value, snake_to_camel -from ads.config import COMPARTMENT_OCID, OCI_REGION_METADATA, PROJECT_OCID +from ads.config import COMPARTMENT_OCID, OCI_REGION_METADATA, PROJECT_OCID, USER from ads.jobs.builders.base import Builder from ads.model.datascience_model import DataScienceModel from ads.model.service.oci_datascience_model_version_set import ( @@ -454,7 +454,7 @@ def kind(self) -> str: return "modelVersionSet" @classmethod - def list(cls, compartment_id: str = None, category: str = "USER", **kwargs) -> List["ModelVersionSet"]: + def list(cls, compartment_id: str = None, category: str = USER, **kwargs) -> List["ModelVersionSet"]: """ List model version sets in a given compartment. diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index d1e746130..5bbc1728a 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -6,16 +6,20 @@ import logging import time +from dataclasses import dataclass from functools import wraps from io import BytesIO -from typing import Callable, Dict, List, Optional +from typing import Callable, Dict, List, Optional, Union import oci.data_science +from requests.structures import CaseInsensitiveDict + from ads.common import utils from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_datascience import OCIDataScienceMixin from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource +from ads.common.serializer import DataClassSerializable from ads.common.utils import extract_region from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment @@ -59,6 +63,13 @@ class ModelWithActiveDeploymentError(Exception): # pragma: no cover class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover pass +@dataclass(repr=False) +class ModelMetadataArtifactDetails(DataClassSerializable): + """Represents a details of Model Metadata .""" + + headers: Union[Dict,CaseInsensitiveDict] + status: str + def check_for_model_id(msg: str = MODEL_NEEDS_TO_BE_SAVED): """The decorator helping to check if the ID attribute sepcified for a datascience model. @@ -92,10 +103,8 @@ def wrapper(self, *args, **kwargs): return decorator -def convert_response_to_dict(headers:dict,status:int): - response_dict: dict = headers - response_dict['status'] = str(status) - return response_dict +def convert_model_metadata_response(headers: Union[Dict,CaseInsensitiveDict], status: int) -> ModelMetadataArtifactDetails: + return ModelMetadataArtifactDetails(headers=headers, status=str(status)) class OCIDataScienceModel( @@ -606,7 +615,7 @@ def is_model_by_reference(self): return True return False - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters @@ -644,10 +653,10 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers,response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters @@ -681,10 +690,10 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s contents = f.read() response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers,response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters @@ -719,11 +728,11 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s contents = f.read() response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> dict: + def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters @@ -759,7 +768,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: @@ -783,6 +792,7 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: + logger.error(f"The metadata with keyname - {metadata_key_name} not found") raise ModelMetadataArtifactNotFoundError() @@ -807,10 +817,11 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: + logger.error(f"The metadata with keyname - {metadata_key_name} not found") raise ModelMetadataArtifactNotFoundError() - def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Gets custom metadata artifact metadata for specified model metadata key. Parameters @@ -839,10 +850,10 @@ def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) """ response = self.client.head_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Gets defined metadata artifact metadata for specified model metadata key. Parameters @@ -871,10 +882,10 @@ def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str """ response = self.client.head_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters @@ -901,10 +912,10 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ response = self.client.delete_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> dict: + def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters @@ -931,5 +942,5 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s """ response = self.client.delete_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_response_to_dict(response.headers, response.status) + response_data = convert_model_metadata_response(response.headers, response.status) return response_data diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 9db40aadc..c17a39eac 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -501,7 +501,7 @@ def test_create_custom_metadata_artifact(self, mock_client): mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", "./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_defined_metadata_artifact(self, mock_client): @@ -509,7 +509,7 @@ def test_update_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_defined_metadatum_artifact.return_value = response data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_custom_metadata_artifact(self, mock_client): @@ -517,7 +517,7 @@ def test_update_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_custom_metadatum_artifact.return_value = response data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_delete_defined_metadata_artifact(self, mock_client): @@ -525,7 +525,7 @@ def test_delete_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.delete_model_defined_metadatum_artifact.return_value = response data = self.mock_model.delete_defined_metadata_artifact("MODEL_OCID", "metadata_key_name") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_delete_custom_metadata_artifact(self, mock_client): @@ -533,7 +533,7 @@ def test_delete_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204,data=None,request=None) mock_client.delete_model_custom_metadatum_artifact.return_value = response data = self.mock_model.delete_custom_metadata_artifact("MODEL_OCID", "metadata_key_name") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_get_custom_metadata_artifact(self, mock_client): From 0ea9c9deeb3d82453f93d09460c0421cefcae3fe Mon Sep 17 00:00:00 2001 From: rvikas Date: Thu, 9 Jan 2025 16:26:41 +0530 Subject: [PATCH 12/44] fix unit test --- tests/unitary/default_setup/model/test_oci_datascience_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index c17a39eac..998bfe1b9 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -492,7 +492,7 @@ def test_create_defined_metadata_artifact(self, mock_client): mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", "./test_files/metadata_test_artifact_test.json") - assert data['status'] == '204' + assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_create_custom_metadata_artifact(self, mock_client): From 44e04bddb7ce0a4e575bb5454bf1a9fa8dc61c6a Mon Sep 17 00:00:00 2001 From: rvikas Date: Fri, 10 Jan 2025 13:00:42 +0530 Subject: [PATCH 13/44] addressed review comments --- ads/model/service/oci_datascience_model.py | 48 ++++++++++++++----- .../model/test_oci_datascience_model.py | 27 +++++++++-- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 5bbc1728a..cd1c281f5 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -61,6 +61,10 @@ class ModelWithActiveDeploymentError(Exception): # pragma: no cover pass class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover + def __init__(self, model_ocid, metadata_key: str): + super().__init__( + f"The model {model_ocid} does not contain the metadata with key {metadata_key}." + ) pass @dataclass(repr=False) @@ -631,7 +635,7 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The model custom metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact creation info. Example: { @@ -646,9 +650,13 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() - print(contents) + logger.info(f"The metadata artifact content - {contents}") response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' @@ -672,7 +680,8 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The model custom metadata artifact path to be upload. Returns ------- - The model defined metadata artifact creation info. + ModelMetadataArtifactDetails + The model defined metadata artifact creation info. Example: { 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', @@ -686,8 +695,13 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The metadata artifact content - {contents}") response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') response_data = convert_model_metadata_response(response.headers, response.status) @@ -709,7 +723,7 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The model defined metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact update info. Example: { @@ -724,8 +738,13 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form-data; name="file"; filename="readme.*"') response_data = convert_model_metadata_response(response.headers, response.status) @@ -748,7 +767,7 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The model custom metadata artifact path to be upload. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact update info. Example: { @@ -763,8 +782,13 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ + if not utils.is_path_exists(artifact_path): + raise FileNotFoundError( + f"File not found: {artifact_path} . " + ) with open(artifact_path, 'rb') as f: contents = f.read() + logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, content_disposition='form' '-data; name="file"; filename="readme.*"') @@ -792,8 +816,7 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: - logger.error(f"The metadata with keyname - {metadata_key_name} not found") - raise ModelMetadataArtifactNotFoundError() + raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: @@ -817,8 +840,7 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content except ServiceError as ex: if ex.status == 404: - logger.error(f"The metadata with keyname - {metadata_key_name} not found") - raise ModelMetadataArtifactNotFoundError() + raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: @@ -834,7 +856,7 @@ def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact head call info. Example: { @@ -866,7 +888,7 @@ def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact head call info. Example: { @@ -898,7 +920,7 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact delete call info. Example: { @@ -928,7 +950,7 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact delete call info. Example: { diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 998bfe1b9..7c974e8d4 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -2,7 +2,8 @@ # Copyright (c) 2022, 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 logging +import os from unittest.mock import MagicMock, patch, call, PropertyMock import pytest @@ -91,6 +92,7 @@ def setup_class(cls): cls.mock_delete_model_response = Response( data=None, status=None, headers=None, request=None ) + cls.curr_dir = os.path.dirname(os.path.abspath(__file__)) # Mock create/update model response cls.mock_create_model_response = Response( @@ -488,35 +490,50 @@ def test_is_model_by_reference(self): @patch.object(OCIDataScienceModel, "client") def test_create_defined_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", - "./test_files/metadata_test_artifact_test.json") + self.mock_artifact_file_path) assert data.status == '204' + @patch.object(OCIDataScienceModel, "client") def test_create_custom_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", - "./test_files/metadata_test_artifact_test.json") + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_defined_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_defined_metadatum_artifact.return_value = response - data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + data = self.mock_model.update_defined_metadata_artifact("MODEL_OCID", "metadata_key_name", + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") def test_update_custom_metadata_artifact(self, mock_client): """Tests create defined metadata artifact for specified model.""" + self.mock_artifact_file_path = os.path.join( + self.curr_dir, "test_files/metadata_test_artifact_test.json" + ) response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_custom_metadatum_artifact.return_value = response - data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name","./test_files/metadata_test_artifact_test.json") + data = self.mock_model.update_custom_metadata_artifact("MODEL_OCID", "metadata_key_name", + self.mock_artifact_file_path) assert data.status == '204' @patch.object(OCIDataScienceModel, "client") From bae144e2cb81fc1bf0dcdffd7ed1ef6ad1ae6dd8 Mon Sep 17 00:00:00 2001 From: rvikas Date: Fri, 10 Jan 2025 13:19:47 +0530 Subject: [PATCH 14/44] fix unit test erro --- tests/unitary/default_setup/model/test_datascience_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index f331576db..94563e2a6 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -385,6 +385,7 @@ def test_list(self, mock_list_resource, mock__update_from_oci_dsc_model): mock_list_resource.assert_called_with( "test_compartment_id", project_id="test_project_id", + category="USER", **{"extra_tag": "test_cvalue"}, ) assert len(result) == 1 @@ -419,6 +420,7 @@ def test_list_df(self, mock_list_resource): mock_list_resource.assert_called_with( "test_compartment_id", project_id="test_project_id", + category='USER', **{"extra_tag": "test_cvalue"}, ) assert expected_result.equals(result) From 19c98cf7e661453e71a02192582bb02cd26311ab Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Feb 2025 23:10:31 +0530 Subject: [PATCH 15/44] DataScience.from_id() for ms enchancements changes --- ads/model/model_metadata.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 2667b82ad..51420e8ff 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*-- # Copyright (c) 2021, 2024 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -11,20 +10,21 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field, fields from pathlib import Path -from typing import Dict, List, Tuple, Union, Optional, Any +from typing import Any, Dict, List, Optional, Tuple, Union -import ads.dataset.factory as factory import fsspec import git import oci.data_science.models import pandas as pd import yaml +from oci.util import to_dict + from ads.common import logger from ads.common.error import ChangesNotCommitted from ads.common.extended_enum import ExtendedEnumMeta -from ads.common.serializer import DataClassSerializable from ads.common.object_storage_details import ObjectStorageDetails -from oci.util import to_dict +from ads.common.serializer import DataClassSerializable +from ads.dataset import factory try: from yaml import CDumper as dumper @@ -100,6 +100,8 @@ class MetadataTaxonomyKeys(str, metaclass=ExtendedEnumMeta): ALGORITHM = "Algorithm" HYPERPARAMETERS = "Hyperparameters" ARTIFACT_TEST_RESULT = "ArtifactTestResults" + README = "readme" + LICENSE = "license" class MetadataCustomKeys(str, metaclass=ExtendedEnumMeta): @@ -1398,7 +1400,7 @@ def from_dict(cls, data: Dict) -> "ModelCustomMetadata": if ( not data or not isinstance(data, Dict) - or not "data" in data + or "data" not in data or not isinstance(data["data"], List) ): raise ValueError( @@ -1550,7 +1552,7 @@ def from_dict(cls, data: Dict) -> "ModelTaxonomyMetadata": if ( not data or not isinstance(data, Dict) - or not "data" in data + or "data" not in data or not isinstance(data["data"], List) ): raise ValueError( From 2a8e4b713a8a2bd6936bfae81405374e20876f26 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Thu, 13 Feb 2025 17:42:34 +0530 Subject: [PATCH 16/44] Addressing review comments --- ads/model/datascience_model.py | 216 +++++++++------- ads/model/model_metadata.py | 2 - ads/model/service/oci_datascience_model.py | 276 ++++++++++++--------- 3 files changed, 290 insertions(+), 204 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index fe40fcd67..d31717aa9 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -119,17 +119,17 @@ class ModelBackupSetting: """ def __init__( - self, - is_backup_enabled: Optional[bool] = None, - backup_region: Optional[str] = None, - customer_notification_type: Optional[CustomerNotificationType] = None, + self, + is_backup_enabled: Optional[bool] = None, + backup_region: Optional[str] = None, + customer_notification_type: Optional[CustomerNotificationType] = None, ): self.is_backup_enabled = ( is_backup_enabled if is_backup_enabled is not None else False ) self.backup_region = backup_region self.customer_notification_type = ( - customer_notification_type or CustomerNotificationType.NONE + customer_notification_type or CustomerNotificationType.NONE ) def to_dict(self) -> Dict: @@ -149,7 +149,7 @@ def from_dict(cls, data: Dict) -> "ModelBackupSetting": customer_notification_type=CustomerNotificationType( data.get("customer_notification_type") ) - or None, + or None, ) def to_json(self) -> str: @@ -169,12 +169,15 @@ def to_yaml(self) -> str: def validate(self) -> bool: """Validates the backup settings details. Returns True if valid, False otherwise.""" - return all([ - isinstance(self.is_backup_enabled, bool), - not self.backup_region or isinstance(self.backup_region, str), - isinstance(self.customer_notification_type, str) and self.customer_notification_type in - CustomerNotificationType.values() - ]) + return all( + [ + isinstance(self.is_backup_enabled, bool), + not self.backup_region or isinstance(self.backup_region, str), + isinstance(self.customer_notification_type, str) + and self.customer_notification_type + in CustomerNotificationType.values(), + ] + ) def __repr__(self): return self.to_yaml() @@ -201,15 +204,15 @@ class ModelRetentionSetting: """ def __init__( - self, - archive_after_days: Optional[int] = None, - delete_after_days: Optional[int] = None, - customer_notification_type: Optional[CustomerNotificationType] = None, + self, + archive_after_days: Optional[int] = None, + delete_after_days: Optional[int] = None, + customer_notification_type: Optional[CustomerNotificationType] = None, ): self.archive_after_days = archive_after_days self.delete_after_days = delete_after_days self.customer_notification_type = ( - customer_notification_type or CustomerNotificationType.NONE + customer_notification_type or CustomerNotificationType.NONE ) def to_dict(self) -> Dict: @@ -229,7 +232,7 @@ def from_dict(cls, data: Dict) -> "ModelRetentionSetting": customer_notification_type=CustomerNotificationType( data.get("customer_notification_type") ) - or None, + or None, ) def to_json(self) -> str: @@ -248,13 +251,23 @@ def to_yaml(self) -> str: def validate(self) -> bool: """Validates the retention settings details. Returns True if valid, False otherwise.""" - return all([ - self.archive_after_days is None or ( - isinstance(self.archive_after_days, int) and self.archive_after_days >= 0), - self.delete_after_days is None or (isinstance(self.delete_after_days, int) and self.delete_after_days >= 0), - isinstance(self.customer_notification_type, str) and self.customer_notification_type in - CustomerNotificationType.values() - ]) + return all( + [ + self.archive_after_days is None + or ( + isinstance(self.archive_after_days, int) + and self.archive_after_days >= 0 + ), + self.delete_after_days is None + or ( + isinstance(self.delete_after_days, int) + and self.delete_after_days >= 0 + ), + isinstance(self.customer_notification_type, str) + and self.customer_notification_type + in CustomerNotificationType.values(), + ] + ) def __repr__(self): return self.to_yaml() @@ -281,13 +294,13 @@ class ModelRetentionOperationDetails: """ def __init__( - self, - archive_state: Optional[SettingStatus] = None, - archive_state_details: Optional[str] = None, - delete_state: Optional[SettingStatus] = None, - delete_state_details: Optional[str] = None, - time_archival_scheduled: Optional[int] = None, - time_deletion_scheduled: Optional[int] = None, + self, + archive_state: Optional[SettingStatus] = None, + archive_state_details: Optional[str] = None, + delete_state: Optional[SettingStatus] = None, + delete_state_details: Optional[str] = None, + time_archival_scheduled: Optional[int] = None, + time_deletion_scheduled: Optional[int] = None, ): self.archive_state = archive_state self.archive_state_details = archive_state_details @@ -337,8 +350,10 @@ def validate(self) -> bool: """Validates the retention operation details.""" return all( [ - self.archive_state is None or self.archive_state in SettingStatus.values(), - self.delete_state is None or self.delete_state in SettingStatus.values(), + self.archive_state is None + or self.archive_state in SettingStatus.values(), + self.delete_state is None + or self.delete_state in SettingStatus.values(), self.time_archival_scheduled is None or isinstance(self.time_archival_scheduled, int), self.time_deletion_scheduled is None @@ -371,10 +386,10 @@ class ModelBackupOperationDetails: """ def __init__( - self, - backup_state: Optional[SettingStatus] = None, - backup_state_details: Optional[str] = None, - time_last_backup: Optional[int] = None, + self, + backup_state: Optional[SettingStatus] = None, + backup_state_details: Optional[str] = None, + time_last_backup: Optional[int] = None, ): self.backup_state = backup_state self.backup_state_details = backup_state_details @@ -414,8 +429,14 @@ def to_yaml(self) -> str: def validate(self) -> bool: """Validates the backup operation details.""" return not ( - (self.backup_state is not None and self.backup_state not in SettingStatus.values()) or - (self.time_last_backup is not None and not isinstance(self.time_last_backup, int)) + ( + self.backup_state is not None + and self.backup_state not in SettingStatus.values() + ) + or ( + self.time_last_backup is not None + and not isinstance(self.time_last_backup, int) + ) ) def __repr__(self): @@ -1080,7 +1101,7 @@ def retention_setting(self) -> ModelRetentionSetting: return self.get_spec(self.CONST_RETENTION_SETTING) def with_retention_setting( - self, retention_setting: Union[Dict, ModelRetentionSetting] + self, retention_setting: Union[Dict, ModelRetentionSetting] ) -> "DataScienceModel": """ Sets the retention setting details for the model. @@ -1109,7 +1130,7 @@ def backup_setting(self) -> ModelBackupSetting: return self.get_spec(self.CONST_BACKUP_SETTING) def with_backup_setting( - self, backup_setting: Union[Dict, ModelBackupSetting] + self, backup_setting: Union[Dict, ModelBackupSetting] ) -> "DataScienceModel": """ Sets the model's backup setting details. @@ -1371,8 +1392,8 @@ def _remove_file_description_artifact(self): shutil.rmtree(self.local_copy_dir, ignore_errors=True) def restore_model( - self, - restore_model_for_hours_specified: Optional[int] = None, + self, + restore_model_for_hours_specified: Optional[int] = None, ) -> None: """ Restore archived model artifact. @@ -1401,8 +1422,12 @@ def restore_model( # Optional: Validate restore_model_for_hours_specified if restore_model_for_hours_specified is not None and ( - not isinstance(restore_model_for_hours_specified, int) or restore_model_for_hours_specified <= 0): - raise ValueError("restore_model_for_hours_specified must be a positive integer.") + not isinstance(restore_model_for_hours_specified, int) + or restore_model_for_hours_specified <= 0 + ): + raise ValueError( + "restore_model_for_hours_specified must be a positive integer." + ) self.dsc_model.restore_archived_model_artifact( restore_model_for_hours_specified=restore_model_for_hours_specified, @@ -1574,7 +1599,11 @@ def delete( @classmethod def list( - cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs + cls, + compartment_id: str = None, + project_id: str = None, + category: str = USER, + **kwargs, ) -> List["DataScienceModel"]: """Lists datascience models in a given compartment. @@ -1603,7 +1632,11 @@ def list( @classmethod def list_df( - cls, compartment_id: str = None, project_id: str = None, category: str = USER, **kwargs + cls, + compartment_id: str = None, + project_id: str = None, + category: str = USER, + **kwargs, ) -> "pandas.DataFrame": """Lists datascience models in a given compartment. @@ -1625,7 +1658,7 @@ def list_df( """ records = [] for model in OCIDataScienceModel.list_resource( - compartment_id, project_id=project_id, category=category, **kwargs + compartment_id, project_id=project_id, category=category, **kwargs ): records.append( { @@ -1728,7 +1761,7 @@ def _update_from_oci_dsc_model( self.CONST_BACKUP_SETTING: ModelBackupSetting.to_dict, self.CONST_RETENTION_SETTING: ModelRetentionSetting.to_dict, self.CONST_BACKUP_OPERATION_DETAILS: ModelBackupOperationDetails.to_dict, - self.CONST_RETENTION_OPERATION_DETAILS: ModelRetentionOperationDetails.to_dict + self.CONST_RETENTION_OPERATION_DETAILS: ModelRetentionOperationDetails.to_dict, } # Update the main properties @@ -2201,7 +2234,9 @@ def find_model_idx(): # model found case self.model_file_description["models"].pop(modelSearchIdx) - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + def create_custom_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters @@ -2232,19 +2267,17 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ - return self.dsc_model.create_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, - artifact_path=artifact_path) - + return self.dsc_model.create_custom_metadata_artifact( + metadata_key_name=metadata_key_name, artifact_path=artifact_path + ) - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + def create_defined_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -2266,11 +2299,13 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ - return self.dsc_model.create_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, - artifact_path=artifact_path) - + return self.dsc_model.create_defined_metadata_artifact( + metadata_key_name=metadata_key_name, artifact_path=artifact_path + ) - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + def update_custom_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters @@ -2301,11 +2336,13 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ - return self.dsc_model.update_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, - artifact_path=artifact_path) - + return self.dsc_model.update_custom_metadata_artifact( + metadata_key_name=metadata_key_name, artifact_path=artifact_path + ) - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + def update_defined_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters @@ -2336,11 +2373,13 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ - return self.dsc_model.update_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name, - artifact_path=artifact_path) + return self.dsc_model.update_defined_metadata_artifact( + metadata_key_name=metadata_key_name, artifact_path=artifact_path + ) - def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, - override: bool = False) -> None: + def get_custom_metadata_artifact( + self, metadata_key_name: str, target_dir: str, override: bool = False + ) -> None: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -2363,11 +2402,10 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, custom metadata artifact content """ - file_content = self.dsc_model.get_custom_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) - artifact_file_path = os.path.join( - target_dir, f"{metadata_key_name}" + file_content = self.dsc_model.get_custom_metadata_artifact( + metadata_key_name=metadata_key_name ) + artifact_file_path = os.path.join(target_dir, f"{metadata_key_name}") if not override and os.path.exists(artifact_file_path): raise FileExistsError(f"File already exists: {artifact_file_path}") @@ -2376,8 +2414,9 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, _file.write(file_content) logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, target_dir: str, - override: bool = False) -> None: + def get_defined_metadata_artifact( + self, metadata_key_name: str, target_dir: str, override: bool = False + ) -> None: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -2400,11 +2439,10 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, Defined metadata artifact content """ - file_content = self.dsc_model.get_defined_metadata_artifact(model_ocid=model_ocid, - metadata_key_name=metadata_key_name) - artifact_file_path = os.path.join( - target_dir, f"{metadata_key_name}" + file_content = self.dsc_model.get_defined_metadata_artifact( + metadata_key_name=metadata_key_name ) + artifact_file_path = os.path.join(target_dir, f"{metadata_key_name}") if not override and os.path.exists(artifact_file_path): raise FileExistsError(f"File already exists: {artifact_file_path}") @@ -2413,7 +2451,9 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, _file.write(file_content) logger.info(f"Artifact downloaded to location - {artifact_file_path}") - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + def delete_custom_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters @@ -2439,9 +2479,13 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ - return self.dsc_model.delete_custom_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) + return self.dsc_model.delete_custom_metadata_artifact( + metadata_key_name=metadata_key_name + ) - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + def delete_defined_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters @@ -2467,4 +2511,6 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ - return self.dsc_model.delete_defined_metadata_artifact(model_ocid=model_ocid, metadata_key_name=metadata_key_name) + return self.dsc_model.delete_defined_metadata_artifact( + metadata_key_name=metadata_key_name + ) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 51420e8ff..451a44b2a 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -100,8 +100,6 @@ class MetadataTaxonomyKeys(str, metaclass=ExtendedEnumMeta): ALGORITHM = "Algorithm" HYPERPARAMETERS = "Hyperparameters" ARTIFACT_TEST_RESULT = "ArtifactTestResults" - README = "readme" - LICENSE = "license" class MetadataCustomKeys(str, metaclass=ExtendedEnumMeta): diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index cd1c281f5..9be8d5e07 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -1,17 +1,24 @@ #!/usr/bin/env python -# -*- coding: utf-8; -*- # Copyright (c) 2022, 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 logging -import time from dataclasses import dataclass from functools import wraps from io import BytesIO from typing import Callable, Dict, List, Optional, Union import oci.data_science +from oci.data_science.models import ( + ArtifactExportDetailsObjectStorage, + ArtifactImportDetailsObjectStorage, + CreateModelDetails, + ExportModelArtifactDetails, + ImportModelArtifactDetails, + UpdateModelDetails, +) +from oci.exceptions import ServiceError from requests.structures import CaseInsensitiveDict from ads.common import utils @@ -23,16 +30,6 @@ from ads.common.utils import extract_region from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment -from oci.data_science.models import ( - ArtifactExportDetailsObjectStorage, - ArtifactImportDetailsObjectStorage, - CreateModelDetails, - ExportModelArtifactDetails, - ImportModelArtifactDetails, - UpdateModelDetails, - WorkRequest, -) -from oci.exceptions import ServiceError logger = logging.getLogger(__name__) @@ -60,18 +57,21 @@ class ModelNotSavedError(Exception): # pragma: no cover class ModelWithActiveDeploymentError(Exception): # pragma: no cover pass + class ModelMetadataArtifactNotFoundError(Exception): # pragma: no cover def __init__(self, model_ocid, metadata_key: str): super().__init__( f"The model {model_ocid} does not contain the metadata with key {metadata_key}." ) + pass + @dataclass(repr=False) class ModelMetadataArtifactDetails(DataClassSerializable): """Represents a details of Model Metadata .""" - headers: Union[Dict,CaseInsensitiveDict] + headers: Union[Dict, CaseInsensitiveDict] status: str @@ -107,7 +107,9 @@ def wrapper(self, *args, **kwargs): return decorator -def convert_model_metadata_response(headers: Union[Dict,CaseInsensitiveDict], status: int) -> ModelMetadataArtifactDetails: +def convert_model_metadata_response( + headers: Union[Dict, CaseInsensitiveDict], status: int +) -> ModelMetadataArtifactDetails: return ModelMetadataArtifactDetails(headers=headers, status=str(status)) @@ -206,7 +208,7 @@ def create(self) -> "OCIDataScienceModel": msg="Model needs to be saved to the Model Catalog before the provenance metadata can be created." ) def create_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Creates model provenance metadata. @@ -226,7 +228,7 @@ def create_model_provenance( msg="Model needs to be saved to the Model Catalog before the provenance metadata can be updated." ) def update_model_provenance( - self, model_provenance: oci.data_science.models.ModelProvenance + self, model_provenance: oci.data_science.models.ModelProvenance ) -> oci.data_science.models.ModelProvenance: """Updates model provenance metadata. @@ -304,7 +306,7 @@ def get_artifact_info(self) -> Dict: msg="Model needs to be restored before the archived artifact content can be accessed." ) def restore_archived_model_artifact( - self, restore_model_for_hours_specified: Optional[int] = None + self, restore_model_for_hours_specified: Optional[int] = None ) -> None: """Restores the archived model artifact. @@ -326,7 +328,8 @@ def restore_archived_model_artifact( """ return self.client.restore_archived_model_artifact( model_id=self.id, - restore_model_for_hours_specified=restore_model_for_hours_specified).headers["opc-work-request-id"] + restore_model_for_hours_specified=restore_model_for_hours_specified, + ).headers["opc-work-request-id"] @check_for_model_id( msg="Model needs to be saved to the Model Catalog before the artifact content can be read." @@ -553,8 +556,6 @@ def model_deployment( Parameters ---------- - model_id: str - The model ID. config: (Dict, optional). Defaults to `None`. Configuration keys and values as per SDK and Tool Configuration. The from_file() method can be used to load configuration from a file. @@ -619,15 +620,16 @@ def is_model_by_reference(self): return True return False - def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before the creating custom metadata artifact corresponding to that model" + ) + def create_custom_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -651,28 +653,32 @@ def create_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ if not utils.is_path_exists(artifact_path): - raise FileNotFoundError( - f"File not found: {artifact_path} . " - ) - with open(artifact_path, 'rb') as f: + raise FileNotFoundError(f"File not found: {artifact_path} . ") + with open(artifact_path, "rb") as f: contents = f.read() logger.info(f"The metadata artifact content - {contents}") - response = self.client.create_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, - content_disposition='form' - '-data; name="file"; filename="readme.*"') - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.create_model_custom_metadatum_artifact( + self.id, + metadata_key_name, + contents, + content_disposition="form" '-data; name="file"; filename="readme.*"', + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before creating defined metadata artifact corresponding to that model" + ) + def create_defined_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -696,26 +702,31 @@ def create_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s """ if not utils.is_path_exists(artifact_path): - raise FileNotFoundError( - f"File not found: {artifact_path} . " - ) - with open(artifact_path, 'rb') as f: + raise FileNotFoundError(f"File not found: {artifact_path} . ") + with open(artifact_path, "rb") as f: contents = f.read() logger.info(f"The metadata artifact content - {contents}") - response = self.client.create_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, - content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.create_model_defined_metadatum_artifact( + self.id, + metadata_key_name, + contents, + content_disposition='form-data; name="file"; filename="readme.*"', + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before updating defined metadata artifact corresponding to that model" + ) + def update_defined_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -739,27 +750,33 @@ def update_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s """ if not utils.is_path_exists(artifact_path): - raise FileNotFoundError( - f"File not found: {artifact_path} . " - ) - with open(artifact_path, 'rb') as f: + raise FileNotFoundError(f"File not found: {artifact_path} . ") + with open(artifact_path, "rb") as f: contents = f.read() - logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") - response = self.client.update_model_defined_metadatum_artifact(model_ocid, metadata_key_name, contents, - content_disposition='form-data; name="file"; filename="readme.*"') - response_data = convert_model_metadata_response(response.headers, response.status) + logger.info( + f"The content of metadata with key {metadata_key_name} - {contents}" + ) + response = self.client.update_model_defined_metadatum_artifact( + self.id, + metadata_key_name, + contents, + content_disposition='form-data; name="file"; filename="readme.*"', + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - - def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str, artifact_path: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before updating custom metadata artifact corresponding to that model" + ) + def update_custom_metadata_artifact( + self, metadata_key_name: str, artifact_path: str + ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -783,27 +800,31 @@ def update_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st """ if not utils.is_path_exists(artifact_path): - raise FileNotFoundError( - f"File not found: {artifact_path} . " - ) - with open(artifact_path, 'rb') as f: + raise FileNotFoundError(f"File not found: {artifact_path} . ") + with open(artifact_path, "rb") as f: contents = f.read() - logger.info(f"The content of metadata with key {metadata_key_name} - {contents}") - response = self.client.update_model_custom_metadatum_artifact(model_ocid, metadata_key_name, contents, - content_disposition='form' - '-data; name="file"; filename="readme.*"') - response_data = convert_model_metadata_response(response.headers, response.status) + logger.info( + f"The content of metadata with key {metadata_key_name} - {contents}" + ) + response = self.client.update_model_custom_metadatum_artifact( + self.id, + metadata_key_name, + contents, + content_disposition="form" '-data; name="file"; filename="readme.*"', + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before fetching custom metadata artifact corresponding to that model" + ) + def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: """Downloads model custom metadata artifact content for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -813,21 +834,21 @@ def get_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) """ try: - return self.client.get_model_custom_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + return self.client.get_model_custom_metadatum_artifact_content( + self.id, metadata_key_name + ).data.content except ServiceError as ex: if ex.status == 404: - raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) + raise ModelMetadataArtifactNotFoundError(self.id, metadata_key_name) - - def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> BytesIO: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before fetching defined metadata artifact corresponding to that model" + ) + def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: """Downloads model defined metadata artifact content for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -837,21 +858,23 @@ def get_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) """ try: - return self.client.get_model_defined_metadatum_artifact_content(model_ocid, metadata_key_name).data.content + return self.client.get_model_defined_metadatum_artifact_content( + self.id, metadata_key_name + ).data.content except ServiceError as ex: if ex.status == 404: - raise ModelMetadataArtifactNotFoundError(model_ocid,metadata_key_name) - + raise ModelMetadataArtifactNotFoundError(self.id, metadata_key_name) - def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before fetching custom metadata artifact corresponding to that model" + ) + def head_custom_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Gets custom metadata artifact metadata for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -871,19 +894,24 @@ def head_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) } """ - response = self.client.head_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.head_model_custom_metadatum_artifact( + self.id, metadata_key_name + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before fetching defined metadata artifact corresponding to that model" + ) + def head_defined_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Gets defined metadata artifact metadata for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -903,19 +931,24 @@ def head_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str } """ - response = self.client.head_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.head_model_defined_metadatum_artifact( + self.id, metadata_key_name + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before the deleting custom metadata artifact corresponding to that model" + ) + def delete_custom_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Deletes model custom metadata artifact for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -933,19 +966,24 @@ def delete_custom_metadata_artifact(self, model_ocid: str, metadata_key_name: st } """ - response = self.client.delete_model_custom_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.delete_model_custom_metadatum_artifact( + self.id, metadata_key_name + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data - def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: str) -> ModelMetadataArtifactDetails: + @check_for_model_id( + msg="Model needs to be saved to the Model Catalog before the deleting defined metadata artifact corresponding to that model" + ) + def delete_defined_metadata_artifact( + self, metadata_key_name: str + ) -> ModelMetadataArtifactDetails: """Deletes model defined metadata artifact for specified model metadata key. Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -963,6 +1001,10 @@ def delete_defined_metadata_artifact(self, model_ocid: str, metadata_key_name: s } """ - response = self.client.delete_model_defined_metadatum_artifact(model_ocid, metadata_key_name) - response_data = convert_model_metadata_response(response.headers, response.status) + response = self.client.delete_model_defined_metadatum_artifact( + self.id, metadata_key_name + ) + response_data = convert_model_metadata_response( + response.headers, response.status + ) return response_data From 0f53a8d09ae9528af808a6ae38c761b61725c4b2 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Thu, 13 Feb 2025 20:29:14 +0530 Subject: [PATCH 17/44] Updating doc string --- ads/model/datascience_model.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 41112ef28..5b66f5df4 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2235,10 +2235,6 @@ def create_custom_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -2304,10 +2300,6 @@ def update_custom_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -2341,10 +2333,6 @@ def update_defined_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. @@ -2378,10 +2366,6 @@ def get_custom_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. target_dir: str @@ -2415,10 +2399,6 @@ def get_defined_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. target_dir: str @@ -2452,10 +2432,6 @@ def delete_custom_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns @@ -2484,10 +2460,6 @@ def delete_defined_metadata_artifact( Parameters ---------- - model_ocid: str - The `OCID`__ of the model. - __ https://docs.cloud.oracle.com/iaas/Content/General/Concepts/identifiers.htm - metadata_key_name: str The name of the model metadatum in the metadata. Returns From 4d60946c869b28d289a23bc3131f72d15bf738c1 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 17 Feb 2025 12:09:01 +0530 Subject: [PATCH 18/44] Adding back license and readme defined metadata keys --- ads/model/model_metadata.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 708cfc8de..d93aa6221 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -100,6 +100,8 @@ class MetadataTaxonomyKeys(ExtendedEnum): ALGORITHM = "Algorithm" HYPERPARAMETERS = "Hyperparameters" ARTIFACT_TEST_RESULT = "ArtifactTestResults" + README = "readme" + LICENSE = "license" class MetadataCustomKeys(ExtendedEnum): From 01e040d9ff061fb4dedb652233150533707560d0 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 17 Feb 2025 13:13:59 +0530 Subject: [PATCH 19/44] Fixing UTs --- tests/unitary/default_setup/model/test_datascience_model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index d2d09adf1..326bfcbe7 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -74,6 +74,8 @@ {"key": "Hyperparameters"}, {"key": "ArtifactTestResults"}, {"key": "UnexpectedKey", "value": "unexpected_value"}, + {"key": "license"}, + {"key": "readme"}, ], "backup_setting": { "is_backup_enabled": True, @@ -152,6 +154,8 @@ {"key": "Hyperparameters", "value": None}, {"key": "ArtifactTestResults", "value": None}, {"key": "UnexpectedKey", "value": "unexpected_value"}, + {"key": "license", "value": None}, + {"key": "readme", "value": None}, ] }, "provenanceMetadata": { @@ -650,6 +654,8 @@ def test__update_from_oci_dsc_model( {"key": "UseCaseType", "value": "multinomial_classification"}, {"key": "Hyperparameters", "value": "new test"}, {"key": "ArtifactTestResults", "value": "new test"}, + {"key": "license", "value": None}, + {"key": "readme", "value": None}, ], "backup_setting": { "is_backup_enabled": True, From 7238c2ed119a24e9663986866a0c56d9a0260f59 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 17 Feb 2025 14:15:26 +0530 Subject: [PATCH 20/44] Fixing UTs --- tests/unitary/default_setup/model/test_datascience_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index 326bfcbe7..dee744026 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -729,6 +729,8 @@ def test__update_from_oci_dsc_model( {"key": "UseCaseType", "value": "multinomial_classification"}, {"key": "Hyperparameters", "value": "new test"}, {"key": "ArtifactTestResults", "value": "new test"}, + {"key": "readme", "value": None}, + {"key": "license", "value": None}, ] }, "backupSetting": { From fabb26886f424cc79311c8ee547aa2ab036a8170 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 17 Feb 2025 18:33:40 +0530 Subject: [PATCH 21/44] Extended metadata artifact apis to include oss path and content --- ads/common/utils.py | 25 ++++++---- ads/model/datascience_model.py | 36 ++++++++++--- ads/model/service/oci_datascience_model.py | 50 +++++++++++-------- .../model/test_oci_datascience_model.py | 17 +++++-- 4 files changed, 84 insertions(+), 44 deletions(-) diff --git a/ads/common/utils.py b/ads/common/utils.py index 92a185c8f..21dbeb045 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -1,10 +1,8 @@ #!/usr/bin/env python -# -*- coding: utf-8; -*- # Copyright (c) 2020, 2024 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -from __future__ import absolute_import, print_function import collections import contextlib @@ -23,7 +21,6 @@ from datetime import datetime from enum import Enum from io import DEFAULT_BUFFER_SIZE -from pathlib import Path from textwrap import fill from typing import Dict, Optional, Union from urllib import request @@ -46,6 +43,7 @@ OptionalDependency, runtime_dependency, ) +from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_client import OCIClientFactory from ads.common.word_lists import adjectives, animals @@ -85,6 +83,13 @@ color=["teal", "blueviolet", "forestgreen", "peru", "y", "dodgerblue", "r"] ) + +class MetadataArtifactPathType(ExtendedEnum): + LOCAL = "local" + OSS = "oss" + CONTENT = "content" + + # sqlalchemy engines _engines = {} @@ -501,13 +506,13 @@ def print_user_message( if is_documentation_mode() and is_notebook(): if display_type.lower() == "tip": if "\n" in msg: - t = "{}:".format(title.upper().strip()) if title else "" + t = f"{title.upper().strip()}:" if title else "" user_message = "{}{}".format( t, "".join( [ - "
  + {}".format(x.strip()) + f"
  + {x.strip()}" for x in msg.strip().split("\n") ] ), @@ -646,7 +651,7 @@ def ellipsis_strings(raw, n=24): else: n2 = int(n) // 2 - 3 n1 = n - n2 - 3 - result.append("{0}...{1}".format(s[:n1], s[-n2:])) + result.append(f"{s[:n1]}...{s[-n2:]}") return result @@ -942,9 +947,9 @@ def generate_requirement_file( with open(os.path.join(file_path, file_name), "w") as req_file: for lib in requirements: if requirements[lib]: - req_file.write("{}=={}\n".format(lib, requirements[lib])) + req_file.write(f"{lib}=={requirements[lib]}\n") else: - req_file.write("{}\n".format(lib)) + req_file.write(f"{lib}\n") def _get_feature_type_and_dtype(column): @@ -966,7 +971,7 @@ def to_dataframe( pd.Series, np.ndarray, pd.DataFrame, - ] + ], ): """ Convert to pandas DataFrame. @@ -1391,7 +1396,7 @@ def remove_file(file_path: str, auth: Optional[Dict] = None) -> None: fs = fsspec.filesystem(scheme, **auth) try: fs.rm(file_path) - except FileNotFoundError as e: + except FileNotFoundError: raise FileNotFoundError(f"`{file_path}` not found.") except Exception as e: raise e diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 1b7937610..160ad835f 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2229,7 +2229,10 @@ def find_model_idx(): self.model_file_description["models"].pop(modelSearchIdx) def create_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, + metadata_key_name: str, + artifact_path_or_content: str, + path_type: str = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -2258,11 +2261,16 @@ def create_custom_metadata_artifact( """ return self.dsc_model.create_custom_metadata_artifact( - metadata_key_name=metadata_key_name, artifact_path=artifact_path + metadata_key_name=metadata_key_name, + artifact_path=artifact_path_or_content, + path_type=path_type, ) def create_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, + metadata_key_name: str, + artifact_path_or_content: str, + path_type: str = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -2290,11 +2298,16 @@ def create_defined_metadata_artifact( """ return self.dsc_model.create_defined_metadata_artifact( - metadata_key_name=metadata_key_name, artifact_path=artifact_path + metadata_key_name=metadata_key_name, + artifact_path=artifact_path_or_content, + path_type=path_type, ) def update_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, + metadata_key_name: str, + artifact_path_or_content: str, + path_type: str = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -2323,11 +2336,16 @@ def update_custom_metadata_artifact( """ return self.dsc_model.update_custom_metadata_artifact( - metadata_key_name=metadata_key_name, artifact_path=artifact_path + metadata_key_name=metadata_key_name, + artifact_path=artifact_path_or_content, + path_type=path_type, ) def update_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, + metadata_key_name: str, + artifact_path_or_content: str, + path_type: str = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -2356,7 +2374,9 @@ def update_defined_metadata_artifact( """ return self.dsc_model.update_defined_metadata_artifact( - metadata_key_name=metadata_key_name, artifact_path=artifact_path + metadata_key_name=metadata_key_name, + artifact_path=artifact_path_or_content, + path_type=path_type, ) def get_custom_metadata_artifact( diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 7d1035e70..a3a00641b 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -30,6 +30,7 @@ from ads.common.utils import extract_region from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment +from ads.opctl.operator.common.utils import default_signer logger = logging.getLogger(__name__) @@ -669,11 +670,32 @@ def create_custom_metadata_artifact( ) return response_data + def get_metadata_content(self, artifact_path_or_content: str, path_type): + if path_type == utils.MetadataArtifactPathType.CONTENT: + return artifact_path_or_content + elif path_type == utils.MetadataArtifactPathType.LOCAL: + if not utils.is_path_exists(artifact_path_or_content): + raise FileNotFoundError( + f"File not found: {artifact_path_or_content} . " + ) + with open(artifact_path_or_content, "rb") as f: + contents = f.read() + logger.info(f"The metadata artifact content - {contents}") + return contents + elif path_type == utils.MetadataArtifactPathType.OSS: + from ads.aqua.common.utils import read_file + + if not utils.is_path_exists(artifact_path_or_content): + raise FileNotFoundError(f"File not found: {artifact_path_or_content}") + contents = str(read_file(artifact_path_or_content, default_signer())) + logger.info(f"The metadata artifact content - {contents}") + return contents + @check_for_model_id( msg="Model needs to be saved to the Model Catalog before creating defined metadata artifact corresponding to that model" ) def create_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, metadata_key_name: str, artifact_path: str, path_type: str ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -701,11 +723,7 @@ def create_defined_metadata_artifact( } """ - if not utils.is_path_exists(artifact_path): - raise FileNotFoundError(f"File not found: {artifact_path} . ") - with open(artifact_path, "rb") as f: - contents = f.read() - logger.info(f"The metadata artifact content - {contents}") + contents = self.get_metadata_content(artifact_path, path_type) response = self.client.create_model_defined_metadatum_artifact( self.id, metadata_key_name, @@ -721,7 +739,7 @@ def create_defined_metadata_artifact( msg="Model needs to be saved to the Model Catalog before updating defined metadata artifact corresponding to that model" ) def update_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, metadata_key_name: str, artifact_path: str, path_type: str ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -749,13 +767,7 @@ def update_defined_metadata_artifact( } """ - if not utils.is_path_exists(artifact_path): - raise FileNotFoundError(f"File not found: {artifact_path} . ") - with open(artifact_path, "rb") as f: - contents = f.read() - logger.info( - f"The content of metadata with key {metadata_key_name} - {contents}" - ) + contents = self.get_metadata_content(artifact_path, path_type) response = self.client.update_model_defined_metadatum_artifact( self.id, metadata_key_name, @@ -771,7 +783,7 @@ def update_defined_metadata_artifact( msg="Model needs to be saved to the Model Catalog before updating custom metadata artifact corresponding to that model" ) def update_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, metadata_key_name: str, artifact_path: str, path_type: str ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -799,13 +811,7 @@ def update_custom_metadata_artifact( } """ - if not utils.is_path_exists(artifact_path): - raise FileNotFoundError(f"File not found: {artifact_path} . ") - with open(artifact_path, "rb") as f: - contents = f.read() - logger.info( - f"The content of metadata with key {metadata_key_name} - {contents}" - ) + contents = self.get_metadata_content(artifact_path, path_type) response = self.client.update_model_custom_metadatum_artifact( self.id, metadata_key_name, diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 596f3e06e..71dce2c3a 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -21,6 +21,7 @@ from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_mixin import OCIModelMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource +from ads.common.utils import MetadataArtifactPathType from ads.model.datascience_model import _MAX_ARTIFACT_SIZE_IN_BYTES from ads.model.service.oci_datascience_model import ( ModelArtifactNotFoundError, @@ -496,7 +497,9 @@ def test_create_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact( - "metadata_key_name", self.mock_artifact_file_path + "metadata_key_name", + self.mock_artifact_file_path, + MetadataArtifactPathType.LOCAL, ) assert data.status == "204" @@ -509,7 +512,9 @@ def test_create_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.create_model_defined_metadatum_artifact.return_value = response data = self.mock_model.create_defined_metadata_artifact( - "metadata_key_name", self.mock_artifact_file_path + "metadata_key_name", + self.mock_artifact_file_path, + MetadataArtifactPathType.LOCAL, ) assert data.status == "204" @@ -522,7 +527,9 @@ def test_update_defined_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_defined_metadatum_artifact.return_value = response data = self.mock_model.update_defined_metadata_artifact( - "metadata_key_name", self.mock_artifact_file_path + "metadata_key_name", + self.mock_artifact_file_path, + MetadataArtifactPathType.LOCAL, ) assert data.status == "204" @@ -535,7 +542,9 @@ def test_update_custom_metadata_artifact(self, mock_client): response = Response(headers={}, status=204, data=None, request=None) mock_client.update_model_custom_metadatum_artifact.return_value = response data = self.mock_model.update_custom_metadata_artifact( - "metadata_key_name", self.mock_artifact_file_path + "metadata_key_name", + self.mock_artifact_file_path, + MetadataArtifactPathType.LOCAL, ) assert data.status == "204" From 134d6c51c2003105809249778883d71b39fec92c Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 18 Feb 2025 15:41:24 +0530 Subject: [PATCH 22/44] Addressing Deployment and FT config defined metadata APIs --- ads/model/model_metadata.py | 6 +++-- .../model/test_datascience_model.py | 24 ++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index d93aa6221..c9a53b1e7 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -100,8 +100,10 @@ class MetadataTaxonomyKeys(ExtendedEnum): ALGORITHM = "Algorithm" HYPERPARAMETERS = "Hyperparameters" ARTIFACT_TEST_RESULT = "ArtifactTestResults" - README = "readme" - LICENSE = "license" + README = "Readme" + LICENSE = "License" + DEPLOYMENT_CONFIGURATION = "DeploymentConfiguration" + FINETUNE_CONFIGURATION = "FineTuneConfiguration" class MetadataCustomKeys(ExtendedEnum): diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index dee744026..197ad0d14 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -74,8 +74,10 @@ {"key": "Hyperparameters"}, {"key": "ArtifactTestResults"}, {"key": "UnexpectedKey", "value": "unexpected_value"}, - {"key": "license"}, - {"key": "readme"}, + {"key": "License"}, + {"key": "Readme"}, + {"key": "FineTuneConfiguration"}, + {"key": "DeploymentConfiguration"}, ], "backup_setting": { "is_backup_enabled": True, @@ -154,8 +156,10 @@ {"key": "Hyperparameters", "value": None}, {"key": "ArtifactTestResults", "value": None}, {"key": "UnexpectedKey", "value": "unexpected_value"}, - {"key": "license", "value": None}, - {"key": "readme", "value": None}, + {"key": "License", "value": None}, + {"key": "Readme", "value": None}, + {"key": "FineTuneConfiguration", "value": None}, + {"key": "DeploymentConfiguration", "value": None}, ] }, "provenanceMetadata": { @@ -654,8 +658,10 @@ def test__update_from_oci_dsc_model( {"key": "UseCaseType", "value": "multinomial_classification"}, {"key": "Hyperparameters", "value": "new test"}, {"key": "ArtifactTestResults", "value": "new test"}, - {"key": "license", "value": None}, - {"key": "readme", "value": None}, + {"key": "License", "value": None}, + {"key": "Readme", "value": None}, + {"key": "FineTuneConfiguration", "value": None}, + {"key": "DeploymentConfiguration", "value": None}, ], "backup_setting": { "is_backup_enabled": True, @@ -729,8 +735,10 @@ def test__update_from_oci_dsc_model( {"key": "UseCaseType", "value": "multinomial_classification"}, {"key": "Hyperparameters", "value": "new test"}, {"key": "ArtifactTestResults", "value": "new test"}, - {"key": "readme", "value": None}, - {"key": "license", "value": None}, + {"key": "License", "value": None}, + {"key": "Readme", "value": None}, + {"key": "FineTuneConfiguration", "value": None}, + {"key": "DeploymentConfiguration", "value": None}, ] }, "backupSetting": { From c1020f846247639051fa55674196a40db0312158 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 25 Feb 2025 23:38:32 +0530 Subject: [PATCH 23/44] Addressing review comments --- ads/common/utils.py | 1 + ads/model/service/oci_datascience_model.py | 38 ++++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/ads/common/utils.py b/ads/common/utils.py index 21dbeb045..239232256 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -84,6 +84,7 @@ ) +# Metadata artifact path type can be either local path or OSS path. It can also be the content itself. class MetadataArtifactPathType(ExtendedEnum): LOCAL = "local" OSS = "oss" diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index a3a00641b..c9ece91b2 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -625,7 +625,7 @@ def _is_model_by_reference(self): msg="Model needs to be saved to the Model Catalog before the creating custom metadata artifact corresponding to that model" ) def create_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str + self, metadata_key_name: str, artifact_path: str, path_type: str ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -653,12 +653,9 @@ def create_custom_metadata_artifact( } """ - if not utils.is_path_exists(artifact_path): - raise FileNotFoundError(f"File not found: {artifact_path} . ") - with open(artifact_path, "rb") as f: - contents = f.read() - logger.info(f"The metadata artifact content - {contents}") - + contents = self.get_metadata_content( + artifact_path_or_content=artifact_path, path_type=path_type + ) response = self.client.create_model_custom_metadatum_artifact( self.id, metadata_key_name, @@ -671,6 +668,21 @@ def create_custom_metadata_artifact( return response_data def get_metadata_content(self, artifact_path_or_content: str, path_type): + """ + returns the content of the metadata artifact + + Parameters + ---------- + artifact_path_or_content: str + The path of the file (local or oss) containing metadata artifact or content. + path_type: str + can be one of local , oss or content + + Returns + ------- + metadata artifact content + """ + if path_type == utils.MetadataArtifactPathType.CONTENT: return artifact_path_or_content elif path_type == utils.MetadataArtifactPathType.LOCAL: @@ -723,7 +735,9 @@ def create_defined_metadata_artifact( } """ - contents = self.get_metadata_content(artifact_path, path_type) + contents = self.get_metadata_content( + artifact_path_or_content=artifact_path, path_type=path_type + ) response = self.client.create_model_defined_metadatum_artifact( self.id, metadata_key_name, @@ -767,7 +781,9 @@ def update_defined_metadata_artifact( } """ - contents = self.get_metadata_content(artifact_path, path_type) + contents = self.get_metadata_content( + artifact_path_or_content=artifact_path, path_type=path_type + ) response = self.client.update_model_defined_metadatum_artifact( self.id, metadata_key_name, @@ -811,7 +827,9 @@ def update_custom_metadata_artifact( } """ - contents = self.get_metadata_content(artifact_path, path_type) + contents = self.get_metadata_content( + artifact_path_or_content=artifact_path, path_type=path_type + ) response = self.client.update_model_custom_metadatum_artifact( self.id, metadata_key_name, From 05e781306d9f70f007b7d05d89d1aba3f3126d39 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 3 Mar 2025 13:13:34 +0530 Subject: [PATCH 24/44] Converting BytesIO to bytes --- ads/model/service/oci_datascience_model.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index c9ece91b2..3a9591556 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -6,7 +6,7 @@ import logging from dataclasses import dataclass from functools import wraps -from io import BytesIO +from io import bytes from typing import Callable, Dict, List, Optional, Union import oci.data_science @@ -159,9 +159,9 @@ def update_model_provenance(self, ModelProvenance) -> oci.data_science.models.Mo Gets model provenance metadata. get_artifact_info(self) -> Dict: Gets model artifact attachment information. - def get_model_artifact_content(self) -> BytesIO: + def get_model_artifact_content(self) -> bytes: Gets model artifact content. - create_model_artifact(self, bytes_content: BytesIO) -> None: + create_model_artifact(self, bytes_content: bytes) -> None: Creates model artifact for specified model. import_model_artifact(self, bucket_uri: str, region: str = None) -> None: Imports model artifact content from the model catalog. @@ -335,14 +335,14 @@ def restore_archived_model_artifact( @check_for_model_id( msg="Model needs to be saved to the Model Catalog before the artifact content can be read." ) - def get_model_artifact_content(self) -> BytesIO: + def get_model_artifact_content(self) -> bytes: """Gets model artifact content. Can only be used to the small artifacts, which size is less than 2GB. For the large artifacts needs to be used a `import_model_artifact` method. Returns ------- - BytesIO + bytes Object with data of type stream. Raises @@ -362,14 +362,14 @@ def get_model_artifact_content(self) -> BytesIO: ) def create_model_artifact( self, - bytes_content: BytesIO, + bytes_content: bytes, extension: str = None, ) -> None: """Creates model artifact for specified model. Parameters ---------- - bytes_content: BytesIO + bytes_content: bytes Model artifacts to upload. extension: str File extension, defaults to zip @@ -844,7 +844,7 @@ def update_custom_metadata_artifact( @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching custom metadata artifact corresponding to that model" ) - def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: + def get_custom_metadata_artifact(self, metadata_key_name: str) -> bytes: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -853,7 +853,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: The name of the model metadatum in the metadata. Returns ------- - BytesIO + bytes custom metadata artifact content """ @@ -868,7 +868,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching defined metadata artifact corresponding to that model" ) - def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: + def get_defined_metadata_artifact(self, metadata_key_name: str) -> bytes: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -877,7 +877,7 @@ def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: The name of the model metadatum in the metadata. Returns ------- - BytesIO + bytes Defined metadata artifact content """ From 5b1207d9924772d0857841d8595f7ef9894b2ccd Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 3 Mar 2025 14:32:25 +0530 Subject: [PATCH 25/44] Reverting --- ads/model/service/oci_datascience_model.py | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 3a9591556..f4a13ef3e 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -6,7 +6,7 @@ import logging from dataclasses import dataclass from functools import wraps -from io import bytes +from io import BytesIO from typing import Callable, Dict, List, Optional, Union import oci.data_science @@ -159,9 +159,9 @@ def update_model_provenance(self, ModelProvenance) -> oci.data_science.models.Mo Gets model provenance metadata. get_artifact_info(self) -> Dict: Gets model artifact attachment information. - def get_model_artifact_content(self) -> bytes: + def get_model_artifact_content(self) -> BytesIO: Gets model artifact content. - create_model_artifact(self, bytes_content: bytes) -> None: + create_model_artifact(self, BytesIO_content: BytesIO) -> None: Creates model artifact for specified model. import_model_artifact(self, bucket_uri: str, region: str = None) -> None: Imports model artifact content from the model catalog. @@ -335,14 +335,14 @@ def restore_archived_model_artifact( @check_for_model_id( msg="Model needs to be saved to the Model Catalog before the artifact content can be read." ) - def get_model_artifact_content(self) -> bytes: + def get_model_artifact_content(self) -> BytesIO: """Gets model artifact content. Can only be used to the small artifacts, which size is less than 2GB. For the large artifacts needs to be used a `import_model_artifact` method. Returns ------- - bytes + BytesIO Object with data of type stream. Raises @@ -362,14 +362,14 @@ def get_model_artifact_content(self) -> bytes: ) def create_model_artifact( self, - bytes_content: bytes, + BytesIO_content: BytesIO, extension: str = None, ) -> None: """Creates model artifact for specified model. Parameters ---------- - bytes_content: bytes + BytesIO_content: BytesIO Model artifacts to upload. extension: str File extension, defaults to zip @@ -377,7 +377,7 @@ def create_model_artifact( ext = ".json" if extension and extension.lower() == ".json" else ".zip" self.client.create_model_artifact( self.id, - bytes_content, + BytesIO_content, content_disposition=f'attachment; filename="{self.id}{ext}"', ) @@ -844,7 +844,7 @@ def update_custom_metadata_artifact( @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching custom metadata artifact corresponding to that model" ) - def get_custom_metadata_artifact(self, metadata_key_name: str) -> bytes: + def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -853,7 +853,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> bytes: The name of the model metadatum in the metadata. Returns ------- - bytes + BytesIO custom metadata artifact content """ @@ -868,7 +868,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> bytes: @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching defined metadata artifact corresponding to that model" ) - def get_defined_metadata_artifact(self, metadata_key_name: str) -> bytes: + def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -877,7 +877,7 @@ def get_defined_metadata_artifact(self, metadata_key_name: str) -> bytes: The name of the model metadatum in the metadata. Returns ------- - bytes + BytesIO Defined metadata artifact content """ From 5eb496e9d85aec190f3182963b1d3464d569194f Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 4 Mar 2025 13:21:23 +0530 Subject: [PATCH 26/44] Adding has_artifact field in model metadata ads --- ads/common/utils.py | 16 ++++ ads/model/model_metadata.py | 40 ++++++++-- ads/model/service/oci_datascience_model.py | 90 ++++++++++++---------- 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/ads/common/utils.py b/ads/common/utils.py index 002ececad..c170e0b20 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -158,6 +158,22 @@ def oci_key_location(): ) +def text_sanitizer(content): + if isinstance(content, str): + return ( + content.replace("“", '"') + .replace("”", '"') + .replace("’", "'") + .replace("‘", "'") + .replace("—", "-") + .encode("utf-8", "ignore") + .decode("utf-8", "ignore") + ) + if isinstance(content, dict): + return json.dumps(content) + return str(content) + + @deprecated( "2.5.10", details="Deprecated, use: from ads.common.auth import AuthState; AuthState().oci_config_path", diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index c9a53b1e7..de3ce01cf 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -86,11 +86,13 @@ class MetadataCustomPrintColumns(ExtendedEnum): VALUE = "Value" DESCRIPTION = "Description" CATEGORY = "Category" + HAS_ARTIFACT = "HasArtifact" class MetadataTaxonomyPrintColumns(ExtendedEnum): KEY = "Key" VALUE = "Value" + HAS_ARTIFACT = "HasArtifact" class MetadataTaxonomyKeys(ExtendedEnum): @@ -344,6 +346,9 @@ def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": if isinstance(key_value_map["value"], str): try: key_value_map["value"] = json.loads(oci_metadata_item.get("value")) + key_value_map["has_artifact"] = json.loads( + oci_metadata_item.get("has_artifact") + ) except Exception: pass @@ -402,15 +407,12 @@ class ModelTaxonomyMetadataItem(ModelMetadataItem): Validates metadata item. """ - _FIELDS = ["key", "value"] + _FIELDS = ["key", "value", "has_artifact"] - def __init__( - self, - key: str, - value: str = None, - ): + def __init__(self, key: str, value: str = None, has_artifact: bool = False): self.key = key self.value = value + self.has_artifact = has_artifact @property def key(self) -> str: @@ -436,6 +438,17 @@ def key(self, key: str): raise ValueError("The key cannot be empty.") self._key = key + @property + def has_artifact(self) -> bool: + return self._has_artifact + + @has_artifact.setter + def has_artifact(self, has_artifact: bool): + if not has_artifact: + self._has_artifact = False + return + self._has_artifact = has_artifact + @property def value(self) -> str: return self._value @@ -559,7 +572,7 @@ class ModelCustomMetadataItem(ModelTaxonomyMetadataItem): Validates metadata item. """ - _FIELDS = ["key", "value", "description", "category"] + _FIELDS = ["key", "value", "description", "category", "has_artifact"] def __init__( self, @@ -567,10 +580,12 @@ def __init__( value: str = None, description: str = None, category: str = None, + has_artifact: bool = False, ): super().__init__(key=key, value=value) self.description = description self.category = category + self.has_artifact = has_artifact @property def description(self) -> str: @@ -590,6 +605,17 @@ def description(self, description: str): self._description = description + @property + def has_artifact(self) -> bool: + return self._has_artifact + + @has_artifact.setter + def has_artifact(self, has_artifact: bool): + if not has_artifact: + self._has_artifact = False + else: + self._has_artifact = has_artifact + @property def category(self) -> str: return self._category diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index f4a13ef3e..1a9327a87 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -621,6 +621,50 @@ def _is_model_by_reference(self): return True return False + def get_metadata_content(self, artifact_path_or_content: str, path_type): + """ + returns the content of the metadata artifact + + Parameters + ---------- + artifact_path_or_content: str + The path of the file (local or oss) containing metadata artifact or content. + path_type: str + can be one of local , oss or content + + Returns + ------- + metadata artifact content + """ + + if path_type == utils.MetadataArtifactPathType.CONTENT: + return artifact_path_or_content + + elif path_type == utils.MetadataArtifactPathType.LOCAL: + if not utils.is_path_exists(artifact_path_or_content): + raise FileNotFoundError( + f"File not found: {artifact_path_or_content} . " + ) + + with open(artifact_path_or_content, "rb") as f: + contents = f.read() + logger.info(f"The metadata artifact content - {contents}") + + return contents + + elif path_type == utils.MetadataArtifactPathType.OSS: + from ads.aqua.common.utils import read_file + + if not utils.is_path_exists(artifact_path_or_content): + raise FileNotFoundError(f"File not found: {artifact_path_or_content}") + + contents = str( + read_file(file_path=artifact_path_or_content, auth=default_signer()) + ) + logger.info(f"The metadata artifact content - {contents}") + + return contents + @check_for_model_id( msg="Model needs to be saved to the Model Catalog before the creating custom metadata artifact corresponding to that model" ) @@ -659,7 +703,7 @@ def create_custom_metadata_artifact( response = self.client.create_model_custom_metadatum_artifact( self.id, metadata_key_name, - contents, + text_sanitizer(contents), content_disposition="form" '-data; name="file"; filename="readme.*"', ) response_data = convert_model_metadata_response( @@ -667,42 +711,6 @@ def create_custom_metadata_artifact( ) return response_data - def get_metadata_content(self, artifact_path_or_content: str, path_type): - """ - returns the content of the metadata artifact - - Parameters - ---------- - artifact_path_or_content: str - The path of the file (local or oss) containing metadata artifact or content. - path_type: str - can be one of local , oss or content - - Returns - ------- - metadata artifact content - """ - - if path_type == utils.MetadataArtifactPathType.CONTENT: - return artifact_path_or_content - elif path_type == utils.MetadataArtifactPathType.LOCAL: - if not utils.is_path_exists(artifact_path_or_content): - raise FileNotFoundError( - f"File not found: {artifact_path_or_content} . " - ) - with open(artifact_path_or_content, "rb") as f: - contents = f.read() - logger.info(f"The metadata artifact content - {contents}") - return contents - elif path_type == utils.MetadataArtifactPathType.OSS: - from ads.aqua.common.utils import read_file - - if not utils.is_path_exists(artifact_path_or_content): - raise FileNotFoundError(f"File not found: {artifact_path_or_content}") - contents = str(read_file(artifact_path_or_content, default_signer())) - logger.info(f"The metadata artifact content - {contents}") - return contents - @check_for_model_id( msg="Model needs to be saved to the Model Catalog before creating defined metadata artifact corresponding to that model" ) @@ -741,7 +749,7 @@ def create_defined_metadata_artifact( response = self.client.create_model_defined_metadatum_artifact( self.id, metadata_key_name, - contents, + text_sanitizer(contents), content_disposition='form-data; name="file"; filename="readme.*"', ) response_data = convert_model_metadata_response( @@ -787,7 +795,7 @@ def update_defined_metadata_artifact( response = self.client.update_model_defined_metadatum_artifact( self.id, metadata_key_name, - contents, + text_sanitizer(contents), content_disposition='form-data; name="file"; filename="readme.*"', ) response_data = convert_model_metadata_response( @@ -833,7 +841,7 @@ def update_custom_metadata_artifact( response = self.client.update_model_custom_metadatum_artifact( self.id, metadata_key_name, - contents, + text_sanitizer(contents), content_disposition="form" '-data; name="file"; filename="readme.*"', ) response_data = convert_model_metadata_response( @@ -886,7 +894,7 @@ def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: self.id, metadata_key_name ).data.content except ServiceError as ex: - if ex.status == 404: + if ex.status == 404 or ex.status == 400: raise ModelMetadataArtifactNotFoundError(self.id, metadata_key_name) @check_for_model_id( From 4cc32ad7c88ba7a8dc324421a6cf44cef3d5c4c5 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 4 Mar 2025 13:23:09 +0530 Subject: [PATCH 27/44] Adding has_artifact field in model metadata ads --- ads/model/service/oci_datascience_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 1a9327a87..292fae120 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -27,7 +27,7 @@ from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource from ads.common.serializer import DataClassSerializable -from ads.common.utils import extract_region +from ads.common.utils import extract_region , text_sanitizer from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment from ads.opctl.operator.common.utils import default_signer From 5c2d6e65a7e82d6b63a894be01991c482c335e9e Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 4 Mar 2025 13:33:56 +0530 Subject: [PATCH 28/44] Updating bytes content variable name --- ads/model/service/oci_datascience_model.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 292fae120..605719da0 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -27,7 +27,7 @@ from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource from ads.common.serializer import DataClassSerializable -from ads.common.utils import extract_region , text_sanitizer +from ads.common.utils import extract_region, text_sanitizer from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment from ads.opctl.operator.common.utils import default_signer @@ -161,7 +161,7 @@ def update_model_provenance(self, ModelProvenance) -> oci.data_science.models.Mo Gets model artifact attachment information. def get_model_artifact_content(self) -> BytesIO: Gets model artifact content. - create_model_artifact(self, BytesIO_content: BytesIO) -> None: + create_model_artifact(self, bytes_content: BytesIO) -> None: Creates model artifact for specified model. import_model_artifact(self, bucket_uri: str, region: str = None) -> None: Imports model artifact content from the model catalog. @@ -362,14 +362,14 @@ def get_model_artifact_content(self) -> BytesIO: ) def create_model_artifact( self, - BytesIO_content: BytesIO, + bytes_content: BytesIO, extension: str = None, ) -> None: """Creates model artifact for specified model. Parameters ---------- - BytesIO_content: BytesIO + bytes_content: BytesIO Model artifacts to upload. extension: str File extension, defaults to zip @@ -377,7 +377,7 @@ def create_model_artifact( ext = ".json" if extension and extension.lower() == ".json" else ".zip" self.client.create_model_artifact( self.id, - BytesIO_content, + bytes_content, content_disposition=f'attachment; filename="{self.id}{ext}"', ) From cd3dd8a32964b55e1db913118988f409bc8a0741 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 4 Mar 2025 23:19:19 +0530 Subject: [PATCH 29/44] Fixing UTs --- ads/model/model_metadata.py | 10 +- .../model/test_datascience_model.py | 109 ++++++++++++------ .../model/test_model_metadata.py | 33 ++++-- 3 files changed, 108 insertions(+), 44 deletions(-) diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index de3ce01cf..4789503f4 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -1398,7 +1398,13 @@ def to_dataframe(self) -> pd.DataFrame: return ( pd.DataFrame( ( - (item.key, item.value, item.description, item.category) + ( + item.key, + item.value, + item.description, + item.category, + item.has_artifact, + ) for item in self._items ), columns=[value for value in MetadataCustomPrintColumns.values()], @@ -1555,7 +1561,7 @@ def to_dataframe(self) -> pd.DataFrame: """ return ( pd.DataFrame( - ((item.key, item.value) for item in self._items), + ((item.key, item.value, item.has_artifact) for item in self._items), columns=[value for value in MetadataTaxonomyPrintColumns.values()], ) .sort_values(by=[MetadataTaxonomyPrintColumns.KEY]) diff --git a/tests/unitary/default_setup/model/test_datascience_model.py b/tests/unitary/default_setup/model/test_datascience_model.py index 197ad0d14..2f6f7b547 100644 --- a/tests/unitary/default_setup/model/test_datascience_model.py +++ b/tests/unitary/default_setup/model/test_datascience_model.py @@ -64,20 +64,25 @@ "value": "oci://bucket@namespace/service_pack/cpu/Data_Exploration_and_Manipulation_for_CPU_Python_3.7/3.0/dataexpl_p37_cpu_v3", "description": "The conda environment where the model was trained.", "category": "Training Environment", + "hasArtifact": False, }, ], "defined_metadata_list": [ - {"key": "Algorithm", "value": "test"}, - {"key": "Framework"}, - {"key": "FrameworkVersion"}, - {"key": "UseCaseType", "value": "multinomial_classification"}, - {"key": "Hyperparameters"}, - {"key": "ArtifactTestResults"}, - {"key": "UnexpectedKey", "value": "unexpected_value"}, - {"key": "License"}, - {"key": "Readme"}, - {"key": "FineTuneConfiguration"}, - {"key": "DeploymentConfiguration"}, + {"key": "Algorithm", "value": "test", "hasArtifact": False}, + {"key": "Framework", "hasArtifact": False}, + {"key": "FrameworkVersion", "hasArtifact": False}, + { + "key": "UseCaseType", + "value": "multinomial_classification", + "hasArtifact": False, + }, + {"key": "Hyperparameters", "hasArtifact": False}, + {"key": "ArtifactTestResults", "hasArtifact": False}, + {"key": "UnexpectedKey", "value": "unexpected_value", "hasArtifact": False}, + {"key": "License", "hasArtifact": False}, + {"key": "Readme", "hasArtifact": False}, + {"key": "FineTuneConfiguration", "hasArtifact": False}, + {"key": "DeploymentConfiguration", "hasArtifact": False}, ], "backup_setting": { "is_backup_enabled": True, @@ -144,22 +149,31 @@ "value": "oci://bucket@namespace/service_pack/cpu/Data_Exploration_and_Manipulation_for_CPU_Python_3.7/3.0/dataexpl_p37_cpu_v3", "description": "The conda environment where the model was trained.", "category": "Training Environment", + "has_artifact": False, }, ] }, "definedMetadataList": { "data": [ - {"key": "Algorithm", "value": "test"}, - {"key": "Framework", "value": None}, - {"key": "FrameworkVersion", "value": None}, - {"key": "UseCaseType", "value": "multinomial_classification"}, - {"key": "Hyperparameters", "value": None}, - {"key": "ArtifactTestResults", "value": None}, - {"key": "UnexpectedKey", "value": "unexpected_value"}, - {"key": "License", "value": None}, - {"key": "Readme", "value": None}, - {"key": "FineTuneConfiguration", "value": None}, - {"key": "DeploymentConfiguration", "value": None}, + {"key": "Algorithm", "value": "test", "has_artifact": False}, + {"key": "Framework", "value": None, "has_artifact": False}, + {"key": "FrameworkVersion", "value": None, "has_artifact": False}, + { + "key": "UseCaseType", + "value": "multinomial_classification", + "has_artifact": False, + }, + {"key": "Hyperparameters", "value": None, "has_artifact": False}, + {"key": "ArtifactTestResults", "value": None, "has_artifact": False}, + { + "key": "UnexpectedKey", + "value": "unexpected_value", + "has_artifact": False, + }, + {"key": "License", "value": None, "has_artifact": False}, + {"key": "Readme", "value": None, "has_artifact": False}, + {"key": "FineTuneConfiguration", "value": None, "has_artifact": False}, + {"key": "DeploymentConfiguration", "value": None, "has_artifact": False}, ] }, "provenanceMetadata": { @@ -610,7 +624,11 @@ def test__to_oci_dsc_model(self): assert self.prepare_dict(test_oci_dsc_model.to_dict()) == self.prepare_dict( self.mock_dsc_model._to_oci_dsc_model().to_dict() ) - + print("test_oci_dsc_model.to_dict(): ", test_oci_dsc_model.to_dict()) + print( + "mock_dsc_model._to_oci_dsc_model().to_dict(): ", + self.mock_dsc_model._to_oci_dsc_model().to_dict(), + ) test_oci_dsc_model.display_name = "new_name" assert self.prepare_dict(test_oci_dsc_model.to_dict()) == self.prepare_dict( self.mock_dsc_model._to_oci_dsc_model(display_name="new_name").to_dict() @@ -724,21 +742,46 @@ def test__update_from_oci_dsc_model( "value": "new oci://bucket@namespace/service_pack/cpu/Data_Exploration_and_Manipulation_for_CPU_Python_3.7/3.0/dataexpl_p37_cpu_v3", "description": "new The conda environment where the model was trained.", "category": "Training Environment", + "has_artifact": False, }, ] }, "definedMetadataList": { "data": [ - {"key": "Algorithm", "value": "new test"}, - {"key": "Framework", "value": "new test"}, - {"key": "FrameworkVersion", "value": "new test"}, - {"key": "UseCaseType", "value": "multinomial_classification"}, - {"key": "Hyperparameters", "value": "new test"}, - {"key": "ArtifactTestResults", "value": "new test"}, - {"key": "License", "value": None}, - {"key": "Readme", "value": None}, - {"key": "FineTuneConfiguration", "value": None}, - {"key": "DeploymentConfiguration", "value": None}, + {"key": "Algorithm", "value": "new test", "has_artifact": False}, + {"key": "Framework", "value": "new test", "has_artifact": False}, + { + "key": "FrameworkVersion", + "value": "new test", + "has_artifact": False, + }, + { + "key": "UseCaseType", + "value": "multinomial_classification", + "has_artifact": False, + }, + { + "key": "Hyperparameters", + "value": "new test", + "has_artifact": False, + }, + { + "key": "ArtifactTestResults", + "value": "new test", + "has_artifact": False, + }, + {"key": "License", "value": None, "has_artifact": False}, + {"key": "Readme", "value": None, "has_artifact": False}, + { + "key": "FineTuneConfiguration", + "value": None, + "has_artifact": False, + }, + { + "key": "DeploymentConfiguration", + "value": None, + "has_artifact": False, + }, ] }, "backupSetting": { diff --git a/tests/unitary/default_setup/model/test_model_metadata.py b/tests/unitary/default_setup/model/test_model_metadata.py index f726c65df..ca2f2b812 100644 --- a/tests/unitary/default_setup/model/test_model_metadata.py +++ b/tests/unitary/default_setup/model/test_model_metadata.py @@ -119,6 +119,7 @@ def test_to_dict(self): expected_result = { "key": self.test_item.key, "value": self.test_item.value, + "has_artifact": False, } assert item_dict == expected_result @@ -151,6 +152,7 @@ def test_validate(self): test_item = ModelTaxonomyMetadataItem( key=MetadataTaxonomyKeys.USE_CASE_TYPE, value=UseCaseType.CLUSTERING, + has_artifact=False, ) assert test_item.validate() == True @@ -178,7 +180,7 @@ def test_validate(self): # Any other key test_item = ModelTaxonomyMetadataItem( - key=MetadataTaxonomyKeys.ALGORITHM, value="any value" + key=MetadataTaxonomyKeys.ALGORITHM, value="any value", has_artifact=False ) assert test_item.validate() == True @@ -197,13 +199,11 @@ def test__to_oci_metadata(self, test_value, expected_value): """Tests converting metadata item to OCI metadata item.""" # case with non empty string value test_metadata_item = ModelTaxonomyMetadataItem( - key=self.test_key, - value=test_value, + key=self.test_key, value=test_value, has_artifact=False ) expected_oci_metadata_item = OciMetadataItem( - key=self.test_key, - value=expected_value, + key=self.test_key, value=expected_value, has_artifact=False ) result_oci_metadata_item = test_metadata_item._to_oci_metadata() assert expected_oci_metadata_item == result_oci_metadata_item @@ -224,11 +224,10 @@ def test__to_oci_metadata(self, test_value, expected_value): def test__from_oci_metadata(self, test_value, expected_value): """Tests creating a new metadata item from the OCI metadata item.""" test_oci_metadata_item = OciMetadataItem( - key=self.test_key, - value=test_value, + key=self.test_key, value=test_value, has_artifact=False ) expected_model_metadata_item = ModelTaxonomyMetadataItem( - key=self.test_key, value=expected_value + key=self.test_key, value=expected_value, has_artifact=False ) result_metadata_item = ModelTaxonomyMetadataItem._from_oci_metadata( test_oci_metadata_item @@ -258,11 +257,13 @@ def test_item(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) assert item.key == self.KEY assert item.value == self.VALUE assert item.description == self.DESCRIPTION assert item.category == self.CATEGORY + assert item.has_artifact == False def test_item_description(self): # test replace description @@ -299,6 +300,7 @@ def test_item_update(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) # test update @@ -315,6 +317,7 @@ def test_item_to_dict(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) # test to dictionary format item_dict = item.to_dict() @@ -329,6 +332,7 @@ def test_item_to_yaml(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) # test to yaml format item_yaml = item.to_yaml() @@ -340,6 +344,7 @@ def test_item_size(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) # test size item.size() == len(json.dumps(item.to_dict()).encode("utf-16")) @@ -415,6 +420,7 @@ def test__to_oci_metadata(self, test_value, expected_value): value=test_value, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) expected_oci_metadata_item = OciMetadataItem( @@ -422,6 +428,7 @@ def test__to_oci_metadata(self, test_value, expected_value): value=expected_value, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) result_oci_metadata_item = test_metadata_item._to_oci_metadata() assert expected_oci_metadata_item == result_oci_metadata_item @@ -446,12 +453,14 @@ def test__from_oci_metadata(self, test_value, expected_value): value=test_value, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) expected_model_metadata_item = ModelCustomMetadataItem( key=self.KEY, value=expected_value, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) result_metadata_item = ModelCustomMetadataItem._from_oci_metadata( test_oci_metadata_item @@ -466,6 +475,7 @@ def test_validate(self): value=self.VALUE, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) assert metadata_item.validate() == True @@ -484,6 +494,7 @@ def test_validate(self): value=[1] * METADATA_VALUE_LENGTH_LIMIT, description=self.DESCRIPTION, category=self.CATEGORY, + has_artifact=False, ) with pytest.raises(MetadataValueTooLong) as exc: metadata_item.validate() @@ -494,6 +505,7 @@ def test_validate(self): value="test", description="ab" * METADATA_DESCRIPTION_LENGTH_LIMIT, category=self.CATEGORY, + has_artifact=False, ) with pytest.raises(MetadataDescriptionTooLong) as exc: metadata_item.validate() @@ -507,6 +519,7 @@ class TestModelCustomMetadata: value="pyspark30_p37_cpu_v1", description="The slug name which was uesd to train the model.", category=MetadataCustomCategory.PERFORMANCE, + has_artifact=False, ) user_defined_item = ModelCustomMetadataItem( @@ -514,6 +527,7 @@ class TestModelCustomMetadata: value="My own Meta", description="This is my own meta", category=MetadataCustomCategory.OTHER, + has_artifact=False, ) dict_item = ModelCustomMetadataItem( @@ -521,7 +535,7 @@ class TestModelCustomMetadata: ) empty_value_item = ModelCustomMetadataItem( - key="My Meta With Empty Value", value=None + key="My Meta With Empty Value", value=None, has_artifact=False ) def test__add(self): @@ -633,6 +647,7 @@ def test_to_dataframe(self): assert df["Value"].iloc[0] == self.performance_item.value assert df["Description"].iloc[0] == self.performance_item.description assert df["Category"].iloc[0] == self.performance_item.category + assert df["HasArtifact"].iloc[0] == self.performance_item.has_artifact def test_size(self): # test check size of model metadata From ead67533f768a3db24b9325ff427d67543a46986 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 5 Mar 2025 18:07:04 +0530 Subject: [PATCH 30/44] Updating doc --- ads/model/datascience_model.py | 58 ++++++++++++++++------ ads/model/service/oci_datascience_model.py | 17 ++++++- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index edd34b9dc..26a42c1b4 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2240,10 +2240,16 @@ def create_custom_metadata_artifact( Parameters ---------- metadata_key_name: str - The name of the model metadatum in the metadata. + The name of the model custom metadata key + + artifact_path_or_content: str + The model custom metadata artifact path to be upload. It can also be the actual content of the custom metadata + + path_type: str + Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + Specifies what type of path is to be provided for metadata artifact. + Can be either local , oss or the actual content itself - artifact_path: str - The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2278,10 +2284,16 @@ def create_defined_metadata_artifact( Parameters ---------- metadata_key_name: str - The name of the model metadatum in the metadata. + The name of the model defined metadata key + + artifact_path_or_content: str + The model defined metadata artifact path to be upload. It can also be the actual content of the defined metadata + + path_type: str + Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + Specifies what type of path is to be provided for metadata artifact. + Can be either local , oss or the actual content itself - artifact_path: str - The model defined metadata artifact local file path to be upload. Returns ------- The model defined metadata artifact creation info. @@ -2315,10 +2327,16 @@ def update_custom_metadata_artifact( Parameters ---------- metadata_key_name: str - The name of the model metadatum in the metadata. + The name of the model custom metadata key + + artifact_path_or_content: str + The model custom metadata artifact path. It can also be the actual content of the custom metadata + + path_type: str + Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + Specifies what type of path is to be provided for metadata artifact. + Can be either local , oss or the actual content itself - artifact_path: str - The model custom metadata artifact local file path to be upload. Returns ------- Dict @@ -2353,10 +2371,16 @@ def update_defined_metadata_artifact( Parameters ---------- metadata_key_name: str - The name of the model metadatum in the metadata. + The name of the model defined metadata key + + artifact_path_or_content: str + The model defined metadata artifact path. It can also be the actual content of the defined metadata + + path_type: str + Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + Specifies what type of path is to be provided for metadata artifact. + Can be either local , oss or the actual content itself - artifact_path: str - The model defined metadata artifact local file path to be upload. Returns ------- Dict @@ -2388,9 +2412,11 @@ def get_custom_metadata_artifact( Parameters ---------- metadata_key_name: str - The name of the model metadatum in the metadata. + The name of the custom metadata key of the model + target_dir: str - The local file path where downloaded model custom metadata artifact saved. + The local file path where downloaded model custom metadata artifact will be saved. + override: bool A boolean flag that controls downloaded metadata artifact file overwriting - If True, overwrites the file if it already exists. @@ -2422,8 +2448,10 @@ def get_defined_metadata_artifact( ---------- metadata_key_name: str The name of the model metadatum in the metadata. + target_dir: str - The local file path where downloaded model defined metadata artifact saved. + The local file path where downloaded model defined metadata artifact will be saved. + override: bool A boolean flag that controls downloaded metadata artifact file overwriting - If True, overwrites the file if it already exists. diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 12d5654ef..2fa4ba47e 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -630,7 +630,7 @@ def get_metadata_content(self, artifact_path_or_content: str, path_type): artifact_path_or_content: str The path of the file (local or oss) containing metadata artifact or content. path_type: str - can be one of local , oss or content + can be one of local , oss or actual content itself Returns ------- @@ -680,6 +680,10 @@ def create_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. + + path_type: str + can be one of local , oss or actual content itself + Returns ------- ModelMetadataArtifactDetails @@ -726,6 +730,10 @@ def create_defined_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. + + path_type: str + can be one of local , oss or actual content itself. + Returns ------- ModelMetadataArtifactDetails @@ -772,6 +780,9 @@ def update_defined_metadata_artifact( artifact_path: str The model defined metadata artifact path to be upload. + + path_type:str + can be one of local , oss or actual content itself. Returns ------- ModelMetadataArtifactDetails @@ -818,6 +829,10 @@ def update_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. + + path_type: str + can be one of local , oss or actual content itself. + Returns ------- ModelMetadataArtifactDetails From f917ee798ac63fb9e7321eed1ac7d990b51e9a6e Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 10 Mar 2025 16:41:27 +0530 Subject: [PATCH 31/44] Updating has_artifact field in ADS --- ads/aqua/app.py | 4 ++-- ads/aqua/common/utils.py | 15 ++++---------- ads/aqua/constants.py | 2 +- ads/aqua/evaluation/evaluation.py | 5 ++--- ads/aqua/finetuning/finetuning.py | 3 +-- ads/aqua/model/model.py | 3 +-- ads/aqua/modeldeployment/deployment.py | 3 +-- ads/aqua/modeldeployment/entities.py | 4 ++-- ads/common/utils.py | 9 ++++++++ ads/model/datascience_model.py | 20 +++++++++--------- ads/model/model_metadata.py | 24 ++++++++++++++-------- ads/model/service/oci_datascience_model.py | 10 +++------ 12 files changed, 51 insertions(+), 51 deletions(-) diff --git a/ads/aqua/app.py b/ads/aqua/app.py index f94b2b29b..66ea85095 100644 --- a/ads/aqua/app.py +++ b/ads/aqua/app.py @@ -21,10 +21,10 @@ 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, is_path_exists +from ads.common.utils import extract_region, is_path_exists , UNKNOWN from ads.config import ( AQUA_TELEMETRY_BUCKET, AQUA_TELEMETRY_BUCKET_NS, diff --git a/ads/aqua/common/utils.py b/ads/aqua/common/utils.py index f4e002d19..21d978595 100644 --- a/ads/aqua/common/utils.py +++ b/ads/aqua/common/utils.py @@ -58,17 +58,19 @@ SUPPORTED_FILE_FORMATS, TEI_CONTAINER_DEFAULT_HOST, TGI_INFERENCE_RESTRICTED_PARAMS, - UNKNOWN, UNKNOWN_JSON_STR, VLLM_INFERENCE_RESTRICTED_PARAMS, ) + +from ads.common.utils import UNKNOWN + from ads.aqua.data import AquaResourceIdentifier from ads.common.auth import AuthState, default_signer from ads.common.decorator.threaded import threaded from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.common.utils import copy_file, get_console_link, upload_to_os +from ads.common.utils import copy_file, get_console_link, upload_to_os, read_file from ads.config import ( AQUA_MODEL_DEPLOYMENT_FOLDER, AQUA_SERVICE_MODELS_BUCKET, @@ -228,15 +230,6 @@ def get_artifact_path(custom_metadata_list: List) -> str: return UNKNOWN -def read_file(file_path: str, **kwargs) -> str: - try: - with fsspec.open(file_path, "r", **kwargs.get("auth", {})) as f: - return f.read() - except Exception as e: - logger.debug(f"Failed to read file {file_path}. {e}") - return UNKNOWN - - @threaded() def load_config(file_path: str, config_file_name: str, **kwargs) -> dict: artifact_path = f"{file_path.rstrip('/')}/{config_file_name}" diff --git a/ads/aqua/constants.py b/ads/aqua/constants.py index 8e0d5ca76..022185534 100644 --- a/ads/aqua/constants.py +++ b/ads/aqua/constants.py @@ -3,7 +3,7 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """This module defines constants used in ads.aqua module.""" -UNKNOWN = "" + UNKNOWN_VALUE = "" READY_TO_IMPORT_STATUS = "TRUE" UNKNOWN_DICT = {} diff --git a/ads/aqua/evaluation/evaluation.py b/ads/aqua/evaluation/evaluation.py index 13adf0bdb..46e0f427b 100644 --- a/ads/aqua/evaluation/evaluation.py +++ b/ads/aqua/evaluation/evaluation.py @@ -52,8 +52,7 @@ EVALUATION_REPORT_MD, JOB_INFRASTRUCTURE_TYPE_DEFAULT_NETWORKING, LIFECYCLE_DETAILS_MISSING_JOBRUN, - NB_SESSION_IDENTIFIER, - UNKNOWN, + NB_SESSION_IDENTIFIER ) from ads.aqua.evaluation.constants import ( EVAL_TERMINATION_STATE, @@ -78,7 +77,7 @@ from ads.aqua.ui import AquaContainerConfig from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_console_link, get_files, get_log_links +from ads.common.utils import get_console_link, get_files, get_log_links , UNKNOWN from ads.config import ( AQUA_JOB_SUBNET_ID, COMPARTMENT_OCID, diff --git a/ads/aqua/finetuning/finetuning.py b/ads/aqua/finetuning/finetuning.py index 29b9e1838..f2791774d 100644 --- a/ads/aqua/finetuning/finetuning.py +++ b/ads/aqua/finetuning/finetuning.py @@ -30,7 +30,6 @@ DEFAULT_FT_REPLICA, DEFAULT_FT_VALIDATION_SET_SIZE, JOB_INFRASTRUCTURE_TYPE_DEFAULT_NETWORKING, - UNKNOWN, UNKNOWN_DICT, ) from ads.aqua.data import AquaResourceIdentifier @@ -45,7 +44,7 @@ ) from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_console_link +from ads.common.utils import get_console_link , UNKNOWN from ads.config import ( AQUA_FINETUNING_CONTAINER_OVERRIDE_FLAG_METADATA_NAME, AQUA_JOB_SUBNET_ID, diff --git a/ads/aqua/model/model.py b/ads/aqua/model/model.py index fff23578f..cca687454 100644 --- a/ads/aqua/model/model.py +++ b/ads/aqua/model/model.py @@ -56,7 +56,6 @@ READY_TO_IMPORT_STATUS, TRAINING_METRICS_FINAL, TRINING_METRICS, - UNKNOWN, VALIDATION_METRICS, VALIDATION_METRICS_FINAL, ) @@ -79,7 +78,7 @@ from ads.aqua.ui import AquaContainerConfig, AquaContainerConfigItem from ads.common.auth import default_signer from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.common.utils import get_console_link +from ads.common.utils import get_console_link , UNKNOWN from ads.config import ( AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME, AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME, diff --git a/ads/aqua/modeldeployment/deployment.py b/ads/aqua/modeldeployment/deployment.py index c65858b53..0c57a11d1 100644 --- a/ads/aqua/modeldeployment/deployment.py +++ b/ads/aqua/modeldeployment/deployment.py @@ -30,7 +30,6 @@ AQUA_MODEL_TYPE_CUSTOM, AQUA_MODEL_TYPE_SERVICE, MODEL_BY_REFERENCE_OSS_PATH_KEY, - UNKNOWN, UNKNOWN_DICT, ) from ads.aqua.data import AquaResourceIdentifier @@ -42,7 +41,7 @@ ) from ads.aqua.ui import ModelFormat from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_log_links +from ads.common.utils import get_log_links , UNKNOWN from ads.config import ( AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME, AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME, diff --git a/ads/aqua/modeldeployment/entities.py b/ads/aqua/modeldeployment/entities.py index 9a6eb5b7b..959031b2c 100644 --- a/ads/aqua/modeldeployment/entities.py +++ b/ads/aqua/modeldeployment/entities.py @@ -11,10 +11,10 @@ ) from ads.aqua.common.enums import Tags -from ads.aqua.constants import UNKNOWN, UNKNOWN_DICT +from ads.aqua.constants import UNKNOWN_DICT from ads.aqua.data import AquaResourceIdentifier from ads.common.serializer import DataClassSerializable -from ads.common.utils import get_console_link +from ads.common.utils import get_console_link , UNKNOWN @dataclass diff --git a/ads/common/utils.py b/ads/common/utils.py index c170e0b20..177767dff 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -64,6 +64,8 @@ # Maximum distinct values by cardinality will be used for plotting MAX_DISPLAY_VALUES = 10 +UNKNOWN = "" + # par link of the index json file. PAR_LINK = "https://objectstorage.us-ashburn-1.oraclecloud.com/p/WyjtfVIG0uda-P3-2FmAfwaLlXYQZbvPZmfX1qg0-sbkwEQO6jpwabGr2hMDBmBp/n/ociodscdev/b/service-conda-packs/o/service_pack/index.json" @@ -236,6 +238,13 @@ def random_valid_ocid(prefix="ocid1.dataflowapplication.oc1.iad"): fake = "".join([random.choice(string.ascii_lowercase) for i in range(60)]) return f"{left}.{fake}" +def read_file(file_path: str, **kwargs) -> str: + try: + with fsspec.open(file_path, "r", **kwargs.get("auth", {})) as f: + return f.read() + except Exception as e: + logger.debug(f"Failed to read file {file_path}. {e}") + return UNKNOWN def get_dataframe_styles(max_width=75): """Styles used for dataframe, example usage: diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 26a42c1b4..14a88cd54 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2233,7 +2233,7 @@ def create_custom_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: str = utils.MetadataArtifactPathType.LOCAL, + path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -2245,7 +2245,7 @@ def create_custom_metadata_artifact( artifact_path_or_content: str The model custom metadata artifact path to be upload. It can also be the actual content of the custom metadata - path_type: str + path_type: utils.MetadataArtifactPathType Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2277,7 +2277,7 @@ def create_defined_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: str = utils.MetadataArtifactPathType.LOCAL, + path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -2289,7 +2289,7 @@ def create_defined_metadata_artifact( artifact_path_or_content: str The model defined metadata artifact path to be upload. It can also be the actual content of the defined metadata - path_type: str + path_type: utils.MetadataArtifactPathType Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2320,7 +2320,7 @@ def update_custom_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: str = utils.MetadataArtifactPathType.LOCAL, + path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -2332,7 +2332,7 @@ def update_custom_metadata_artifact( artifact_path_or_content: str The model custom metadata artifact path. It can also be the actual content of the custom metadata - path_type: str + path_type: utils.MetadataArtifactPathType Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2364,7 +2364,7 @@ def update_defined_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: str = utils.MetadataArtifactPathType.LOCAL, + path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -2376,7 +2376,7 @@ def update_defined_metadata_artifact( artifact_path_or_content: str The model defined metadata artifact path. It can also be the actual content of the defined metadata - path_type: str + path_type: utils.MetadataArtifactPathType Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2437,7 +2437,7 @@ def get_custom_metadata_artifact( with open(artifact_file_path, "wb") as _file: _file.write(file_content) - logger.info(f"Artifact downloaded to location - {artifact_file_path}") + logger.debug(f"Artifact downloaded to location - {artifact_file_path}") def get_defined_metadata_artifact( self, metadata_key_name: str, target_dir: str, override: bool = False @@ -2472,7 +2472,7 @@ def get_defined_metadata_artifact( with open(artifact_file_path, "wb") as _file: _file.write(file_content) - logger.info(f"Artifact downloaded to location - {artifact_file_path}") + logger.debug(f"Artifact downloaded to location - {artifact_file_path}") def delete_custom_metadata_artifact( self, metadata_key_name: str diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 4789503f4..efdc9f892 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -341,17 +341,22 @@ def _to_oci_metadata(self): def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": """Creates a new metadata item from the OCI metadata item.""" oci_metadata_item = to_dict(oci_metadata_item) + print("oci_metadata_item: ",oci_metadata_item) key_value_map = {field: oci_metadata_item.get(field) for field in cls._FIELDS} - + print("key_value_map['value'] type: ",type(key_value_map["value"])) if isinstance(key_value_map["value"], str): try: + print("type of oci_metadata_item: ",type(oci_metadata_item)) key_value_map["value"] = json.loads(oci_metadata_item.get("value")) - key_value_map["has_artifact"] = json.loads( - oci_metadata_item.get("has_artifact") - ) + try: + key_value_map["has_artifact"] = json.loads( + oci_metadata_item.get("has_artifact") + ) + except Exception: + key_value_map["has_artifact"]=False except Exception: pass - + print("key value map: ",key_value_map) return cls(**key_value_map) def __hash__(self): @@ -444,10 +449,7 @@ def has_artifact(self) -> bool: @has_artifact.setter def has_artifact(self, has_artifact: bool): - if not has_artifact: - self._has_artifact = False - return - self._has_artifact = has_artifact + self._has_artifact = has_artifact is True @property def value(self) -> str: @@ -689,6 +691,8 @@ def _to_oci_metadata(self): oci_metadata_item.value = _METADATA_EMPTY_VALUE if not oci_metadata_item.category: oci_metadata_item.category = MetadataCustomCategory.OTHER + if not oci_metadata_item.has_artifact: + oci_metadata_item.has_artifact=False return oci_metadata_item def validate(self) -> bool: @@ -1544,7 +1548,9 @@ def _from_oci_metadata(cls, metadata_list): """ metadata = cls() for oci_item in metadata_list: + print("oci_item: ",oci_item) item = ModelTaxonomyMetadataItem._from_oci_metadata(oci_item) + print("item: ",item) if item.key in metadata.keys: metadata[item.key].update(value=item.value) else: diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 2fa4ba47e..e77542677 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -27,10 +27,10 @@ from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource from ads.common.serializer import DataClassSerializable -from ads.common.utils import extract_region, text_sanitizer +from ads.common.utils import extract_region, text_sanitizer , read_file from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment -from ads.opctl.operator.common.utils import default_signer +from ads.common.auth import default_signer logger = logging.getLogger(__name__) @@ -65,8 +65,6 @@ def __init__(self, model_ocid, metadata_key: str): f"The model {model_ocid} does not contain the metadata with key {metadata_key}." ) - pass - @dataclass(repr=False) class ModelMetadataArtifactDetails(DataClassSerializable): @@ -653,15 +651,13 @@ def get_metadata_content(self, artifact_path_or_content: str, path_type): return contents elif path_type == utils.MetadataArtifactPathType.OSS: - from ads.aqua.common.utils import read_file - if not utils.is_path_exists(artifact_path_or_content): raise FileNotFoundError(f"File not found: {artifact_path_or_content}") contents = str( read_file(file_path=artifact_path_or_content, auth=default_signer()) ) - logger.info(f"The metadata artifact content - {contents}") + logger.debug(f"The metadata artifact content - {contents}") return contents From f847ca3d792e1ab130ebf55e04a24178146fa88e Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 10 Mar 2025 16:41:43 +0530 Subject: [PATCH 32/44] Updating has_artifact field in ADS --- ads/aqua/app.py | 3 +-- ads/aqua/common/utils.py | 12 +++++++----- ads/aqua/constants.py | 1 - ads/aqua/evaluation/evaluation.py | 4 ++-- ads/aqua/finetuning/finetuning.py | 2 +- ads/aqua/model/model.py | 2 +- ads/aqua/modeldeployment/deployment.py | 2 +- ads/aqua/modeldeployment/entities.py | 4 ++-- ads/common/utils.py | 2 ++ ads/model/model_metadata.py | 16 ++++++++-------- ads/model/service/oci_datascience_model.py | 4 ++-- 11 files changed, 27 insertions(+), 25 deletions(-) diff --git a/ads/aqua/app.py b/ads/aqua/app.py index 66ea85095..44efe53ed 100644 --- a/ads/aqua/app.py +++ b/ads/aqua/app.py @@ -21,10 +21,9 @@ is_valid_ocid, load_config, ) - from ads.common import oci_client as oc from ads.common.auth import default_signer -from ads.common.utils import extract_region, is_path_exists , UNKNOWN +from ads.common.utils import UNKNOWN, extract_region, is_path_exists from ads.config import ( AQUA_TELEMETRY_BUCKET, AQUA_TELEMETRY_BUCKET_NS, diff --git a/ads/aqua/common/utils.py b/ads/aqua/common/utils.py index 21d978595..bd74d4eb8 100644 --- a/ads/aqua/common/utils.py +++ b/ads/aqua/common/utils.py @@ -19,7 +19,6 @@ from string import Template from typing import List, Union -import fsspec import oci from cachetools import TTLCache, cached from huggingface_hub.constants import HF_HUB_CACHE @@ -61,16 +60,19 @@ UNKNOWN_JSON_STR, VLLM_INFERENCE_RESTRICTED_PARAMS, ) - -from ads.common.utils import UNKNOWN - from ads.aqua.data import AquaResourceIdentifier from ads.common.auth import AuthState, default_signer from ads.common.decorator.threaded import threaded from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.common.utils import copy_file, get_console_link, upload_to_os, read_file +from ads.common.utils import ( + UNKNOWN, + copy_file, + get_console_link, + read_file, + upload_to_os, +) from ads.config import ( AQUA_MODEL_DEPLOYMENT_FOLDER, AQUA_SERVICE_MODELS_BUCKET, diff --git a/ads/aqua/constants.py b/ads/aqua/constants.py index 022185534..107570478 100644 --- a/ads/aqua/constants.py +++ b/ads/aqua/constants.py @@ -3,7 +3,6 @@ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ """This module defines constants used in ads.aqua module.""" - UNKNOWN_VALUE = "" READY_TO_IMPORT_STATUS = "TRUE" UNKNOWN_DICT = {} diff --git a/ads/aqua/evaluation/evaluation.py b/ads/aqua/evaluation/evaluation.py index 46e0f427b..794f2c661 100644 --- a/ads/aqua/evaluation/evaluation.py +++ b/ads/aqua/evaluation/evaluation.py @@ -52,7 +52,7 @@ EVALUATION_REPORT_MD, JOB_INFRASTRUCTURE_TYPE_DEFAULT_NETWORKING, LIFECYCLE_DETAILS_MISSING_JOBRUN, - NB_SESSION_IDENTIFIER + NB_SESSION_IDENTIFIER, ) from ads.aqua.evaluation.constants import ( EVAL_TERMINATION_STATE, @@ -77,7 +77,7 @@ from ads.aqua.ui import AquaContainerConfig from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_console_link, get_files, get_log_links , UNKNOWN +from ads.common.utils import UNKNOWN, get_console_link, get_files, get_log_links from ads.config import ( AQUA_JOB_SUBNET_ID, COMPARTMENT_OCID, diff --git a/ads/aqua/finetuning/finetuning.py b/ads/aqua/finetuning/finetuning.py index f2791774d..db0101e1a 100644 --- a/ads/aqua/finetuning/finetuning.py +++ b/ads/aqua/finetuning/finetuning.py @@ -44,7 +44,7 @@ ) from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_console_link , UNKNOWN +from ads.common.utils import UNKNOWN, get_console_link from ads.config import ( AQUA_FINETUNING_CONTAINER_OVERRIDE_FLAG_METADATA_NAME, AQUA_JOB_SUBNET_ID, diff --git a/ads/aqua/model/model.py b/ads/aqua/model/model.py index cca687454..81b9c0189 100644 --- a/ads/aqua/model/model.py +++ b/ads/aqua/model/model.py @@ -78,7 +78,7 @@ from ads.aqua.ui import AquaContainerConfig, AquaContainerConfigItem from ads.common.auth import default_signer from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.common.utils import get_console_link , UNKNOWN +from ads.common.utils import UNKNOWN, get_console_link from ads.config import ( AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME, AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME, diff --git a/ads/aqua/modeldeployment/deployment.py b/ads/aqua/modeldeployment/deployment.py index 0c57a11d1..7d62cab2c 100644 --- a/ads/aqua/modeldeployment/deployment.py +++ b/ads/aqua/modeldeployment/deployment.py @@ -41,7 +41,7 @@ ) from ads.aqua.ui import ModelFormat from ads.common.object_storage_details import ObjectStorageDetails -from ads.common.utils import get_log_links , UNKNOWN +from ads.common.utils import UNKNOWN, get_log_links from ads.config import ( AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME, AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME, diff --git a/ads/aqua/modeldeployment/entities.py b/ads/aqua/modeldeployment/entities.py index 959031b2c..8e34f986d 100644 --- a/ads/aqua/modeldeployment/entities.py +++ b/ads/aqua/modeldeployment/entities.py @@ -11,10 +11,10 @@ ) from ads.aqua.common.enums import Tags -from ads.aqua.constants import UNKNOWN_DICT +from ads.aqua.constants import UNKNOWN_DICT from ads.aqua.data import AquaResourceIdentifier from ads.common.serializer import DataClassSerializable -from ads.common.utils import get_console_link , UNKNOWN +from ads.common.utils import UNKNOWN, get_console_link @dataclass diff --git a/ads/common/utils.py b/ads/common/utils.py index 177767dff..07b734387 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -238,6 +238,7 @@ def random_valid_ocid(prefix="ocid1.dataflowapplication.oc1.iad"): fake = "".join([random.choice(string.ascii_lowercase) for i in range(60)]) return f"{left}.{fake}" + def read_file(file_path: str, **kwargs) -> str: try: with fsspec.open(file_path, "r", **kwargs.get("auth", {})) as f: @@ -246,6 +247,7 @@ def read_file(file_path: str, **kwargs) -> str: logger.debug(f"Failed to read file {file_path}. {e}") return UNKNOWN + def get_dataframe_styles(max_width=75): """Styles used for dataframe, example usage: diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index efdc9f892..d8e697d15 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -341,22 +341,22 @@ def _to_oci_metadata(self): def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": """Creates a new metadata item from the OCI metadata item.""" oci_metadata_item = to_dict(oci_metadata_item) - print("oci_metadata_item: ",oci_metadata_item) + print("oci_metadata_item: ", oci_metadata_item) key_value_map = {field: oci_metadata_item.get(field) for field in cls._FIELDS} - print("key_value_map['value'] type: ",type(key_value_map["value"])) + print("key_value_map['value'] type: ", type(key_value_map["value"])) if isinstance(key_value_map["value"], str): try: - print("type of oci_metadata_item: ",type(oci_metadata_item)) + print("type of oci_metadata_item: ", type(oci_metadata_item)) key_value_map["value"] = json.loads(oci_metadata_item.get("value")) try: key_value_map["has_artifact"] = json.loads( oci_metadata_item.get("has_artifact") ) except Exception: - key_value_map["has_artifact"]=False + key_value_map["has_artifact"] = False except Exception: pass - print("key value map: ",key_value_map) + print("key value map: ", key_value_map) return cls(**key_value_map) def __hash__(self): @@ -692,7 +692,7 @@ def _to_oci_metadata(self): if not oci_metadata_item.category: oci_metadata_item.category = MetadataCustomCategory.OTHER if not oci_metadata_item.has_artifact: - oci_metadata_item.has_artifact=False + oci_metadata_item.has_artifact = False return oci_metadata_item def validate(self) -> bool: @@ -1548,9 +1548,9 @@ def _from_oci_metadata(cls, metadata_list): """ metadata = cls() for oci_item in metadata_list: - print("oci_item: ",oci_item) + print("oci_item: ", oci_item) item = ModelTaxonomyMetadataItem._from_oci_metadata(oci_item) - print("item: ",item) + print("item: ", item) if item.key in metadata.keys: metadata[item.key].update(value=item.value) else: diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index e77542677..dfe0ed035 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -22,15 +22,15 @@ from requests.structures import CaseInsensitiveDict from ads.common import utils +from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_datascience import OCIDataScienceMixin from ads.common.oci_mixin import OCIWorkRequestMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource from ads.common.serializer import DataClassSerializable -from ads.common.utils import extract_region, text_sanitizer , read_file +from ads.common.utils import extract_region, read_file, text_sanitizer from ads.common.work_request import DataScienceWorkRequest from ads.model.deployment import ModelDeployment -from ads.common.auth import default_signer logger = logging.getLogger(__name__) From e05cbf30ad49ef625eab41391eead52c66bf4630 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 10 Mar 2025 18:24:00 +0530 Subject: [PATCH 33/44] Updating update method for defined and custom metadata list --- ads/model/datascience_model.py | 5 +++-- ads/model/model_metadata.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 14a88cd54..d558f15f8 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1802,8 +1802,9 @@ def _update_from_oci_dsc_model( self.set_spec(self.CONST_ARTIFACT, file_name_info["filename"]) except: pass - - return self + print("self: ", self) + print("self dsc_model: ", self.dsc_model) + return self.dsc_model def to_dict(self) -> Dict: """Serializes model to a dictionary. diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index d8e697d15..5a1c6f951 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -341,12 +341,9 @@ def _to_oci_metadata(self): def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": """Creates a new metadata item from the OCI metadata item.""" oci_metadata_item = to_dict(oci_metadata_item) - print("oci_metadata_item: ", oci_metadata_item) key_value_map = {field: oci_metadata_item.get(field) for field in cls._FIELDS} - print("key_value_map['value'] type: ", type(key_value_map["value"])) if isinstance(key_value_map["value"], str): try: - print("type of oci_metadata_item: ", type(oci_metadata_item)) key_value_map["value"] = json.loads(oci_metadata_item.get("value")) try: key_value_map["has_artifact"] = json.loads( @@ -356,7 +353,6 @@ def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": key_value_map["has_artifact"] = False except Exception: pass - print("key value map: ", key_value_map) return cls(**key_value_map) def __hash__(self): @@ -490,7 +486,7 @@ def reset(self) -> None: """ self.update(value=None) - def update(self, value: str) -> None: + def update(self, value: str, has_artifact: bool = False) -> None: """Updates metadata item value. Parameters @@ -504,6 +500,7 @@ def update(self, value: str) -> None: Nothing. """ self.value = value + self.has_artifact = has_artifact def validate(self) -> bool: """Validates metadata item. @@ -663,7 +660,9 @@ def reset(self) -> None: """ self.update(value=None, description=None, category=None) - def update(self, value: str, description: str, category: str) -> None: + def update( + self, value: str, description: str, category: str, has_artifact: bool = False + ) -> None: """Updates metadata item. Parameters @@ -683,6 +682,7 @@ def update(self, value: str, description: str, category: str) -> None: self.value = value self.description = description self.category = category + self.has_artifact = has_artifact def _to_oci_metadata(self): """Converts metadata item to OCI metadata item.""" @@ -1548,11 +1548,11 @@ def _from_oci_metadata(cls, metadata_list): """ metadata = cls() for oci_item in metadata_list: - print("oci_item: ", oci_item) item = ModelTaxonomyMetadataItem._from_oci_metadata(oci_item) - print("item: ", item) if item.key in metadata.keys: - metadata[item.key].update(value=item.value) + metadata[item.key].update( + value=item.value, has_artifact=item.has_artifact + ) else: metadata._items.add(item) return metadata From d8bab8a55e7a066b0628e24bf9c29f8afa09d98c Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 10 Mar 2025 19:20:32 +0530 Subject: [PATCH 34/44] Addressing review comments --- ads/model/service/oci_datascience_model.py | 32 ++++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index dfe0ed035..3670f4adb 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -619,7 +619,9 @@ def is_model_created_by_reference(self): return True return False - def get_metadata_content(self, artifact_path_or_content: str, path_type): + def get_metadata_content( + self, artifact_path_or_content: str, path_type: utils.MetadataArtifactPathType + ): """ returns the content of the metadata artifact @@ -665,7 +667,10 @@ def get_metadata_content(self, artifact_path_or_content: str, path_type): msg="Model needs to be saved to the Model Catalog before the creating custom metadata artifact corresponding to that model" ) def create_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str, path_type: str + self, + metadata_key_name: str, + artifact_path: str, + path_type: utils.MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -677,7 +682,7 @@ def create_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: str + path_type: utils.MetadataArtifactPathType can be one of local , oss or actual content itself Returns @@ -715,7 +720,10 @@ def create_custom_metadata_artifact( msg="Model needs to be saved to the Model Catalog before creating defined metadata artifact corresponding to that model" ) def create_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str, path_type: str + self, + metadata_key_name: str, + artifact_path: str, + path_type: utils.MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -727,7 +735,7 @@ def create_defined_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: str + path_type: utils.MetadataArtifactPathType can be one of local , oss or actual content itself. Returns @@ -765,7 +773,10 @@ def create_defined_metadata_artifact( msg="Model needs to be saved to the Model Catalog before updating defined metadata artifact corresponding to that model" ) def update_defined_metadata_artifact( - self, metadata_key_name: str, artifact_path: str, path_type: str + self, + metadata_key_name: str, + artifact_path: str, + path_type: utils.MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -777,7 +788,7 @@ def update_defined_metadata_artifact( artifact_path: str The model defined metadata artifact path to be upload. - path_type:str + path_type:utils.MetadataArtifactPathType can be one of local , oss or actual content itself. Returns ------- @@ -814,7 +825,10 @@ def update_defined_metadata_artifact( msg="Model needs to be saved to the Model Catalog before updating custom metadata artifact corresponding to that model" ) def update_custom_metadata_artifact( - self, metadata_key_name: str, artifact_path: str, path_type: str + self, + metadata_key_name: str, + artifact_path: str, + path_type: utils.MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -826,7 +840,7 @@ def update_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: str + path_type: utils.MetadataArtifactPathType can be one of local , oss or actual content itself. Returns From 0813099e4eb348bee86a5813069aa624afe3342a Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Mon, 10 Mar 2025 22:51:25 +0530 Subject: [PATCH 35/44] Addressing review comments --- ads/model/datascience_model.py | 2 -- ads/model/model_metadata.py | 9 +++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index d558f15f8..17b23a347 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1802,8 +1802,6 @@ def _update_from_oci_dsc_model( self.set_spec(self.CONST_ARTIFACT, file_name_info["filename"]) except: pass - print("self: ", self) - print("self dsc_model: ", self.dsc_model) return self.dsc_model def to_dict(self) -> Dict: diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index 5a1c6f951..ddfe70a9d 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -345,12 +345,9 @@ def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": if isinstance(key_value_map["value"], str): try: key_value_map["value"] = json.loads(oci_metadata_item.get("value")) - try: - key_value_map["has_artifact"] = json.loads( - oci_metadata_item.get("has_artifact") - ) - except Exception: - key_value_map["has_artifact"] = False + key_value_map["has_artifact"] = bool( + oci_metadata_item.get("has_artifact") + ) except Exception: pass return cls(**key_value_map) From 40456fe3650137c5a23d52058339bf04a8ce4f0e Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Tue, 11 Mar 2025 17:05:28 +0530 Subject: [PATCH 36/44] Reverting unwanted changes pushed by mistake --- ads/model/datascience_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 17b23a347..e04b7d587 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -1802,7 +1802,7 @@ def _update_from_oci_dsc_model( self.set_spec(self.CONST_ARTIFACT, file_name_info["filename"]) except: pass - return self.dsc_model + return self def to_dict(self) -> Dict: """Serializes model to a dictionary. From 45e9014f40aa032fd6a62f4476e5fd052659f8e1 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 02:21:28 +0530 Subject: [PATCH 37/44] Addressing review comments --- ads/common/utils.py | 8 ------- ads/model/common/utils.py | 18 ++++++++++++--- ads/model/datascience_model.py | 25 +++++++++++---------- ads/model/service/oci_datascience_model.py | 26 +++++++++++----------- 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/ads/common/utils.py b/ads/common/utils.py index 07b734387..6fa0f2f81 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -43,7 +43,6 @@ OptionalDependency, runtime_dependency, ) -from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_client import OCIClientFactory from ads.common.word_lists import adjectives, animals @@ -86,13 +85,6 @@ ) -# Metadata artifact path type can be either local path or OSS path. It can also be the content itself. -class MetadataArtifactPathType(ExtendedEnum): - LOCAL = "local" - OSS = "oss" - CONTENT = "content" - - # sqlalchemy engines _engines = {} diff --git a/ads/model/common/utils.py b/ads/model/common/utils.py index fc4cd8533..3a943b408 100644 --- a/ads/model/common/utils.py +++ b/ads/model/common/utils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*-- # Copyright (c) 2022, 2023 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ @@ -7,16 +6,29 @@ import json import os import tempfile -import yaml from typing import Any, Dict, Optional from zipfile import ZipFile -from ads.common import utils +import yaml + +from ads.common import utils +from ads.common.extended_enum import ExtendedEnum DEPRECATE_AS_ONNX_WARNING = "This attribute `as_onnx` will be deprecated in the future. You can choose specific format by setting `model_save_serializer`." DEPRECATE_USE_TORCH_SCRIPT_WARNING = "This attribute `use_torch_script` will be deprecated in the future. You can choose specific format by setting `model_save_serializer`." +class MetadataArtifactPathType(ExtendedEnum): + """ + Enum for defining metadata artifact path type. + Can be either local path or OSS path. It can also be the content itself. + """ + + LOCAL = "local" + OSS = "oss" + CONTENT = "content" + + def _extract_locals( locals: Dict[str, Any], filter_out_nulls: Optional[bool] = True ) -> Dict[str, Any]: diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index e04b7d587..22617dfe9 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -34,6 +34,7 @@ SmallArtifactDownloader, ) from ads.model.artifact_uploader import LargeArtifactUploader, SmallArtifactUploader +from ads.model.common.utils import MetadataArtifactPathType from ads.model.model_metadata import ( MetadataCustomCategory, ModelCustomMetadata, @@ -2232,7 +2233,7 @@ def create_custom_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, + path_type: MetadataArtifactPathType = MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -2244,8 +2245,8 @@ def create_custom_metadata_artifact( artifact_path_or_content: str The model custom metadata artifact path to be upload. It can also be the actual content of the custom metadata - path_type: utils.MetadataArtifactPathType - Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + path_type: MetadataArtifactPathType + Can be either of MetadataArtifactPathType.LOCAL , MetadataArtifactPathType.OSS , MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2276,7 +2277,7 @@ def create_defined_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, + path_type: MetadataArtifactPathType = MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -2288,8 +2289,8 @@ def create_defined_metadata_artifact( artifact_path_or_content: str The model defined metadata artifact path to be upload. It can also be the actual content of the defined metadata - path_type: utils.MetadataArtifactPathType - Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + path_type: MetadataArtifactPathType + Can be either of MetadataArtifactPathType.LOCAL , MetadataArtifactPathType.OSS , MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2319,7 +2320,7 @@ def update_custom_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, + path_type: MetadataArtifactPathType = MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -2331,8 +2332,8 @@ def update_custom_metadata_artifact( artifact_path_or_content: str The model custom metadata artifact path. It can also be the actual content of the custom metadata - path_type: utils.MetadataArtifactPathType - Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + path_type: MetadataArtifactPathType + Can be either of MetadataArtifactPathType.LOCAL , MetadataArtifactPathType.OSS , MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself @@ -2363,7 +2364,7 @@ def update_defined_metadata_artifact( self, metadata_key_name: str, artifact_path_or_content: str, - path_type: utils.MetadataArtifactPathType = utils.MetadataArtifactPathType.LOCAL, + path_type: MetadataArtifactPathType = MetadataArtifactPathType.LOCAL, ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -2375,8 +2376,8 @@ def update_defined_metadata_artifact( artifact_path_or_content: str The model defined metadata artifact path. It can also be the actual content of the defined metadata - path_type: utils.MetadataArtifactPathType - Can be either of utils.MetadataArtifactPathType.LOCAL , utils.MetadataArtifactPathType.OSS , utils.MetadataArtifactPathType.CONTENT + path_type: MetadataArtifactPathType + Can be either of MetadataArtifactPathType.LOCAL , MetadataArtifactPathType.OSS , MetadataArtifactPathType.CONTENT Specifies what type of path is to be provided for metadata artifact. Can be either local , oss or the actual content itself diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index 3670f4adb..e5264d90c 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -21,7 +21,6 @@ from oci.exceptions import ServiceError from requests.structures import CaseInsensitiveDict -from ads.common import utils from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_datascience import OCIDataScienceMixin @@ -30,6 +29,7 @@ from ads.common.serializer import DataClassSerializable from ads.common.utils import extract_region, read_file, text_sanitizer from ads.common.work_request import DataScienceWorkRequest +from ads.model.common.utils import MetadataArtifactPathType from ads.model.deployment import ModelDeployment logger = logging.getLogger(__name__) @@ -620,7 +620,7 @@ def is_model_created_by_reference(self): return False def get_metadata_content( - self, artifact_path_or_content: str, path_type: utils.MetadataArtifactPathType + self, artifact_path_or_content: str, path_type: MetadataArtifactPathType ): """ returns the content of the metadata artifact @@ -637,10 +637,10 @@ def get_metadata_content( metadata artifact content """ - if path_type == utils.MetadataArtifactPathType.CONTENT: + if path_type == MetadataArtifactPathType.CONTENT: return artifact_path_or_content - elif path_type == utils.MetadataArtifactPathType.LOCAL: + elif path_type == MetadataArtifactPathType.LOCAL: if not utils.is_path_exists(artifact_path_or_content): raise FileNotFoundError( f"File not found: {artifact_path_or_content} . " @@ -652,7 +652,7 @@ def get_metadata_content( return contents - elif path_type == utils.MetadataArtifactPathType.OSS: + elif path_type == MetadataArtifactPathType.OSS: if not utils.is_path_exists(artifact_path_or_content): raise FileNotFoundError(f"File not found: {artifact_path_or_content}") @@ -670,7 +670,7 @@ def create_custom_metadata_artifact( self, metadata_key_name: str, artifact_path: str, - path_type: utils.MetadataArtifactPathType, + path_type: MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Creates model custom metadata artifact for specified model. @@ -682,7 +682,7 @@ def create_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: utils.MetadataArtifactPathType + path_type: MetadataArtifactPathType can be one of local , oss or actual content itself Returns @@ -723,7 +723,7 @@ def create_defined_metadata_artifact( self, metadata_key_name: str, artifact_path: str, - path_type: utils.MetadataArtifactPathType, + path_type: MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Creates model defined metadata artifact for specified model. @@ -735,7 +735,7 @@ def create_defined_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: utils.MetadataArtifactPathType + path_type: MetadataArtifactPathType can be one of local , oss or actual content itself. Returns @@ -776,7 +776,7 @@ def update_defined_metadata_artifact( self, metadata_key_name: str, artifact_path: str, - path_type: utils.MetadataArtifactPathType, + path_type: MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Update model defined metadata artifact for specified model. @@ -788,7 +788,7 @@ def update_defined_metadata_artifact( artifact_path: str The model defined metadata artifact path to be upload. - path_type:utils.MetadataArtifactPathType + path_type:MetadataArtifactPathType can be one of local , oss or actual content itself. Returns ------- @@ -828,7 +828,7 @@ def update_custom_metadata_artifact( self, metadata_key_name: str, artifact_path: str, - path_type: utils.MetadataArtifactPathType, + path_type: MetadataArtifactPathType, ) -> ModelMetadataArtifactDetails: """Update model custom metadata artifact for specified model. @@ -840,7 +840,7 @@ def update_custom_metadata_artifact( artifact_path: str The model custom metadata artifact path to be upload. - path_type: utils.MetadataArtifactPathType + path_type: MetadataArtifactPathType can be one of local , oss or actual content itself. Returns From 782839f25f1a091810563dd3145adb25bfc08513 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 02:38:46 +0530 Subject: [PATCH 38/44] Fixing UTs --- ads/model/service/oci_datascience_model.py | 1 + tests/unitary/default_setup/model/test_oci_datascience_model.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index e5264d90c..a3d03d19b 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -21,6 +21,7 @@ from oci.exceptions import ServiceError from requests.structures import CaseInsensitiveDict +from ads.common import utils from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_datascience import OCIDataScienceMixin diff --git a/tests/unitary/default_setup/model/test_oci_datascience_model.py b/tests/unitary/default_setup/model/test_oci_datascience_model.py index 6597d99ce..c95464da3 100644 --- a/tests/unitary/default_setup/model/test_oci_datascience_model.py +++ b/tests/unitary/default_setup/model/test_oci_datascience_model.py @@ -21,7 +21,7 @@ from ads.common.object_storage_details import ObjectStorageDetails from ads.common.oci_mixin import OCIModelMixin from ads.common.oci_resource import SEARCH_TYPE, OCIResource -from ads.common.utils import MetadataArtifactPathType +from ads.model.common.utils import MetadataArtifactPathType from ads.model.datascience_model import _MAX_ARTIFACT_SIZE_IN_BYTES from ads.model.service.oci_datascience_model import ( ModelArtifactNotFoundError, From aabbb98d539c71f02739a564249024dc6744a6d0 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 02:54:24 +0530 Subject: [PATCH 39/44] Adding bool parser --- ads/common/utils.py | 24 +++++++++++++++++++++++- ads/model/model_metadata.py | 3 ++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ads/common/utils.py b/ads/common/utils.py index 6fa0f2f81..f2fd992b3 100644 --- a/ads/common/utils.py +++ b/ads/common/utils.py @@ -22,7 +22,7 @@ from enum import Enum from io import DEFAULT_BUFFER_SIZE from textwrap import fill -from typing import Dict, Optional, Tuple, Union +from typing import Any, Dict, Optional, Tuple, Union from urllib import request from urllib.parse import urlparse @@ -231,6 +231,28 @@ def random_valid_ocid(prefix="ocid1.dataflowapplication.oc1.iad"): return f"{left}.{fake}" +def parse_bool(value: Any) -> bool: + """ + Converts a value to boolean. For strings, it interprets 'true', '1', or 'yes' + (case insensitive) as True; everything else as False. + + Parameters + ---------- + value : Any + The value to convert to boolean. + + Returns + ------- + bool + The boolean interpretation of the value. + """ + if isinstance(value, bool): + return value + if isinstance(value, str): + return value.strip().lower() in ("true", "1", "yes") + return bool(value) + + def read_file(file_path: str, **kwargs) -> str: try: with fsspec.open(file_path, "r", **kwargs.get("auth", {})) as f: diff --git a/ads/model/model_metadata.py b/ads/model/model_metadata.py index ddfe70a9d..2d9644670 100644 --- a/ads/model/model_metadata.py +++ b/ads/model/model_metadata.py @@ -24,6 +24,7 @@ from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails from ads.common.serializer import DataClassSerializable +from ads.common.utils import parse_bool from ads.dataset import factory try: @@ -345,7 +346,7 @@ def _from_oci_metadata(cls, oci_metadata_item) -> "ModelMetadataItem": if isinstance(key_value_map["value"], str): try: key_value_map["value"] = json.loads(oci_metadata_item.get("value")) - key_value_map["has_artifact"] = bool( + key_value_map["has_artifact"] = parse_bool( oci_metadata_item.get("has_artifact") ) except Exception: From b4999d0472e037c64635659fc6f7ee74f005e002 Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 03:24:38 +0530 Subject: [PATCH 40/44] Fixing UTs --- tests/unitary/with_extras/aqua/test_evaluation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unitary/with_extras/aqua/test_evaluation.py b/tests/unitary/with_extras/aqua/test_evaluation.py index ef3475184..e05e4717f 100644 --- a/tests/unitary/with_extras/aqua/test_evaluation.py +++ b/tests/unitary/with_extras/aqua/test_evaluation.py @@ -28,7 +28,8 @@ ShapeConfig, UIConfig, ) -from ads.aqua.constants import EVALUATION_REPORT_JSON, EVALUATION_REPORT_MD, UNKNOWN +from ads.aqua.constants import EVALUATION_REPORT_JSON, EVALUATION_REPORT_MD +from ads.common.utils import UNKNOWN from ads.aqua.evaluation import AquaEvaluationApp from ads.aqua.evaluation.entities import ( AquaEvalMetrics, From 54296f11379b1be77bb7ced1c3cdefc33db5805a Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 03:48:14 +0530 Subject: [PATCH 41/44] Addressing review comments --- ads/model/datascience_model.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 22617dfe9..e66245639 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2252,7 +2252,7 @@ def create_custom_metadata_artifact( Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact creation info. Example: { @@ -2296,7 +2296,8 @@ def create_defined_metadata_artifact( Returns ------- - The model defined metadata artifact creation info. + ModelMetadataArtifactDetails + The model defined metadata artifact creation info. Example: { 'Date': 'Mon, 02 Dec 2024 06:38:24 GMT', @@ -2339,7 +2340,7 @@ def update_custom_metadata_artifact( Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact update info. Example: { @@ -2383,7 +2384,7 @@ def update_defined_metadata_artifact( Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact update info. Example: { @@ -2485,7 +2486,7 @@ def delete_custom_metadata_artifact( The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model custom metadata artifact delete call info. Example: { @@ -2513,7 +2514,7 @@ def delete_defined_metadata_artifact( The name of the model metadatum in the metadata. Returns ------- - Dict + ModelMetadataArtifactDetails The model defined metadata artifact delete call info. Example: { From 39763e9d732c66fb6e0452cd7c82df694a0e0e5d Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 03:51:12 +0530 Subject: [PATCH 42/44] Addressing review comments --- ads/model/datascience_model.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index e66245639..2b6f60ce8 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -2424,8 +2424,7 @@ def get_custom_metadata_artifact( - If False (default), raises a `FileExistsError` if the file exists. Returns ------- - BytesIO - custom metadata artifact content + None """ file_content = self.dsc_model.get_custom_metadata_artifact( @@ -2459,8 +2458,7 @@ def get_defined_metadata_artifact( - If False (default), raises a `FileExistsError` if the file exists. Returns ------- - BytesIO - Defined metadata artifact content + None """ file_content = self.dsc_model.get_defined_metadata_artifact( From 2eb12a35af4380b59833623aeb4135797fab9c8e Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 03:59:07 +0530 Subject: [PATCH 43/44] Updating BytesIO to bytes --- ads/model/service/oci_datascience_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ads/model/service/oci_datascience_model.py b/ads/model/service/oci_datascience_model.py index a3d03d19b..5522522bd 100644 --- a/ads/model/service/oci_datascience_model.py +++ b/ads/model/service/oci_datascience_model.py @@ -878,7 +878,7 @@ def update_custom_metadata_artifact( @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching custom metadata artifact corresponding to that model" ) - def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: + def get_custom_metadata_artifact(self, metadata_key_name: str) -> bytes: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -887,7 +887,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: The name of the model metadatum in the metadata. Returns ------- - BytesIO + bytes custom metadata artifact content """ @@ -902,7 +902,7 @@ def get_custom_metadata_artifact(self, metadata_key_name: str) -> BytesIO: @check_for_model_id( msg="Model needs to be saved to the Model Catalog before fetching defined metadata artifact corresponding to that model" ) - def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: + def get_defined_metadata_artifact(self, metadata_key_name: str) -> bytes: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -911,7 +911,7 @@ def get_defined_metadata_artifact(self, metadata_key_name: str) -> BytesIO: The name of the model metadatum in the metadata. Returns ------- - BytesIO + bytes Defined metadata artifact content """ From e414716310832ada2b436be07ae0f3522bf6eadd Mon Sep 17 00:00:00 2001 From: kumar shivam ranjan Date: Wed, 12 Mar 2025 13:47:23 +0530 Subject: [PATCH 44/44] Adding path directory validation --- ads/model/datascience_model.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ads/model/datascience_model.py b/ads/model/datascience_model.py index 2b6f60ce8..2d682fb9b 100644 --- a/ads/model/datascience_model.py +++ b/ads/model/datascience_model.py @@ -19,6 +19,7 @@ from ads.common import utils from ads.common.extended_enum import ExtendedEnum from ads.common.object_storage_details import ObjectStorageDetails +from ads.common.utils import is_path_exists from ads.config import ( AQUA_SERVICE_MODELS_BUCKET as SERVICE_MODELS_BUCKET, ) @@ -74,6 +75,11 @@ def __init__( super().__init__(msg) +class PathNotFoundError(Exception): + def __init__(self, msg="The given path doesn't exist."): + super().__init__(msg) + + class ModelFileDescriptionError(Exception): # pragma: no cover def __init__(self, msg="Model File Description file is not set up."): super().__init__(msg) @@ -2407,7 +2413,7 @@ def update_defined_metadata_artifact( def get_custom_metadata_artifact( self, metadata_key_name: str, target_dir: str, override: bool = False - ) -> None: + ) -> bytes: """Downloads model custom metadata artifact content for specified model metadata key. Parameters @@ -2424,9 +2430,13 @@ def get_custom_metadata_artifact( - If False (default), raises a `FileExistsError` if the file exists. Returns ------- - None + bytes + File content of the custom metadata artifact """ + if not is_path_exists(target_dir): + raise PathNotFoundError(f"Path : {target_dir} does not exist") + file_content = self.dsc_model.get_custom_metadata_artifact( metadata_key_name=metadata_key_name ) @@ -2438,10 +2448,11 @@ def get_custom_metadata_artifact( with open(artifact_file_path, "wb") as _file: _file.write(file_content) logger.debug(f"Artifact downloaded to location - {artifact_file_path}") + return file_content def get_defined_metadata_artifact( self, metadata_key_name: str, target_dir: str, override: bool = False - ) -> None: + ) -> bytes: """Downloads model defined metadata artifact content for specified model metadata key. Parameters @@ -2458,9 +2469,13 @@ def get_defined_metadata_artifact( - If False (default), raises a `FileExistsError` if the file exists. Returns ------- - None + bytes + File content of the custom metadata artifact """ + if not is_path_exists(target_dir): + raise PathNotFoundError(f"Path : {target_dir} does not exist") + file_content = self.dsc_model.get_defined_metadata_artifact( metadata_key_name=metadata_key_name ) @@ -2472,6 +2487,7 @@ def get_defined_metadata_artifact( with open(artifact_file_path, "wb") as _file: _file.write(file_content) logger.debug(f"Artifact downloaded to location - {artifact_file_path}") + return file_content def delete_custom_metadata_artifact( self, metadata_key_name: str