From 1583829b652e7cff4851ff81bbfd1f958b2e7735 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 14:54:18 -0800 Subject: [PATCH 01/10] support input tags for model creation --- ads/aqua/app.py | 14 +++- ads/aqua/model/entities.py | 2 + ads/aqua/model/model.py | 40 +++++++++-- tests/unitary/with_extras/aqua/test_model.py | 71 +++++++++++++++++++- 4 files changed, 117 insertions(+), 10 deletions(-) diff --git a/ads/aqua/app.py b/ads/aqua/app.py index 1cd70b55f..a7a6165d8 100644 --- a/ads/aqua/app.py +++ b/ads/aqua/app.py @@ -2,6 +2,7 @@ # Copyright (c) 2024 Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +import json import os from dataclasses import fields from typing import Dict, Union @@ -135,6 +136,8 @@ def create_model_version_set( description: str = None, compartment_id: str = None, project_id: str = None, + freeform_tags: dict = None, + defined_tags: dict = None, **kwargs, ) -> tuple: """Creates ModelVersionSet from given ID or Name. @@ -153,7 +156,10 @@ def create_model_version_set( Project OCID. tag: (str, optional) calling tag, can be Tags.AQUA_FINE_TUNING or Tags.AQUA_EVALUATION - + freeform_tags: (dict, optional) + Freeform tags for the model version set + defined_tags: (dict, optional) + Defined tags for the model version set Returns ------- tuple: (model_version_set_id, model_version_set_name) @@ -182,6 +188,7 @@ def create_model_version_set( mvs_freeform_tags = { tag: tag, } + mvs_freeform_tags = {**mvs_freeform_tags, **(freeform_tags or {})} model_version_set = ( ModelVersionSet() .with_compartment_id(compartment_id) @@ -189,6 +196,7 @@ def create_model_version_set( .with_name(model_version_set_name) .with_description(description) .with_freeform_tags(**mvs_freeform_tags) + .with_defined_tags(**(defined_tags or {})) # TODO: decide what parameters will be needed # when refactor eval to use this method, we need to pass tag here. .create(**kwargs) @@ -340,7 +348,9 @@ def build_cli(self) -> str: """ cmd = f"ads aqua {self._command}" params = [ - f"--{field.name} {getattr(self,field.name)}" + f"--{field.name} {json.dumps(getattr(self, field.name))}" + if isinstance(getattr(self, field.name), dict) + else f"--{field.name} {getattr(self, field.name)}" for field in fields(self.__class__) if getattr(self, field.name) is not None ] diff --git a/ads/aqua/model/entities.py b/ads/aqua/model/entities.py index 2d94fb3d6..ecdb8b8e7 100644 --- a/ads/aqua/model/entities.py +++ b/ads/aqua/model/entities.py @@ -291,6 +291,8 @@ class ImportModelDetails(CLIBuilderMixin): inference_container_uri: Optional[str] = None allow_patterns: Optional[List[str]] = None ignore_patterns: Optional[List[str]] = None + freeform_tags: Optional[dict] = None + defined_tags: Optional[dict] = None def __post_init__(self): self._command = "model register" diff --git a/ads/aqua/model/model.py b/ads/aqua/model/model.py index 2d01022ae..02e0df00f 100644 --- a/ads/aqua/model/model.py +++ b/ads/aqua/model/model.py @@ -127,7 +127,13 @@ class AquaModelApp(AquaApp): @telemetry(entry_point="plugin=model&action=create", name="aqua") def create( - self, model_id: str, project_id: str, compartment_id: str = None, **kwargs + self, + model_id: str, + project_id: str, + compartment_id: str = None, + freeform_tags: Optional[dict] = None, + defined_tags: Optional[dict] = None, + **kwargs, ) -> DataScienceModel: """Creates custom aqua model from service model. @@ -140,7 +146,10 @@ def create( compartment_id: str The compartment id for custom model. Defaults to None. If not provided, compartment id will be fetched from environment variables. - + freeform_tags: dict + Freeform tags for the model + defined_tags: dict + Defined tags for the model Returns ------- DataScienceModel: @@ -157,6 +166,16 @@ def create( ) return service_model + # combine tags + combined_freeform_tags = { + **(service_model.freeform_tags or {}), + **(freeform_tags or {}), + } + combined_defined_tags = { + **(service_model.defined_tags or {}), + **(defined_tags or {}), + } + custom_model = ( DataScienceModel() .with_compartment_id(target_compartment) @@ -164,8 +183,8 @@ def create( .with_model_file_description(json_dict=service_model.model_file_description) .with_display_name(service_model.display_name) .with_description(service_model.description) - .with_freeform_tags(**(service_model.freeform_tags or {})) - .with_defined_tags(**(service_model.defined_tags or {})) + .with_freeform_tags(**combined_freeform_tags) + .with_defined_tags(**combined_defined_tags) .with_custom_metadata_list(service_model.custom_metadata_list) .with_defined_metadata_list(service_model.defined_metadata_list) .with_provenance_metadata(service_model.provenance_metadata) @@ -414,7 +433,7 @@ def edit_registered_model(self, id, inference_container, enable_finetuning, task except Exception as ex: raise AquaRuntimeError( f"The given model already doesn't support finetuning: {ex}" - ) + ) from ex custom_metadata_list.remove("modelDescription") if task: @@ -766,6 +785,8 @@ def _create_model_catalog_entry( compartment_id: Optional[str], project_id: Optional[str], inference_container_uri: Optional[str], + freeform_tags: Optional[dict] = None, + defined_tags: Optional[dict] = None, ) -> DataScienceModel: """Create model by reference from the object storage path @@ -778,6 +799,8 @@ def _create_model_catalog_entry( compartment_id (Optional[str]): Compartment Id of the compartment where the model has to be created project_id (Optional[str]): Project id of the project where the model has to be created inference_container_uri (Optional[str]): Inference container uri for BYOC + freeform_tags (dict): Freeform tags for the model + defined_tags (dict): Defined tags for the model Returns: DataScienceModel: Returns Datascience model instance. @@ -918,6 +941,8 @@ def _create_model_catalog_entry( category="Other", replace=True, ) + # override tags with freeform tags if set + tags = {**tags, **(freeform_tags or {})} model = ( model.with_custom_metadata_list(metadata) .with_compartment_id(compartment_id or COMPARTMENT_OCID) @@ -925,6 +950,7 @@ def _create_model_catalog_entry( .with_artifact(os_path) .with_display_name(model_name) .with_freeform_tags(**tags) + .with_defined_tags(**(defined_tags or {})) ).create(model_by_reference=True) logger.debug(model) return model @@ -1314,7 +1340,7 @@ def _download_model_from_hf( os_path=os_path, local_dir=local_dir, model_name=model_name, - exclude_pattern=f"{HF_METADATA_FOLDER}*" + exclude_pattern=f"{HF_METADATA_FOLDER}*", ) return model_artifact_path @@ -1402,6 +1428,8 @@ def register( compartment_id=import_model_details.compartment_id, project_id=import_model_details.project_id, inference_container_uri=import_model_details.inference_container_uri, + freeform_tags=import_model_details.freeform_tags, + defined_tags=import_model_details.defined_tags, ) # registered model will always have inference and evaluation container, but # fine-tuning container may be not set diff --git a/tests/unitary/with_extras/aqua/test_model.py b/tests/unitary/with_extras/aqua/test_model.py index bc6fc21d7..cabb8c523 100644 --- a/tests/unitary/with_extras/aqua/test_model.py +++ b/tests/unitary/with_extras/aqua/test_model.py @@ -748,13 +748,13 @@ def test_import_verified_model( local_dir=str(tmpdir), download_from_hf=True, allow_patterns=["*.json"], - ignore_patterns=["test.json"] + ignore_patterns=["test.json"], ) mock_snapshot_download.assert_called_with( repo_id=model_name, local_dir=f"{str(tmpdir)}/{model_name}", allow_patterns=["*.json"], - ignore_patterns=["test.json"] + ignore_patterns=["test.json"], ) mock_subprocess.assert_called_with( shlex.split( @@ -1119,6 +1119,61 @@ def test_import_tei_model_byoc( assert model.ready_to_deploy is True assert model.ready_to_finetune is False + @patch("ads.model.service.oci_datascience_model.OCIDataScienceModel.create") + @patch("ads.model.datascience_model.DataScienceModel.sync") + @patch("ads.model.datascience_model.DataScienceModel.upload_artifact") + @patch("ads.common.object_storage_details.ObjectStorageDetails.list_objects") + @patch.object(HfApi, "model_info") + @patch("ads.aqua.common.utils.load_config", return_value={}) + def test_import_model_with_input_tags( + self, + mock_load_config, + mock_list_objects, + mock_upload_artifact, + mock_sync, + mock_ocidsc_create, + mock_get_hf_model_info, + mock_init_client, + ): + my_model = "oracle/aqua-1t-mega-model" + ObjectStorageDetails.is_bucket_versioned = MagicMock(return_value=True) + + os_path = "oci://aqua-bkt@aqua-ns/prefix/path" + ds_freeform_tags = { + "OCI_AQUA": "active", + } + mock_list_objects.return_value = MagicMock(objects=[]) + + reload(ads.aqua.model.model) + app = AquaModelApp() + with patch.object(AquaModelApp, "list") as aqua_model_mock_list: + aqua_model_mock_list.return_value = [ + AquaModelSummary( + id="test_id1", + name="organization1/name1", + organization="organization1", + ) + ] + model: AquaModel = app.register( + model=my_model, + os_path=os_path, + inference_container="odsc-vllm-or-tgi-container", + finetuning_container="odsc-llm-fine-tuning", + download_from_hf=False, + freeform_tags={"ftag1": "fvalue1", "ftag2": "fvalue2"}, + defined_tags={"dtag1": "dvalue1", "dtag2": "dvalue2"}, + ) + assert model.tags == { + "aqua_custom_base_model": "true", + "model_format": "SAFETENSORS", + "ready_to_fine_tune": "true", + "dtag1": "dvalue1", + "dtag2": "dvalue2", + "ftag1": "fvalue1", + "ftag2": "fvalue2", + **ds_freeform_tags, + } + @pytest.mark.parametrize( "data, expected_output", [ @@ -1163,6 +1218,18 @@ def test_import_tei_model_byoc( }, "ads aqua model register --model oracle/oracle-1it --os_path oci://aqua-bkt@aqua-ns/path --download_from_hf True --inference_container odsc-tei-serving --inference_container_uri .ocir.io//", ), + ( + { + "os_path": "oci://aqua-bkt@aqua-ns/path", + "model": "oracle/oracle-1it", + "inference_container": "odsc-vllm-serving", + "freeform_tags": {"ftag1": "fvalue1", "ftag2": "fvalue2"}, + "defined_tags": {"dtag1": "dvalue1", "dtag2": "dvalue2"}, + }, + "ads aqua model register --model oracle/oracle-1it --os_path oci://aqua-bkt@aqua-ns/path " + "--download_from_hf True --inference_container odsc-vllm-serving --freeform_tags " + '{"ftag1": "fvalue1", "ftag2": "fvalue2"} --defined_tags {"dtag1": "dvalue1", "dtag2": "dvalue2"}', + ), ], ) def test_import_cli(self, data, expected_output): From d710034017ca343a43e1183065036ade62069d74 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 14:56:21 -0800 Subject: [PATCH 02/10] support input tags for deployments --- ads/aqua/modeldeployment/deployment.py | 14 +- ads/aqua/modeldeployment/entities.py | 11 +- .../with_extras/aqua/test_deployment.py | 185 ++++++++++-------- 3 files changed, 125 insertions(+), 85 deletions(-) diff --git a/ads/aqua/modeldeployment/deployment.py b/ads/aqua/modeldeployment/deployment.py index 4f5023dae..b7787ea21 100644 --- a/ads/aqua/modeldeployment/deployment.py +++ b/ads/aqua/modeldeployment/deployment.py @@ -110,6 +110,8 @@ def create( private_endpoint_id: Optional[str] = None, container_image_uri: Optional[None] = None, cmd_var: List[str] = None, + freeform_tags: Optional[dict] = None, + defined_tags: Optional[dict] = None, ) -> "AquaDeployment": """ Creates a new Aqua deployment @@ -163,6 +165,10 @@ def create( Required parameter for BYOC based deployments if this parameter was not set during model registration. cmd_var: List[str] The cmd of model deployment container runtime. + freeform_tags: dict + Freeform tags for the model deployment + defined_tags: dict + Defined tags for the model deployment Returns ------- AquaDeployment @@ -172,7 +178,11 @@ def create( # TODO validate if the service model has no artifact and if it requires import step before deployment. # Create a model catalog entry in the user compartment aqua_model = AquaModelApp().create( - model_id=model_id, compartment_id=compartment_id, project_id=project_id + model_id=model_id, + compartment_id=compartment_id, + project_id=project_id, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) tags = {} @@ -418,12 +428,14 @@ def create( if cmd_var: container_runtime.with_cmd(cmd_var) + tags = {**tags, **(freeform_tags or {})} # configure model deployment and deploy model on container runtime deployment = ( ModelDeployment() .with_display_name(display_name) .with_description(description) .with_freeform_tags(**tags) + .with_defined_tags(**(defined_tags or {})) .with_infrastructure(infrastructure) .with_runtime(container_runtime) ).deploy(wait_for_completion=False) diff --git a/ads/aqua/modeldeployment/entities.py b/ads/aqua/modeldeployment/entities.py index 4ad4d1a78..0b73ffe25 100644 --- a/ads/aqua/modeldeployment/entities.py +++ b/ads/aqua/modeldeployment/entities.py @@ -98,9 +98,12 @@ def from_oci_model_deployment( ), ) - freeform_tags = oci_model_deployment.freeform_tags or UNKNOWN_DICT - aqua_service_model_tag = freeform_tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None) - aqua_model_name = freeform_tags.get(Tags.AQUA_MODEL_NAME_TAG, UNKNOWN) + tags = {} + tags.update(oci_model_deployment.freeform_tags or UNKNOWN_DICT) + tags.update(oci_model_deployment.defined_tags or UNKNOWN_DICT) + + aqua_service_model_tag = tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None) + aqua_model_name = tags.get(Tags.AQUA_MODEL_NAME_TAG, UNKNOWN) private_endpoint_id = getattr( instance_configuration, "private_endpoint_id", UNKNOWN ) @@ -125,7 +128,7 @@ def from_oci_model_deployment( ocid=oci_model_deployment.id, region=region, ), - tags=freeform_tags, + tags=tags, environment_variables=environment_variables, cmd=cmd, ) diff --git a/tests/unitary/with_extras/aqua/test_deployment.py b/tests/unitary/with_extras/aqua/test_deployment.py index 1a9d69c87..78b09403b 100644 --- a/tests/unitary/with_extras/aqua/test_deployment.py +++ b/tests/unitary/with_extras/aqua/test_deployment.py @@ -469,6 +469,9 @@ def test_create_deployment_for_foundation_model( self.app.get_deployment_config = MagicMock(return_value=config) + freeform_tags = {"ftag1": "fvalue1", "ftag2": "fvalue2"} + defined_tags = {"dtag1": "dvalue1", "dtag2": "dvalue2"} + container_index_json = os.path.join( self.curr_dir, "test_data/ui/container_index.json" ) @@ -483,6 +486,8 @@ def test_create_deployment_for_foundation_model( model_deployment_obj = ModelDeployment.from_yaml(uri=aqua_deployment) model_deployment_dsc_obj = copy.deepcopy(TestDataset.model_deployment_object[0]) model_deployment_dsc_obj["lifecycle_state"] = "CREATING" + model_deployment_dsc_obj["defined_tags"] = defined_tags + model_deployment_dsc_obj["freeform_tags"].update(freeform_tags) model_deployment_obj.dsc_model_deployment = ( oci.data_science.models.ModelDeploymentSummary(**model_deployment_dsc_obj) ) @@ -495,10 +500,16 @@ def test_create_deployment_for_foundation_model( log_group_id="ocid1.loggroup.oc1..", access_log_id="ocid1.log.oc1..", predict_log_id="ocid1.log.oc1..", + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) mock_create.assert_called_with( - model_id=TestDataset.MODEL_ID, compartment_id=None, project_id=None + model_id=TestDataset.MODEL_ID, + compartment_id=None, + project_id=None, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) mock_get_container_image.assert_called() mock_deploy.assert_called() @@ -508,6 +519,8 @@ def test_create_deployment_for_foundation_model( assert set(actual_attributes) == set(expected_attributes), "Attributes mismatch" expected_result = copy.deepcopy(TestDataset.aqua_deployment_object) expected_result["state"] = "CREATING" + expected_result["tags"].update(freeform_tags) + expected_result["tags"].update(defined_tags) assert actual_attributes == expected_result @patch("ads.aqua.modeldeployment.deployment.get_container_config") @@ -566,7 +579,11 @@ def test_create_deployment_for_fine_tuned_model( ) mock_create.assert_called_with( - model_id=TestDataset.MODEL_ID, compartment_id=None, project_id=None + model_id=TestDataset.MODEL_ID, + compartment_id=None, + project_id=None, + freeform_tags=None, + defined_tags=None, ) mock_get_container_image.assert_called() mock_deploy.assert_called() @@ -638,7 +655,11 @@ def test_create_deployment_for_gguf_model( ) mock_create.assert_called_with( - model_id=TestDataset.MODEL_ID, compartment_id=None, project_id=None + model_id=TestDataset.MODEL_ID, + compartment_id=None, + project_id=None, + freeform_tags=None, + defined_tags=None, ) mock_get_container_image.assert_called() mock_deploy.assert_called() @@ -654,83 +675,87 @@ def test_create_deployment_for_gguf_model( ) assert actual_attributes == expected_result - # @patch("ads.aqua.modeldeployment.deployment.get_container_config") - # @patch("ads.aqua.model.AquaModelApp.create") - # @patch("ads.aqua.modeldeployment.deployment.get_container_image") - # @patch("ads.model.deployment.model_deployment.ModelDeployment.deploy") - # def test_create_deployment_for_tei_byoc_embedding_model( - # self, - # mock_deploy, - # mock_get_container_image, - # mock_create, - # mock_get_container_config, - # ): - # """Test to create a deployment for fine-tuned model""" - # aqua_model = os.path.join( - # self.curr_dir, "test_data/deployment/aqua_tei_byoc_embedding_model.yaml" - # ) - # datascience_model = DataScienceModel.from_yaml(uri=aqua_model) - # mock_create.return_value = datascience_model - # - # config_json = os.path.join( - # self.curr_dir, "test_data/deployment/deployment_config.json" - # ) - # with open(config_json, "r") as _file: - # config = json.load(_file) - # - # self.app.get_deployment_config = MagicMock(return_value=config) - # - # container_index_json = os.path.join( - # self.curr_dir, "test_data/ui/container_index.json" - # ) - # with open(container_index_json, "r") as _file: - # container_index_config = json.load(_file) - # mock_get_container_config.return_value = container_index_config - # - # mock_get_container_image.return_value = TestDataset.DEPLOYMENT_IMAGE_NAME - # aqua_deployment = os.path.join( - # self.curr_dir, "test_data/deployment/aqua_create_embedding_deployment.yaml" - # ) - # model_deployment_obj = ModelDeployment.from_yaml(uri=aqua_deployment) - # model_deployment_dsc_obj = copy.deepcopy( - # TestDataset.model_deployment_object_tei_byoc[0] - # ) - # model_deployment_dsc_obj["lifecycle_state"] = "CREATING" - # model_deployment_obj.dsc_model_deployment = ( - # oci.data_science.models.ModelDeploymentSummary(**model_deployment_dsc_obj) - # ) - # mock_deploy.return_value = model_deployment_obj - # - # result = self.app.create( - # model_id=TestDataset.MODEL_ID, - # instance_shape=TestDataset.DEPLOYMENT_SHAPE_NAME, - # display_name="model-deployment-name", - # log_group_id="ocid1.loggroup.oc1..", - # access_log_id="ocid1.log.oc1..", - # predict_log_id="ocid1.log.oc1..", - # container_family="odsc-tei-serving", - # cmd_var=[], - # ) - # - # mock_create.assert_called_with( - # model_id=TestDataset.MODEL_ID, compartment_id=None, project_id=None - # ) - # mock_get_container_image.assert_called() - # mock_deploy.assert_called() - # - # expected_attributes = set(AquaDeployment.__annotations__.keys()) - # actual_attributes = asdict(result) - # assert set(actual_attributes) == set(expected_attributes), "Attributes mismatch" - # expected_result = copy.deepcopy(TestDataset.aqua_deployment_object) - # expected_result["state"] = "CREATING" - # expected_result["shape_info"] = ( - # TestDataset.aqua_deployment_tei_byoc_embeddings_shape_info - # ) - # expected_result["cmd"] = TestDataset.aqua_deployment_tei_byoc_embeddings_cmd - # expected_result["environment_variables"] = ( - # TestDataset.aqua_deployment_tei_byoc_embeddings_env_vars - # ) - # assert actual_attributes == expected_result + @patch("ads.aqua.modeldeployment.deployment.get_container_config") + @patch("ads.aqua.model.AquaModelApp.create") + @patch("ads.aqua.modeldeployment.deployment.get_container_image") + @patch("ads.model.deployment.model_deployment.ModelDeployment.deploy") + def test_create_deployment_for_tei_byoc_embedding_model( + self, + mock_deploy, + mock_get_container_image, + mock_create, + mock_get_container_config, + ): + """Test to create a deployment for fine-tuned model""" + aqua_model = os.path.join( + self.curr_dir, "test_data/deployment/aqua_tei_byoc_embedding_model.yaml" + ) + datascience_model = DataScienceModel.from_yaml(uri=aqua_model) + mock_create.return_value = datascience_model + + config_json = os.path.join( + self.curr_dir, "test_data/deployment/deployment_config.json" + ) + with open(config_json, "r") as _file: + config = json.load(_file) + + self.app.get_deployment_config = MagicMock(return_value=config) + + container_index_json = os.path.join( + self.curr_dir, "test_data/ui/container_index.json" + ) + with open(container_index_json, "r") as _file: + container_index_config = json.load(_file) + mock_get_container_config.return_value = container_index_config + + mock_get_container_image.return_value = TestDataset.DEPLOYMENT_IMAGE_NAME + aqua_deployment = os.path.join( + self.curr_dir, "test_data/deployment/aqua_create_embedding_deployment.yaml" + ) + model_deployment_obj = ModelDeployment.from_yaml(uri=aqua_deployment) + model_deployment_dsc_obj = copy.deepcopy( + TestDataset.model_deployment_object_tei_byoc[0] + ) + model_deployment_dsc_obj["lifecycle_state"] = "CREATING" + model_deployment_obj.dsc_model_deployment = ( + oci.data_science.models.ModelDeploymentSummary(**model_deployment_dsc_obj) + ) + mock_deploy.return_value = model_deployment_obj + + result = self.app.create( + model_id=TestDataset.MODEL_ID, + instance_shape=TestDataset.DEPLOYMENT_SHAPE_NAME, + display_name="model-deployment-name", + log_group_id="ocid1.loggroup.oc1..", + access_log_id="ocid1.log.oc1..", + predict_log_id="ocid1.log.oc1..", + container_family="odsc-tei-serving", + cmd_var=[], + ) + + mock_create.assert_called_with( + model_id=TestDataset.MODEL_ID, + compartment_id=None, + project_id=None, + freeform_tags=None, + defined_tags=None, + ) + mock_get_container_image.assert_called() + mock_deploy.assert_called() + + expected_attributes = set(AquaDeployment.__annotations__.keys()) + actual_attributes = asdict(result) + assert set(actual_attributes) == set(expected_attributes), "Attributes mismatch" + expected_result = copy.deepcopy(TestDataset.aqua_deployment_object) + expected_result["state"] = "CREATING" + expected_result["shape_info"] = ( + TestDataset.aqua_deployment_tei_byoc_embeddings_shape_info + ) + expected_result["cmd"] = TestDataset.aqua_deployment_tei_byoc_embeddings_cmd + expected_result["environment_variables"] = ( + TestDataset.aqua_deployment_tei_byoc_embeddings_env_vars + ) + assert actual_attributes == expected_result @parameterized.expand( [ From 13053b123b44bc9692ac43bdd477796251c0c0a3 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 15:13:07 -0800 Subject: [PATCH 03/10] support input tags for finetuning --- ads/aqua/finetuning/entities.py | 6 ++ ads/aqua/finetuning/finetuning.py | 86 +++++++++++-------- .../with_extras/aqua/test_finetuning.py | 17 +++- 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/ads/aqua/finetuning/entities.py b/ads/aqua/finetuning/entities.py index d096e190f..ba4c3fc79 100644 --- a/ads/aqua/finetuning/entities.py +++ b/ads/aqua/finetuning/entities.py @@ -80,6 +80,10 @@ class CreateFineTuningDetails(DataClassSerializable): The log id for fine tuning job infrastructure. force_overwrite: (bool, optional). Defaults to `False`. Whether to force overwrite the existing file in object storage. + freeform_tags: (dict, optional) + Freeform tags for the fine-tuning model + defined_tags: (dict, optional) + Defined tags for the fine-tuning model """ ft_source_id: str @@ -101,3 +105,5 @@ class CreateFineTuningDetails(DataClassSerializable): log_id: Optional[str] = None log_group_id: Optional[str] = None force_overwrite: Optional[bool] = False + freeform_tags: Optional[dict] = None + defined_tags: Optional[dict] = None diff --git a/ads/aqua/finetuning/finetuning.py b/ads/aqua/finetuning/finetuning.py index 11a99c6b1..e1457b3f4 100644 --- a/ads/aqua/finetuning/finetuning.py +++ b/ads/aqua/finetuning/finetuning.py @@ -35,7 +35,11 @@ ENV_AQUA_FINE_TUNING_CONTAINER, FineTuneCustomMetadata, ) -from ads.aqua.finetuning.entities import * +from ads.aqua.finetuning.entities import ( + AquaFineTuningParams, + AquaFineTuningSummary, + CreateFineTuningDetails, +) from ads.common.auth import default_signer from ads.common.object_storage_details import ObjectStorageDetails from ads.common.utils import get_console_link @@ -100,14 +104,14 @@ def create( if not create_fine_tuning_details: try: create_fine_tuning_details = CreateFineTuningDetails(**kwargs) - except: + except Exception as ex: allowed_create_fine_tuning_details = ", ".join( field.name for field in fields(CreateFineTuningDetails) ).rstrip() raise AquaValueError( "Invalid create fine tuning parameters. Allowable parameters are: " f"{allowed_create_fine_tuning_details}." - ) + ) from ex source = self.get_source(create_fine_tuning_details.ft_source_id) @@ -148,28 +152,27 @@ def create( "Specify the subnet id via API or environment variable AQUA_JOB_SUBNET_ID." ) - if create_fine_tuning_details.replica > DEFAULT_FT_REPLICA: - if not ( - create_fine_tuning_details.log_id - and create_fine_tuning_details.log_group_id - ): - raise AquaValueError( - f"Logging is required for fine tuning if replica is larger than {DEFAULT_FT_REPLICA}." - ) + if create_fine_tuning_details.replica > DEFAULT_FT_REPLICA and not ( + create_fine_tuning_details.log_id + and create_fine_tuning_details.log_group_id + ): + raise AquaValueError( + f"Logging is required for fine tuning if replica is larger than {DEFAULT_FT_REPLICA}." + ) ft_parameters = None try: ft_parameters = AquaFineTuningParams( **create_fine_tuning_details.ft_parameters, ) - except: + except Exception as ex: allowed_fine_tuning_parameters = ", ".join( field.name for field in fields(AquaFineTuningParams) ).rstrip() raise AquaValueError( "Invalid fine tuning parameters. Fine tuning parameters should " f"be a dictionary with keys: {allowed_fine_tuning_parameters}." - ) + ) from ex experiment_model_version_set_id = create_fine_tuning_details.experiment_id experiment_model_version_set_name = create_fine_tuning_details.experiment_name @@ -197,11 +200,11 @@ def create( auth=default_signer(), force_overwrite=create_fine_tuning_details.force_overwrite, ) - except FileExistsError: + except FileExistsError as fe: raise AquaFileExistsError( f"Dataset {dataset_file} already exists in {create_fine_tuning_details.report_path}. " "Please use a new dataset file name, report path or set `force_overwrite` as True." - ) + ) from fe logger.debug( f"Uploaded local file {ft_dataset_path} to object storage {dst_uri}." ) @@ -222,6 +225,8 @@ def create( description=create_fine_tuning_details.experiment_description, compartment_id=target_compartment, project_id=target_project, + freeform_tags=create_fine_tuning_details.freeform_tags, + defined_tags=create_fine_tuning_details.defined_tags, ) ft_model_custom_metadata = ModelCustomMetadata() @@ -273,6 +278,10 @@ def create( Tags.AQUA_TAG: UNKNOWN, Tags.AQUA_FINE_TUNED_MODEL_TAG: f"{source.id}#{source.display_name}", } + ft_job_freeform_tags = { + **ft_job_freeform_tags, + **(create_fine_tuning_details.freeform_tags or {}), + } ft_job = Job(name=ft_model.display_name).with_infrastructure( DataScienceJob() @@ -286,6 +295,7 @@ def create( or DEFAULT_FT_BLOCK_STORAGE_SIZE ) .with_freeform_tag(**ft_job_freeform_tags) + .with_defined_tag(**(create_fine_tuning_details.defined_tags or {})) ) if not subnet_id: @@ -353,6 +363,7 @@ def create( ft_job_run = ft_job.run( name=ft_model.display_name, freeform_tags=ft_job_freeform_tags, + defined_tags=create_fine_tuning_details.defined_tags or {}, wait=False, ) logger.debug( @@ -372,22 +383,25 @@ def create( for metadata in ft_model_custom_metadata.to_dict()["data"] ] - source_freeform_tags = source.freeform_tags or {} - source_freeform_tags.pop(Tags.LICENSE, None) - source_freeform_tags.update({Tags.READY_TO_FINE_TUNE: "false"}) - source_freeform_tags.update({Tags.AQUA_TAG: UNKNOWN}) - source_freeform_tags.pop(Tags.BASE_MODEL_CUSTOM, None) + model_freeform_tags = source.freeform_tags or {} + model_freeform_tags.pop(Tags.LICENSE, None) + model_freeform_tags.pop(Tags.BASE_MODEL_CUSTOM, None) + + model_freeform_tags = { + **model_freeform_tags, + Tags.READY_TO_FINE_TUNE: "false", + Tags.AQUA_TAG: UNKNOWN, + Tags.AQUA_FINE_TUNED_MODEL_TAG: f"{source.id}#{source.display_name}", + **(create_fine_tuning_details.freeform_tags or {}), + } + model_defined_tags = create_fine_tuning_details.defined_tags or {} self.update_model( model_id=ft_model.id, update_model_details=UpdateModelDetails( custom_metadata_list=updated_custom_metadata_list, - freeform_tags={ - Tags.AQUA_FINE_TUNED_MODEL_TAG: ( - f"{source.id}#{source.display_name}" - ), - **source_freeform_tags, - }, + freeform_tags=model_freeform_tags, + defined_tags=model_defined_tags, ), ) @@ -462,12 +476,16 @@ def create( region=self.region, ), ), - tags=dict( - aqua_finetuning=Tags.AQUA_FINE_TUNING, - finetuning_job_id=ft_job.id, - finetuning_source=source.id, - finetuning_experiment_id=experiment_model_version_set_id, - ), + tags={ + **{ + "aqua_finetuning": Tags.AQUA_FINE_TUNING, + "finetuning_job_id": ft_job.id, + "finetuning_source": source.id, + "finetuning_experiment_id": experiment_model_version_set_id, + }, + **model_freeform_tags, + **model_defined_tags, + }, parameters={ key: value for key, value in asdict(ft_parameters).items() @@ -635,6 +653,6 @@ def validate_finetuning_params(self, params: Dict = None) -> Dict: raise AquaValueError( f"Invalid fine tuning parameters. Allowable parameters are: " f"{allowed_fine_tuning_parameters}." - ) + ) from e - return dict(valid=True) + return {"valid": True} diff --git a/tests/unitary/with_extras/aqua/test_finetuning.py b/tests/unitary/with_extras/aqua/test_finetuning.py index 6fccb5aae..7c07db9a9 100644 --- a/tests/unitary/with_extras/aqua/test_finetuning.py +++ b/tests/unitary/with_extras/aqua/test_finetuning.py @@ -119,6 +119,9 @@ def test_create_fine_tuning( self.app.ds_client.update_model = MagicMock() self.app.ds_client.update_model_provenance = MagicMock() + ft_model_freeform_tags = {"ftag1": "fvalue1", "ftag2": "fvalue2"} + ft_model_defined_tags = {"dtag1": "dvalue1", "dtag2": "dvalue2"} + create_aqua_ft_details = dict( ft_source_id="ocid1.datasciencemodel.oc1.iad.", ft_name="test_ft_name", @@ -134,6 +137,8 @@ def test_create_fine_tuning( validation_set_size=0.2, block_storage_size=1, experiment_name="test_experiment_name", + freeform_tags=ft_model_freeform_tags, + defined_tags=ft_model_defined_tags, ) aqua_ft_summary = self.app.create(**create_aqua_ft_details) @@ -167,10 +172,14 @@ def test_create_fine_tuning( "url": f"https://cloud.oracle.com/data-science/models/{ft_source.id}?region={self.app.region}", }, "tags": { - "aqua_finetuning": "aqua_finetuning", - "finetuning_experiment_id": f"{mock_mvs_create.return_value[0]}", - "finetuning_job_id": f"{mock_job_id.return_value}", - "finetuning_source": f"{ft_source.id}", + **{ + "aqua_finetuning": "aqua_finetuning", + "finetuning_experiment_id": f"{mock_mvs_create.return_value[0]}", + "finetuning_job_id": f"{mock_job_id.return_value}", + "finetuning_source": f"{ft_source.id}", + }, + **ft_model_freeform_tags, + **ft_model_defined_tags, }, "time_created": f"{ft_model.time_created}", } From 6c7a72f5a3b268e1a25c5133371991032341a1fe Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 15:16:56 -0800 Subject: [PATCH 04/10] support input tags for evaluation --- ads/aqua/evaluation/entities.py | 6 +++ ads/aqua/evaluation/evaluation.py | 41 +++++++++++++++---- .../with_extras/aqua/test_evaluation.py | 17 ++++++-- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/ads/aqua/evaluation/entities.py b/ads/aqua/evaluation/entities.py index ad49c88ae..bb165edd8 100644 --- a/ads/aqua/evaluation/entities.py +++ b/ads/aqua/evaluation/entities.py @@ -64,6 +64,10 @@ class CreateAquaEvaluationDetails(Serializable): The metrics for the evaluation. force_overwrite: (bool, optional). Defaults to `False`. Whether to force overwrite the existing file in object storage. + freeform_tags: (dict, optional) + Freeform tags for the evaluation model + defined_tags: (dict, optional) + Defined tags for the evaluation model """ evaluation_source_id: str @@ -85,6 +89,8 @@ class CreateAquaEvaluationDetails(Serializable): log_id: Optional[str] = None metrics: Optional[List[Dict[str, Any]]] = None force_overwrite: Optional[bool] = False + freeform_tags: Optional[dict] = None + defined_tags: Optional[dict] = None class Config: extra = "ignore" diff --git a/ads/aqua/evaluation/evaluation.py b/ads/aqua/evaluation/evaluation.py index cfd0029e6..c4585819d 100644 --- a/ads/aqua/evaluation/evaluation.py +++ b/ads/aqua/evaluation/evaluation.py @@ -297,6 +297,10 @@ def create( evaluation_mvs_freeform_tags = { Tags.AQUA_EVALUATION: Tags.AQUA_EVALUATION, } + evaluation_mvs_freeform_tags = { + **evaluation_mvs_freeform_tags, + **(create_aqua_evaluation_details.freeform_tags or {}), + } model_version_set = ( ModelVersionSet() @@ -307,6 +311,9 @@ def create( create_aqua_evaluation_details.experiment_description ) .with_freeform_tags(**evaluation_mvs_freeform_tags) + .with_defined_tags( + **(create_aqua_evaluation_details.defined_tags or {}) + ) # TODO: decide what parameters will be needed .create(**kwargs) ) @@ -369,6 +376,10 @@ def create( Tags.AQUA_EVALUATION: Tags.AQUA_EVALUATION, Tags.AQUA_EVALUATION_MODEL_ID: evaluation_model.id, } + evaluation_job_freeform_tags = { + **evaluation_job_freeform_tags, + **(create_aqua_evaluation_details.freeform_tags or {}), + } evaluation_job = Job(name=evaluation_model.display_name).with_infrastructure( DataScienceJob() @@ -379,6 +390,7 @@ def create( .with_shape_name(create_aqua_evaluation_details.shape_name) .with_block_storage_size(create_aqua_evaluation_details.block_storage_size) .with_freeform_tag(**evaluation_job_freeform_tags) + .with_defined_tag(**(create_aqua_evaluation_details.defined_tags or {})) ) if ( create_aqua_evaluation_details.memory_in_gbs @@ -425,6 +437,7 @@ def create( evaluation_job_run = evaluation_job.run( name=evaluation_model.display_name, freeform_tags=evaluation_job_freeform_tags, + defined_tags=(create_aqua_evaluation_details.defined_tags or {}), wait=False, ) logger.debug( @@ -444,13 +457,23 @@ def create( for metadata in evaluation_model_custom_metadata.to_dict()["data"] ] + evaluation_model_freeform_tags = { + Tags.AQUA_EVALUATION: Tags.AQUA_EVALUATION, + } + evaluation_model_freeform_tags = { + **evaluation_model_freeform_tags, + **(create_aqua_evaluation_details.freeform_tags or {}), + } + evaluation_model_defined_tags = ( + create_aqua_evaluation_details.defined_tags or {} + ) + self.ds_client.update_model( model_id=evaluation_model.id, update_model_details=UpdateModelDetails( custom_metadata_list=updated_custom_metadata_list, - freeform_tags={ - Tags.AQUA_EVALUATION: Tags.AQUA_EVALUATION, - }, + freeform_tags=evaluation_model_freeform_tags, + defined_tags=evaluation_model_defined_tags, ), ) @@ -520,10 +543,14 @@ def create( ), ), tags={ - "aqua_evaluation": Tags.AQUA_EVALUATION, - "evaluation_job_id": evaluation_job.id, - "evaluation_source": create_aqua_evaluation_details.evaluation_source_id, - "evaluation_experiment_id": experiment_model_version_set_id, + **{ + "aqua_evaluation": Tags.AQUA_EVALUATION, + "evaluation_job_id": evaluation_job.id, + "evaluation_source": create_aqua_evaluation_details.evaluation_source_id, + "evaluation_experiment_id": experiment_model_version_set_id, + }, + **evaluation_model_freeform_tags, + **evaluation_model_defined_tags, }, parameters=AquaEvalParams(), ) diff --git a/tests/unitary/with_extras/aqua/test_evaluation.py b/tests/unitary/with_extras/aqua/test_evaluation.py index 06f716061..ef3475184 100644 --- a/tests/unitary/with_extras/aqua/test_evaluation.py +++ b/tests/unitary/with_extras/aqua/test_evaluation.py @@ -475,6 +475,9 @@ def test_create_evaluation( self.app.ds_client.update_model = MagicMock() self.app.ds_client.update_model_provenance = MagicMock() + eval_model_freeform_tags = {"ftag1": "fvalue1", "ftag2": "fvalue2"} + eval_model_defined_tags = {"dtag1": "dvalue1", "dtag2": "dvalue2"} + create_aqua_evaluation_details = dict( evaluation_source_id="ocid1.datasciencemodel.oc1.iad.", evaluation_name="test_evaluation_name", @@ -486,6 +489,8 @@ def test_create_evaluation( experiment_name="test_experiment_name", memory_in_gbs=1, ocpus=1, + freeform_tags=eval_model_freeform_tags, + defined_tags=eval_model_defined_tags, ) aqua_evaluation_summary = self.app.create(**create_aqua_evaluation_details) @@ -516,10 +521,14 @@ def test_create_evaluation( "url": f"https://cloud.oracle.com/data-science/models/ocid1.datasciencemodel.oc1.iad.?region={self.app.region}", }, "tags": { - "aqua_evaluation": "aqua_evaluation", - "evaluation_experiment_id": f"{experiment.id}", - "evaluation_job_id": f"{mock_job_id.return_value}", - "evaluation_source": "ocid1.datasciencemodel.oc1.iad.", + **{ + "aqua_evaluation": "aqua_evaluation", + "evaluation_experiment_id": f"{experiment.id}", + "evaluation_job_id": f"{mock_job_id.return_value}", + "evaluation_source": "ocid1.datasciencemodel.oc1.iad.", + }, + **eval_model_freeform_tags, + **eval_model_defined_tags, }, "time_created": f"{oci_dsc_model.time_created}", } From 013337df4dd77017cca9e19d89fbf414293b4e99 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 21:34:58 -0800 Subject: [PATCH 05/10] fix FT test --- tests/unitary/with_extras/aqua/test_finetuning.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unitary/with_extras/aqua/test_finetuning.py b/tests/unitary/with_extras/aqua/test_finetuning.py index 7c07db9a9..c3afc80e8 100644 --- a/tests/unitary/with_extras/aqua/test_finetuning.py +++ b/tests/unitary/with_extras/aqua/test_finetuning.py @@ -90,6 +90,10 @@ def test_create_fine_tuning( ft_source.compartment_id = self.SERVICE_COMPARTMENT_ID ft_source.display_name = "test_ft_source_model" ft_source.custom_metadata_list = custom_metadata_list + ft_source.freeform_tags = { + "license": "Some license text", + "aqua_custom_base_model": "base_model_info", + } mock_get_source.return_value = ft_source mock_mvs_create.return_value = ("test_experiment_id", "test_experiment_name") @@ -177,6 +181,9 @@ def test_create_fine_tuning( "finetuning_experiment_id": f"{mock_mvs_create.return_value[0]}", "finetuning_job_id": f"{mock_job_id.return_value}", "finetuning_source": f"{ft_source.id}", + "ready_to_fine_tune": "false", + "OCI_AQUA": "", + "aqua_fine_tuned_model": f"{ft_source.id}#{ft_source.display_name}", }, **ft_model_freeform_tags, **ft_model_defined_tags, From 6490395a61a9efabe3088a8bcdece76fd32eb3d8 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 21:37:16 -0800 Subject: [PATCH 06/10] fix scikit-learn deprecation issue --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 72e9de30d..71204c9e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ dependencies = [ "psutil>=5.7.2", "python_jsonschema_objects>=0.3.13", "requests", - "scikit-learn>=1.0", + "scikit-learn>=1.0,<1.6.0", "tabulate>=0.8.9", "tqdm>=4.59.0", "pydantic>=2.6.3", @@ -179,7 +179,7 @@ anomaly = [ "oracledb", "report-creator==1.0.28", "rrcf==0.4.4", - "scikit-learn", + "scikit-learn<1.6.0", "salesforce-merlion[all]==2.0.4" ] recommender = [ From e203222baef2615ea8c407b8b17cc23632114bf7 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 21:49:15 -0800 Subject: [PATCH 07/10] review comments --- ads/aqua/evaluation/evaluation.py | 10 ++++------ ads/aqua/finetuning/finetuning.py | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ads/aqua/evaluation/evaluation.py b/ads/aqua/evaluation/evaluation.py index c4585819d..c235e8143 100644 --- a/ads/aqua/evaluation/evaluation.py +++ b/ads/aqua/evaluation/evaluation.py @@ -543,12 +543,10 @@ def create( ), ), tags={ - **{ - "aqua_evaluation": Tags.AQUA_EVALUATION, - "evaluation_job_id": evaluation_job.id, - "evaluation_source": create_aqua_evaluation_details.evaluation_source_id, - "evaluation_experiment_id": experiment_model_version_set_id, - }, + "aqua_evaluation": Tags.AQUA_EVALUATION, + "evaluation_job_id": evaluation_job.id, + "evaluation_source": create_aqua_evaluation_details.evaluation_source_id, + "evaluation_experiment_id": experiment_model_version_set_id, **evaluation_model_freeform_tags, **evaluation_model_defined_tags, }, diff --git a/ads/aqua/finetuning/finetuning.py b/ads/aqua/finetuning/finetuning.py index e1457b3f4..d8c2e9bc0 100644 --- a/ads/aqua/finetuning/finetuning.py +++ b/ads/aqua/finetuning/finetuning.py @@ -477,12 +477,10 @@ def create( ), ), tags={ - **{ - "aqua_finetuning": Tags.AQUA_FINE_TUNING, - "finetuning_job_id": ft_job.id, - "finetuning_source": source.id, - "finetuning_experiment_id": experiment_model_version_set_id, - }, + "aqua_finetuning": Tags.AQUA_FINE_TUNING, + "finetuning_job_id": ft_job.id, + "finetuning_source": source.id, + "finetuning_experiment_id": experiment_model_version_set_id, **model_freeform_tags, **model_defined_tags, }, From 82d1ae26398d5847cb5d53c0003515c59bc15ced Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 22:16:29 -0800 Subject: [PATCH 08/10] update model and deployment handlers --- ads/aqua/extension/deployment_handler.py | 12 ++++++++---- ads/aqua/extension/model_handler.py | 16 +++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ads/aqua/extension/deployment_handler.py b/ads/aqua/extension/deployment_handler.py index 88ad84272..2a3e827c3 100644 --- a/ads/aqua/extension/deployment_handler.py +++ b/ads/aqua/extension/deployment_handler.py @@ -59,7 +59,7 @@ def delete(self, model_deployment_id): return self.finish(AquaDeploymentApp().delete(model_deployment_id)) @handle_exceptions - def put(self, *args, **kwargs): + def put(self, *args, **kwargs): # noqa: ARG002 """ Handles put request for the activating and deactivating OCI datascience model deployments Raises @@ -82,7 +82,7 @@ def put(self, *args, **kwargs): raise HTTPError(400, f"The request {self.request.path} is invalid.") @handle_exceptions - def post(self, *args, **kwargs): + def post(self, *args, **kwargs): # noqa: ARG002 """ Handles post request for the deployment APIs Raises @@ -132,6 +132,8 @@ def post(self, *args, **kwargs): private_endpoint_id = input_data.get("private_endpoint_id") container_image_uri = input_data.get("container_image_uri") cmd_var = input_data.get("cmd_var") + freeform_tags = input_data.get("freeform_tags") + defined_tags = input_data.get("defined_tags") self.finish( AquaDeploymentApp().create( @@ -157,6 +159,8 @@ def post(self, *args, **kwargs): private_endpoint_id=private_endpoint_id, container_image_uri=container_image_uri, cmd_var=cmd_var, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) ) @@ -196,7 +200,7 @@ def validate_predict_url(endpoint): return False @handle_exceptions - def post(self, *args, **kwargs): + def post(self, *args, **kwargs): # noqa: ARG002 """ Handles inference request for the Active Model Deployments Raises @@ -262,7 +266,7 @@ def get(self, model_id): ) @handle_exceptions - def post(self, *args, **kwargs): + def post(self, *args, **kwargs): # noqa: ARG002 """Handles post request for the deployment param handler API. Raises diff --git a/ads/aqua/extension/model_handler.py b/ads/aqua/extension/model_handler.py index 1a322d801..42f90ffef 100644 --- a/ads/aqua/extension/model_handler.py +++ b/ads/aqua/extension/model_handler.py @@ -96,7 +96,7 @@ def list(self): ) @handle_exceptions - def post(self, *args, **kwargs): + def post(self, *args, **kwargs): # noqa: ARG002 """ Handles post request for the registering any Aqua model. Raises @@ -131,6 +131,8 @@ def post(self, *args, **kwargs): inference_container_uri = input_data.get("inference_container_uri") allow_patterns = input_data.get("allow_patterns") ignore_patterns = input_data.get("ignore_patterns") + freeform_tags = input_data.get("freeform_tags") + defined_tags = input_data.get("defined_tags") return self.finish( AquaModelApp().register( @@ -145,6 +147,8 @@ def post(self, *args, **kwargs): inference_container_uri=inference_container_uri, allow_patterns=allow_patterns, ignore_patterns=ignore_patterns, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) ) @@ -170,11 +174,9 @@ def put(self, id): enable_finetuning = input_data.get("enable_finetuning") task = input_data.get("task") - app=AquaModelApp() + app = AquaModelApp() self.finish( - app.edit_registered_model( - id, inference_container, enable_finetuning, task - ) + app.edit_registered_model(id, inference_container, enable_finetuning, task) ) app.clear_model_details_cache(model_id=id) @@ -218,7 +220,7 @@ def _find_matching_aqua_model(model_id: str) -> Optional[AquaModelSummary]: return None @handle_exceptions - def get(self, *args, **kwargs): + def get(self, *args, **kwargs): # noqa: ARG002 """ Finds a list of matching models from hugging face based on query string provided from users. @@ -239,7 +241,7 @@ def get(self, *args, **kwargs): return self.finish({"models": models}) @handle_exceptions - def post(self, *args, **kwargs): + def post(self, *args, **kwargs): # noqa: ARG002 """Handles post request for the HF Models APIs Raises From 07feacbcaf12b05b5bc3c1c8d098850d66b8c9b8 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 22:27:40 -0800 Subject: [PATCH 09/10] minor improvements --- ads/aqua/evaluation/evaluation.py | 3 --- ads/aqua/finetuning/finetuning.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/ads/aqua/evaluation/evaluation.py b/ads/aqua/evaluation/evaluation.py index c235e8143..0b7cb7773 100644 --- a/ads/aqua/evaluation/evaluation.py +++ b/ads/aqua/evaluation/evaluation.py @@ -459,9 +459,6 @@ def create( evaluation_model_freeform_tags = { Tags.AQUA_EVALUATION: Tags.AQUA_EVALUATION, - } - evaluation_model_freeform_tags = { - **evaluation_model_freeform_tags, **(create_aqua_evaluation_details.freeform_tags or {}), } evaluation_model_defined_tags = ( diff --git a/ads/aqua/finetuning/finetuning.py b/ads/aqua/finetuning/finetuning.py index d8c2e9bc0..5ff03276b 100644 --- a/ads/aqua/finetuning/finetuning.py +++ b/ads/aqua/finetuning/finetuning.py @@ -277,9 +277,6 @@ def create( ft_job_freeform_tags = { Tags.AQUA_TAG: UNKNOWN, Tags.AQUA_FINE_TUNED_MODEL_TAG: f"{source.id}#{source.display_name}", - } - ft_job_freeform_tags = { - **ft_job_freeform_tags, **(create_fine_tuning_details.freeform_tags or {}), } From 612bf71587d7eff6eb7ec771e6443f3913d89a12 Mon Sep 17 00:00:00 2001 From: Vipul Date: Mon, 9 Dec 2024 22:49:43 -0800 Subject: [PATCH 10/10] add handler tests --- .../aqua/test_deployment_handler.py | 4 ++ .../with_extras/aqua/test_model_handler.py | 46 ++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/tests/unitary/with_extras/aqua/test_deployment_handler.py b/tests/unitary/with_extras/aqua/test_deployment_handler.py index e6f5acc45..95be6c351 100644 --- a/tests/unitary/with_extras/aqua/test_deployment_handler.py +++ b/tests/unitary/with_extras/aqua/test_deployment_handler.py @@ -29,6 +29,8 @@ class TestDataset: "model_id": "ocid1.datasciencemodel.oc1.iad.", "instance_shape": "VM.GPU.A10.1", "display_name": "test-deployment-name", + "freeform_tags": {"ftag1": "fvalue1", "ftag2": "fvalue2"}, + "defined_tags": {"dtag1": "dvalue1", "dtag2": "dvalue2"}, } inference_request = { "prompt": "What is 1+1?", @@ -156,6 +158,8 @@ def test_post(self, mock_create): private_endpoint_id=None, container_image_uri=None, cmd_var=None, + freeform_tags=TestDataset.deployment_request["freeform_tags"], + defined_tags=TestDataset.deployment_request["defined_tags"], ) diff --git a/tests/unitary/with_extras/aqua/test_model_handler.py b/tests/unitary/with_extras/aqua/test_model_handler.py index 0367d4c3c..bf02174b9 100644 --- a/tests/unitary/with_extras/aqua/test_model_handler.py +++ b/tests/unitary/with_extras/aqua/test_model_handler.py @@ -132,10 +132,38 @@ def test_list(self, mock_list): @parameterized.expand( [ - (None, None, False, None, None, None), - ("odsc-llm-fine-tuning", None, False, None, None, ["test.json"]), - (None, "test.gguf", True, None, ["*.json"], None), - (None, None, True, "iad.ocir.io//:", ["*.json"], ["test.json"]), + (None, None, False, None, None, None, None, None), + ( + "odsc-llm-fine-tuning", + None, + False, + None, + None, + ["test.json"], + None, + None, + ), + (None, "test.gguf", True, None, ["*.json"], None, None, None), + ( + None, + None, + True, + "iad.ocir.io//:", + ["*.json"], + ["test.json"], + None, + None, + ), + ( + None, + None, + False, + None, + None, + None, + {"ftag1": "fvalue1"}, + {"dtag1": "dvalue1"}, + ), ], ) @patch("notebook.base.handlers.APIHandler.finish") @@ -148,6 +176,8 @@ def test_register( inference_container_uri, allow_patterns, ignore_patterns, + freeform_tags, + defined_tags, mock_register, mock_finish, ): @@ -168,7 +198,9 @@ def test_register( download_from_hf=download_from_hf, inference_container_uri=inference_container_uri, allow_patterns=allow_patterns, - ignore_patterns=ignore_patterns + ignore_patterns=ignore_patterns, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) ) result = self.model_handler.post() @@ -183,7 +215,9 @@ def test_register( download_from_hf=download_from_hf, inference_container_uri=inference_container_uri, allow_patterns=allow_patterns, - ignore_patterns=ignore_patterns + ignore_patterns=ignore_patterns, + freeform_tags=freeform_tags, + defined_tags=defined_tags, ) assert result["id"] == "test_id" assert result["inference_container"] == "odsc-tgi-serving"