diff --git a/ads/aqua/client/client.py b/ads/aqua/client/client.py index b2a0490a7..a36e4ffc6 100644 --- a/ads/aqua/client/client.py +++ b/ads/aqua/client/client.py @@ -582,6 +582,19 @@ def embeddings( payload = {**(payload or {}), "input": input} return self._request(payload=payload, headers=headers) + def fetch_data(self) -> Union[Dict[str, Any], Iterator[Mapping[str, Any]]]: + """Fetch Data in json format by sending a request to the endpoint. + + Args: + + Returns: + Union[Dict[str, Any], Iterator[Mapping[str, Any]]]: The server's response, typically including the data in JSON format. + """ + # headers = {"Content-Type", "application/json"} + response = self._client.get(self.endpoint) + json_response = response.json() + return json_response + class AsyncClient(BaseClient): """ diff --git a/ads/aqua/extension/deployment_handler.py b/ads/aqua/extension/deployment_handler.py index 957fc486a..b3205eb53 100644 --- a/ads/aqua/extension/deployment_handler.py +++ b/ads/aqua/extension/deployment_handler.py @@ -373,6 +373,37 @@ def post(self, *args, **kwargs): # noqa: ARG002 ) +class AquaModelListHandler(AquaAPIhandler): + """Handler for Aqua model list params REST APIs. + + Methods + ------- + get(self, *args, **kwargs) + Validates parameters for the given model id. + """ + + @handle_exceptions + def get(self, model_deployment_id): + """ + Handles get model list for the Active Model Deployment + Raises + ------ + HTTPError + Raises HTTPError if inputs are missing or are invalid + """ + + self.set_header("Content-Type", "application/json") + endpoint: str = "" + model_deployment = AquaDeploymentApp().get(model_deployment_id) + endpoint = model_deployment.endpoint.rstrip("/") + "/predict/v1/models" + aqua_client = Client(endpoint=endpoint) + try: + list_model_result = aqua_client.fetch_data() + return self.finish(list_model_result) + except Exception as ex: + raise HTTPError(500, str(ex)) + + __handlers__ = [ ("deployments/?([^/]*)/params", AquaDeploymentParamsHandler), ("deployments/config/?([^/]*)", AquaDeploymentHandler), @@ -381,4 +412,5 @@ def post(self, *args, **kwargs): # noqa: ARG002 ("deployments/?([^/]*)/activate", AquaDeploymentHandler), ("deployments/?([^/]*)/deactivate", AquaDeploymentHandler), ("inference/stream/?([^/]*)", AquaDeploymentStreamingInferenceHandler), + ("deployments/models/list/?([^/]*)", AquaModelListHandler), ] diff --git a/tests/unitary/default_setup/model_deployment/test_model_deployment_v2.py b/tests/unitary/default_setup/model_deployment/test_model_deployment_v2.py index 589c58d70..edc298e34 100644 --- a/tests/unitary/default_setup/model_deployment/test_model_deployment_v2.py +++ b/tests/unitary/default_setup/model_deployment/test_model_deployment_v2.py @@ -372,6 +372,7 @@ def test__load_default_properties(self, mock_from_ocid): ModelDeploymentInfrastructure.CONST_SHAPE_CONFIG_DETAILS: { "ocpus": 10.0, "memory_in_gbs": 36.0, + "cpu_baseline": None, }, ModelDeploymentInfrastructure.CONST_REPLICA: 1, } @@ -886,7 +887,7 @@ def test_model_deployment_from_dict(self): def test_update_model_deployment_details(self, mock_create): dsc_model = MagicMock() dsc_model.id = "fakeid.datasciencemodel.oc1.iad.xxx" - mock_create.return_value = dsc_model + mock_create.return_value = dsc_model model_deployment = self.initialize_model_deployment() update_model_deployment_details = ( model_deployment._update_model_deployment_details() @@ -1127,9 +1128,7 @@ def test_from_ocid(self, mock_from_ocid): "create_model_deployment", ) @patch.object(DataScienceModel, "create") - def test_deploy( - self, mock_create, mock_create_model_deployment, mock_sync - ): + def test_deploy(self, mock_create, mock_create_model_deployment, mock_sync): dsc_model = MagicMock() dsc_model.id = "fakeid.datasciencemodel.oc1.iad.xxx" mock_create.return_value = dsc_model @@ -1346,44 +1345,35 @@ def test_update_spec(self): model_deployment = self.initialize_model_deployment() model_deployment._update_spec( display_name="test_updated_name", - freeform_tags={"test_updated_key":"test_updated_value"}, - access_log={ - "log_id": "test_updated_access_log_id" - }, - predict_log={ - "log_group_id": "test_updated_predict_log_group_id" - }, - shape_config_details={ - "ocpus": 100, - "memoryInGBs": 200 - }, + freeform_tags={"test_updated_key": "test_updated_value"}, + access_log={"log_id": "test_updated_access_log_id"}, + predict_log={"log_group_id": "test_updated_predict_log_group_id"}, + shape_config_details={"ocpus": 100, "memoryInGBs": 200}, replica=20, image="test_updated_image", - env={ - "test_updated_env_key":"test_updated_env_value" - } + env={"test_updated_env_key": "test_updated_env_value"}, ) assert model_deployment.display_name == "test_updated_name" assert model_deployment.freeform_tags == { - "test_updated_key":"test_updated_value" + "test_updated_key": "test_updated_value" } assert model_deployment.infrastructure.access_log == { "logId": "test_updated_access_log_id", - "logGroupId": "fakeid.loggroup.oc1.iad.xxx" + "logGroupId": "fakeid.loggroup.oc1.iad.xxx", } assert model_deployment.infrastructure.predict_log == { "logId": "fakeid.log.oc1.iad.xxx", - "logGroupId": "test_updated_predict_log_group_id" + "logGroupId": "test_updated_predict_log_group_id", } assert model_deployment.infrastructure.shape_config_details == { "ocpus": 100, - "memoryInGBs": 200 + "memoryInGBs": 200, } assert model_deployment.infrastructure.replica == 20 assert model_deployment.runtime.image == "test_updated_image" assert model_deployment.runtime.env == { - "test_updated_env_key":"test_updated_env_value" + "test_updated_env_key": "test_updated_env_value" } @patch.object(OCIDataScienceMixin, "sync") @@ -1393,18 +1383,14 @@ def test_update_spec(self): ) @patch.object(DataScienceModel, "create") def test_model_deployment_with_large_size_artifact( - self, - mock_create, - mock_create_model_deployment, - mock_sync + self, mock_create, mock_create_model_deployment, mock_sync ): dsc_model = MagicMock() dsc_model.id = "fakeid.datasciencemodel.oc1.iad.xxx" mock_create.return_value = dsc_model model_deployment = self.initialize_model_deployment() ( - model_deployment.runtime - .with_auth({"test_key":"test_value"}) + model_deployment.runtime.with_auth({"test_key": "test_value"}) .with_region("test_region") .with_overwrite_existing_artifact(True) .with_remove_existing_artifact(True) @@ -1425,18 +1411,18 @@ def test_model_deployment_with_large_size_artifact( mock_create_model_deployment.return_value = response model_deployment = self.initialize_model_deployment() model_deployment.set_spec(model_deployment.CONST_ID, "test_model_deployment_id") - + create_model_deployment_details = ( model_deployment._build_model_deployment_details() ) model_deployment.deploy(wait_for_completion=False) mock_create.assert_called_with( bucket_uri="test_bucket_uri", - auth={"test_key":"test_value"}, + auth={"test_key": "test_value"}, region="test_region", overwrite_existing_artifact=True, remove_existing_artifact=True, - timeout=100 + timeout=100, ) mock_create_model_deployment.assert_called_with(create_model_deployment_details) mock_sync.assert_called() diff --git a/tests/unitary/default_setup/pipeline/test_pipeline.py b/tests/unitary/default_setup/pipeline/test_pipeline.py index 302dfe065..a41688758 100644 --- a/tests/unitary/default_setup/pipeline/test_pipeline.py +++ b/tests/unitary/default_setup/pipeline/test_pipeline.py @@ -32,7 +32,7 @@ nb_session_ocid="ocid1.datasciencenotebooksession.oc1.iad..", shape_name="VM.Standard.E3.Flex", block_storage_size_in_gbs=100, - shape_config_details={"ocpus": 1, "memory_in_gbs": 16}, + shape_config_details={"ocpus": 1.0, "memory_in_gbs": 16.0, "cpu_baseline": None}, ) PIPELINE_OCID = "ocid.xxx.datasciencepipeline." @@ -334,10 +334,8 @@ def test_pipeline_define(self): "jobId": "TestJobIdOne", "description": "Test description one", "commandLineArguments": "ARGUMENT --KEY VALUE", - "environmentVariables": { - "ENV": "VALUE" - }, - "maximumRuntimeInMinutes": 20 + "environmentVariables": {"ENV": "VALUE"}, + "maximumRuntimeInMinutes": 20, }, }, { @@ -1066,10 +1064,8 @@ def test_pipeline_to_dict(self): "jobId": "TestJobIdOne", "description": "Test description one", "commandLineArguments": "ARGUMENT --KEY VALUE", - "environmentVariables": { - "ENV": "VALUE" - }, - "maximumRuntimeInMinutes": 20 + "environmentVariables": {"ENV": "VALUE"}, + "maximumRuntimeInMinutes": 20, }, }, { diff --git a/tests/unitary/with_extras/aqua/test_deployment_handler.py b/tests/unitary/with_extras/aqua/test_deployment_handler.py index 1bdfe85b5..3d4f81c2e 100644 --- a/tests/unitary/with_extras/aqua/test_deployment_handler.py +++ b/tests/unitary/with_extras/aqua/test_deployment_handler.py @@ -13,11 +13,13 @@ from parameterized import parameterized import ads.aqua +from ads.aqua.modeldeployment.entities import AquaDeploymentDetail import ads.config from ads.aqua.extension.deployment_handler import ( AquaDeploymentHandler, AquaDeploymentParamsHandler, AquaDeploymentStreamingInferenceHandler, + AquaModelListHandler, ) @@ -260,3 +262,25 @@ def test_post(self, mock_get_model_deployment_response): self.handler.write.assert_any_call("chunk1") self.handler.write.assert_any_call("chunk2") self.handler.finish.assert_called_once() + + +class AquaModelListHandlerTestCase(unittest.TestCase): + default_params = { + "data": [{"id": "id", "object": "object", "owned_by": "openAI", "created": 124}] + } + + @patch.object(IPythonHandler, "__init__") + def setUp(self, ipython_init_mock) -> None: + ipython_init_mock.return_value = None + self.aqua_model_list_handler = AquaModelListHandler(MagicMock(), MagicMock()) + self.aqua_model_list_handler._headers = MagicMock() + + @patch("ads.aqua.modeldeployment.AquaDeploymentApp.get") + @patch("notebook.base.handlers.APIHandler.finish") + def test_get_model_list(self, mock_get, mock_finish): + """Test to check the handler get method to return model list.""" + + mock_get.return_value = MagicMock(id="test_model_id") + mock_finish.side_effect = lambda x: x + result = self.aqua_model_list_handler.get(model_id="test_model_id") + mock_get.assert_called()