oracle-ads 2.13.5__py3-none-any.whl → 2.13.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. ads/aqua/__init__.py +0 -5
  2. ads/aqua/app.py +133 -20
  3. ads/aqua/cli.py +2 -15
  4. ads/aqua/common/enums.py +14 -0
  5. ads/aqua/common/utils.py +50 -83
  6. ads/aqua/config/container_config.py +105 -69
  7. ads/aqua/config/evaluation/evaluation_service_config.py +40 -0
  8. ads/aqua/constants.py +22 -20
  9. ads/aqua/evaluation/evaluation.py +98 -32
  10. ads/aqua/extension/common_handler.py +3 -12
  11. ads/aqua/extension/common_ws_msg_handler.py +3 -24
  12. ads/aqua/extension/model_handler.py +59 -6
  13. ads/aqua/extension/models/ws_models.py +2 -0
  14. ads/aqua/extension/models_ws_msg_handler.py +1 -0
  15. ads/aqua/extension/utils.py +11 -24
  16. ads/aqua/finetuning/entities.py +23 -1
  17. ads/aqua/finetuning/finetuning.py +26 -10
  18. ads/aqua/model/constants.py +8 -0
  19. ads/aqua/model/entities.py +8 -1
  20. ads/aqua/model/model.py +286 -111
  21. ads/aqua/modeldeployment/deployment.py +51 -47
  22. ads/aqua/modeldeployment/utils.py +23 -5
  23. ads/aqua/ui.py +3 -4
  24. ads/config.py +2 -2
  25. ads/dataset/recommendation.py +11 -20
  26. ads/model/datascience_model.py +29 -0
  27. ads/model/service/oci_datascience_model.py +1 -1
  28. ads/opctl/operator/lowcode/anomaly/model/anomaly_dataset.py +8 -6
  29. ads/opctl/operator/lowcode/anomaly/model/base_model.py +1 -1
  30. ads/opctl/operator/lowcode/anomaly/operator_config.py +5 -3
  31. ads/opctl/operator/lowcode/common/transformations.py +2 -0
  32. ads/opctl/operator/lowcode/forecast/model/automlx.py +29 -0
  33. ads/opctl/operator/lowcode/pii/model/report.py +9 -16
  34. ads/opctl/utils.py +1 -1
  35. ads/type_discovery/typed_feature.py +32 -34
  36. {oracle_ads-2.13.5.dist-info → oracle_ads-2.13.7.dist-info}/METADATA +1 -1
  37. {oracle_ads-2.13.5.dist-info → oracle_ads-2.13.7.dist-info}/RECORD +40 -41
  38. ads/aqua/config/config.py +0 -31
  39. {oracle_ads-2.13.5.dist-info → oracle_ads-2.13.7.dist-info}/WHEEL +0 -0
  40. {oracle_ads-2.13.5.dist-info → oracle_ads-2.13.7.dist-info}/entry_points.txt +0 -0
  41. {oracle_ads-2.13.5.dist-info → oracle_ads-2.13.7.dist-info}/licenses/LICENSE.txt +0 -0
ads/aqua/model/model.py CHANGED
@@ -6,14 +6,14 @@ import os
6
6
  import pathlib
7
7
  from datetime import datetime, timedelta
8
8
  from threading import Lock
9
- from typing import Dict, List, Optional, Set, Union
9
+ from typing import Any, Dict, List, Optional, Set, Union
10
10
 
11
11
  import oci
12
12
  from cachetools import TTLCache
13
13
  from huggingface_hub import snapshot_download
14
14
  from oci.data_science.models import JobRun, Metadata, Model, UpdateModelDetails
15
15
 
16
- from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID, logger
16
+ from ads.aqua import logger
17
17
  from ads.aqua.app import AquaApp
18
18
  from ads.aqua.common.entities import AquaMultiModelRef
19
19
  from ads.aqua.common.enums import (
@@ -37,11 +37,10 @@ from ads.aqua.common.utils import (
37
37
  create_word_icon,
38
38
  generate_tei_cmd_var,
39
39
  get_artifact_path,
40
- get_container_config,
41
40
  get_hf_model_info,
41
+ get_preferred_compatible_family,
42
42
  list_os_files_with_extension,
43
43
  load_config,
44
- read_file,
45
44
  upload_folder,
46
45
  )
47
46
  from ads.aqua.config.container_config import AquaContainerConfig, Usage
@@ -53,7 +52,7 @@ from ads.aqua.constants import (
53
52
  AQUA_MODEL_TOKENIZER_CONFIG,
54
53
  AQUA_MODEL_TYPE_CUSTOM,
55
54
  HF_METADATA_FOLDER,
56
- LICENSE_TXT,
55
+ LICENSE,
57
56
  MODEL_BY_REFERENCE_OSS_PATH_KEY,
58
57
  README,
59
58
  READY_TO_DEPLOY_STATUS,
@@ -65,6 +64,7 @@ from ads.aqua.constants import (
65
64
  VALIDATION_METRICS_FINAL,
66
65
  )
67
66
  from ads.aqua.model.constants import (
67
+ AquaModelMetadataKeys,
68
68
  FineTuningCustomMetadata,
69
69
  FineTuningMetricCategories,
70
70
  ModelCustomMetadataFields,
@@ -75,6 +75,7 @@ from ads.aqua.model.entities import (
75
75
  AquaFineTuningMetric,
76
76
  AquaModel,
77
77
  AquaModelLicense,
78
+ AquaModelReadme,
78
79
  AquaModelSummary,
79
80
  ImportModelDetails,
80
81
  ModelValidationResult,
@@ -82,16 +83,24 @@ from ads.aqua.model.entities import (
82
83
  from ads.aqua.model.enums import MultiModelSupportedTaskType
83
84
  from ads.common.auth import default_signer
84
85
  from ads.common.oci_resource import SEARCH_TYPE, OCIResource
85
- from ads.common.utils import UNKNOWN, get_console_link
86
+ from ads.common.utils import (
87
+ UNKNOWN,
88
+ get_console_link,
89
+ is_path_exists,
90
+ read_file,
91
+ )
86
92
  from ads.config import (
87
93
  AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME,
88
94
  AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
89
95
  AQUA_DEPLOYMENT_CONTAINER_URI_METADATA_NAME,
90
96
  AQUA_EVALUATION_CONTAINER_METADATA_NAME,
91
97
  AQUA_FINETUNING_CONTAINER_METADATA_NAME,
98
+ AQUA_SERVICE_MODELS,
92
99
  COMPARTMENT_OCID,
93
100
  PROJECT_OCID,
101
+ SERVICE,
94
102
  TENANCY_OCID,
103
+ USER,
95
104
  )
96
105
  from ads.model import DataScienceModel
97
106
  from ads.model.common.utils import MetadataArtifactPathType
@@ -175,7 +184,8 @@ class AquaModelApp(AquaApp):
175
184
  target_project = project_id or PROJECT_OCID
176
185
  target_compartment = compartment_id or COMPARTMENT_OCID
177
186
 
178
- if service_model.compartment_id != ODSC_MODEL_COMPARTMENT_OCID:
187
+ # Skip model copying if it is registered model
188
+ if service_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None) is not None:
179
189
  logger.info(
180
190
  f"Aqua Model {model_id} already exists in the user's compartment."
181
191
  "Skipped copying."
@@ -260,18 +270,15 @@ class AquaModelApp(AquaApp):
260
270
  display_name_list = []
261
271
  model_custom_metadata = ModelCustomMetadata()
262
272
 
263
- # Get container config
264
- container_config = get_container_config()
265
-
266
- service_inference_containers = AquaContainerConfig.from_container_index_json(
267
- config=container_config
268
- ).inference.values()
273
+ service_inference_containers = (
274
+ self.get_container_config().to_dict().get("inference")
275
+ )
269
276
 
270
277
  supported_container_families = [
271
278
  container_config_item.family
272
279
  for container_config_item in service_inference_containers
273
280
  if any(
274
- usage in container_config_item.usages
281
+ usage.upper() in container_config_item.usages
275
282
  for usage in [Usage.MULTI_MODEL, Usage.OTHER]
276
283
  )
277
284
  ]
@@ -337,15 +344,25 @@ class AquaModelApp(AquaApp):
337
344
 
338
345
  selected_models_deployment_containers.add(deployment_container)
339
346
 
340
- # Check if the all models in the group shares same container family
341
- if len(selected_models_deployment_containers) > 1:
347
+ if not selected_models_deployment_containers:
342
348
  raise AquaValueError(
343
- "The selected models are associated with different container families: "
344
- f"{list(selected_models_deployment_containers)}."
345
- "For multi-model deployment, all models in the group must share the same container family."
349
+ "None of the selected models are associated with a recognized container family. "
350
+ "Please review the selected models, or select a different group of models."
346
351
  )
347
352
 
348
- deployment_container = selected_models_deployment_containers.pop()
353
+ # Check if the all models in the group shares same container family
354
+ if len(selected_models_deployment_containers) > 1:
355
+ deployment_container = get_preferred_compatible_family(
356
+ selected_families=selected_models_deployment_containers
357
+ )
358
+ if not deployment_container:
359
+ raise AquaValueError(
360
+ "The selected models are associated with different container families: "
361
+ f"{list(selected_models_deployment_containers)}."
362
+ "For multi-model deployment, all models in the group must share the same container family."
363
+ )
364
+ else:
365
+ deployment_container = selected_models_deployment_containers.pop()
349
366
 
350
367
  # Generate model group details
351
368
  timestamp = datetime.now().strftime("%Y%m%d")
@@ -421,7 +438,7 @@ class AquaModelApp(AquaApp):
421
438
  return custom_model
422
439
 
423
440
  @telemetry(entry_point="plugin=model&action=get", name="aqua")
424
- def get(self, model_id: str, load_model_card: Optional[bool] = True) -> "AquaModel":
441
+ def get(self, model_id: str) -> "AquaModel":
425
442
  """Gets the information of an Aqua model.
426
443
 
427
444
  Parameters
@@ -444,6 +461,7 @@ class AquaModelApp(AquaApp):
444
461
 
445
462
  logger.info(f"Fetching model details for model {model_id}.")
446
463
  ds_model = DataScienceModel.from_id(model_id)
464
+
447
465
  if not self._if_show(ds_model):
448
466
  raise AquaRuntimeError(
449
467
  f"Target model `{ds_model.id} `is not an Aqua model as it does not contain "
@@ -455,34 +473,6 @@ class AquaModelApp(AquaApp):
455
473
  and ds_model.freeform_tags.get(Tags.AQUA_FINE_TUNED_MODEL_TAG)
456
474
  )
457
475
 
458
- # todo: consolidate this logic in utils for model and deployment use
459
- is_verified_type = (
460
- ds_model.freeform_tags.get(Tags.READY_TO_IMPORT, "false").upper()
461
- == READY_TO_IMPORT_STATUS
462
- )
463
-
464
- model_card = ""
465
- if load_model_card:
466
- artifact_path = get_artifact_path(
467
- ds_model.custom_metadata_list._to_oci_metadata()
468
- )
469
- if artifact_path != UNKNOWN:
470
- model_card_path = (
471
- f"{artifact_path.rstrip('/')}/config/{README}"
472
- if is_verified_type
473
- else f"{artifact_path.rstrip('/')}/{README}"
474
- )
475
- model_card = str(
476
- read_file(
477
- file_path=model_card_path,
478
- auth=default_signer(),
479
- )
480
- )
481
- if not model_card:
482
- logger.warn(
483
- f"Model card for {model_id} is empty or could not be loaded from {model_card_path}."
484
- )
485
-
486
476
  inference_container = ds_model.custom_metadata_list.get(
487
477
  ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
488
478
  ModelCustomMetadataItem(key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER),
@@ -509,7 +499,6 @@ class AquaModelApp(AquaApp):
509
499
  aqua_model_attributes = dict(
510
500
  **self._process_model(ds_model, self.region),
511
501
  project_id=ds_model.project_id,
512
- model_card=model_card,
513
502
  inference_container=inference_container,
514
503
  inference_container_uri=inference_container_uri,
515
504
  finetuning_container=finetuning_container,
@@ -780,7 +769,9 @@ class AquaModelApp(AquaApp):
780
769
  ]
781
770
 
782
771
  def get_hf_tokenizer_config(self, model_id):
783
- """Gets the default chat template for the given Aqua model.
772
+ """
773
+ Gets the default model tokenizer config for the given Aqua model.
774
+ Returns the content of tokenizer_config.json stored in model artifact.
784
775
 
785
776
  Parameters
786
777
  ----------
@@ -789,14 +780,19 @@ class AquaModelApp(AquaApp):
789
780
 
790
781
  Returns
791
782
  -------
792
- str:
793
- Chat template string.
783
+ Dict:
784
+ Model tokenizer config.
794
785
  """
795
786
  config = self.get_config(
796
787
  model_id, AQUA_MODEL_TOKENIZER_CONFIG, ConfigFolder.ARTIFACT
797
788
  ).config
798
789
  if not config:
799
- logger.debug(f"Tokenizer config for model: {model_id} is not available.")
790
+ logger.debug(
791
+ f"{AQUA_MODEL_TOKENIZER_CONFIG} is not available for the model: {model_id}. "
792
+ f"Check if the custom metadata has the artifact path set."
793
+ )
794
+ return config
795
+
800
796
  return config
801
797
 
802
798
  @staticmethod
@@ -821,6 +817,7 @@ class AquaModelApp(AquaApp):
821
817
  oci.resource_search.models.ResourceSummary,
822
818
  ],
823
819
  region: str,
820
+ inference_containers: Optional[List[Any]] = None,
824
821
  ) -> dict:
825
822
  """Constructs required fields for AquaModelSummary."""
826
823
 
@@ -876,9 +873,10 @@ class AquaModelApp(AquaApp):
876
873
  except Exception:
877
874
  model_file = UNKNOWN
878
875
 
879
- inference_containers = AquaContainerConfig.from_container_index_json(
880
- config=get_container_config()
881
- ).inference
876
+ if not inference_containers:
877
+ inference_containers = (
878
+ AquaApp().get_container_config().to_dict().get("inference")
879
+ )
882
880
 
883
881
  model_formats_str = freeform_tags.get(
884
882
  Tags.MODEL_FORMAT, ModelFormat.SAFETENSORS
@@ -887,7 +885,7 @@ class AquaModelApp(AquaApp):
887
885
 
888
886
  supported_platform: Set[str] = set()
889
887
 
890
- for container in inference_containers.values():
888
+ for container in inference_containers:
891
889
  for model_format in model_formats:
892
890
  if model_format in container.model_formats:
893
891
  supported_platform.update(container.platforms)
@@ -921,12 +919,13 @@ class AquaModelApp(AquaApp):
921
919
  def list(
922
920
  self,
923
921
  compartment_id: str = None,
922
+ category: str = None,
924
923
  project_id: str = None,
925
924
  model_type: str = None,
926
925
  **kwargs,
927
926
  ) -> List["AquaModelSummary"]:
928
927
  """Lists all Aqua models within a specified compartment and/or project.
929
- If `compartment_id` is not specified, the method defaults to returning
928
+ If `category` is not specified, the method defaults to returning
930
929
  the service models within the pre-configured default compartment. By default, the list
931
930
  of models in the service compartment are cached. Use clear_model_list_cache() to invalidate
932
931
  the cache.
@@ -935,6 +934,8 @@ class AquaModelApp(AquaApp):
935
934
  ----------
936
935
  compartment_id: (str, optional). Defaults to `None`.
937
936
  The compartment OCID.
937
+ category: (str,optional). Defaults to `SERVICE`
938
+ The category of the models to fetch. Can be either `USER` or `SERVICE`
938
939
  project_id: (str, optional). Defaults to `None`.
939
940
  The project OCID.
940
941
  model_type: (str, optional). Defaults to `None`.
@@ -948,8 +949,9 @@ class AquaModelApp(AquaApp):
948
949
  The list of the `ads.aqua.model.AquaModelSummary`.
949
950
  """
950
951
 
951
- models = []
952
- if compartment_id:
952
+ category = category or kwargs.pop("category", SERVICE)
953
+ compartment_id = compartment_id or COMPARTMENT_OCID
954
+ if category == USER:
953
955
  # tracks number of times custom model listing was called
954
956
  self.telemetry.record_event_async(
955
957
  category="aqua/custom/model", action="list"
@@ -958,48 +960,47 @@ class AquaModelApp(AquaApp):
958
960
  logger.info(f"Fetching custom models from compartment_id={compartment_id}.")
959
961
  model_type = model_type.upper() if model_type else ModelType.FT
960
962
  models = self._rqs(compartment_id, model_type=model_type)
963
+ logger.info(
964
+ f"Fetched {len(models)} models from {compartment_id or COMPARTMENT_OCID}."
965
+ )
961
966
  else:
962
967
  # tracks number of times service model listing was called
963
968
  self.telemetry.record_event_async(
964
969
  category="aqua/service/model", action="list"
965
970
  )
966
971
 
967
- if ODSC_MODEL_COMPARTMENT_OCID in self._service_models_cache:
968
- logger.info(
969
- f"Returning service models list in {ODSC_MODEL_COMPARTMENT_OCID} from cache."
970
- )
971
- return self._service_models_cache.get(ODSC_MODEL_COMPARTMENT_OCID)
972
- logger.info(
973
- f"Fetching service models from compartment_id={ODSC_MODEL_COMPARTMENT_OCID}"
974
- )
972
+ if AQUA_SERVICE_MODELS in self._service_models_cache:
973
+ logger.info("Returning service models list from cache.")
974
+ return self._service_models_cache.get(AQUA_SERVICE_MODELS)
975
975
  lifecycle_state = kwargs.pop(
976
976
  "lifecycle_state", Model.LIFECYCLE_STATE_ACTIVE
977
977
  )
978
978
 
979
979
  models = self.list_resource(
980
980
  self.ds_client.list_models,
981
- compartment_id=ODSC_MODEL_COMPARTMENT_OCID,
981
+ compartment_id=compartment_id,
982
982
  lifecycle_state=lifecycle_state,
983
+ category=category,
983
984
  **kwargs,
984
985
  )
985
-
986
- logger.info(
987
- f"Fetched {len(models)} model in compartment_id={compartment_id or ODSC_MODEL_COMPARTMENT_OCID}."
988
- )
986
+ logger.info(f"Fetched {len(models)} service models.")
989
987
 
990
988
  aqua_models = []
991
-
989
+ inference_containers = self.get_container_config().to_dict().get("inference")
992
990
  for model in models:
993
991
  aqua_models.append(
994
992
  AquaModelSummary(
995
- **self._process_model(model=model, region=self.region),
993
+ **self._process_model(
994
+ model=model,
995
+ region=self.region,
996
+ inference_containers=inference_containers,
997
+ ),
996
998
  project_id=project_id or UNKNOWN,
997
999
  )
998
1000
  )
999
-
1000
- if not compartment_id:
1001
+ if category == SERVICE:
1001
1002
  self._service_models_cache.__setitem__(
1002
- key=ODSC_MODEL_COMPARTMENT_OCID, value=aqua_models
1003
+ key=AQUA_SERVICE_MODELS, value=aqua_models
1003
1004
  )
1004
1005
 
1005
1006
  return aqua_models
@@ -1015,15 +1016,10 @@ class AquaModelApp(AquaApp):
1015
1016
  """
1016
1017
  res = {}
1017
1018
  with self._cache_lock:
1018
- if ODSC_MODEL_COMPARTMENT_OCID in self._service_models_cache:
1019
- self._service_models_cache.pop(key=ODSC_MODEL_COMPARTMENT_OCID)
1020
- logger.info(
1021
- f"Cleared models cache for service compartment {ODSC_MODEL_COMPARTMENT_OCID}."
1022
- )
1019
+ if AQUA_SERVICE_MODELS in self._service_models_cache:
1020
+ self._service_models_cache.pop(key=AQUA_SERVICE_MODELS)
1021
+ logger.info("Cleared models cache for service compartment.")
1023
1022
  res = {
1024
- "key": {
1025
- "compartment_id": ODSC_MODEL_COMPARTMENT_OCID,
1026
- },
1027
1023
  "cache_deleted": True,
1028
1024
  }
1029
1025
  return res
@@ -1046,14 +1042,85 @@ class AquaModelApp(AquaApp):
1046
1042
 
1047
1043
  @staticmethod
1048
1044
  def list_valid_inference_containers():
1049
- containers = list(
1050
- AquaContainerConfig.from_container_index_json(
1051
- config=get_container_config(), enable_spec=True
1052
- ).inference.values()
1053
- )
1045
+ containers = AquaApp().get_container_config().to_dict().get("inference")
1054
1046
  family_values = [item.family for item in containers]
1055
1047
  return family_values
1056
1048
 
1049
+ @telemetry(
1050
+ entry_point="plugin=model&action=get_defined_metadata_artifact_content",
1051
+ name="aqua",
1052
+ )
1053
+ def get_defined_metadata_artifact_content(self, model_id: str, metadata_key: str):
1054
+ """
1055
+ Gets the defined metadata artifact content for the given model
1056
+
1057
+ Args:
1058
+ model_id: str
1059
+ model ocid for which defined metadata artifact needs to be created
1060
+ metadata_key: str
1061
+ defined metadata key like Readme , License , DeploymentConfiguration , FinetuningConfiguration
1062
+ Returns:
1063
+ The model defined metadata artifact content. Can be either str or Dict
1064
+
1065
+ """
1066
+
1067
+ content = self.get_config(model_id, metadata_key)
1068
+ if not content:
1069
+ logger.debug(
1070
+ f"Defined metadata artifact {metadata_key} for model: {model_id} is not available."
1071
+ )
1072
+ return content
1073
+
1074
+ @telemetry(
1075
+ entry_point="plugin=model&action=create_defined_metadata_artifact", name="aqua"
1076
+ )
1077
+ def create_defined_metadata_artifact(
1078
+ self,
1079
+ model_id: str,
1080
+ metadata_key: str,
1081
+ path_type: MetadataArtifactPathType,
1082
+ artifact_path_or_content: str,
1083
+ ) -> None:
1084
+ """
1085
+ Creates defined metadata artifact for the registered unverified model
1086
+
1087
+ Args:
1088
+ model_id: str
1089
+ model ocid for which defined metadata artifact needs to be created
1090
+ metadata_key: str
1091
+ defined metadata key like Readme , License , DeploymentConfiguration , FinetuningConfiguration
1092
+ path_type: str
1093
+ path type of the given defined metadata can be local , oss or the content itself
1094
+ artifact_path_or_content: str
1095
+ It can be local path or oss path or the actual content itself
1096
+ Returns:
1097
+ None
1098
+ """
1099
+
1100
+ ds_model = DataScienceModel.from_id(model_id)
1101
+ oci_aqua = ds_model.freeform_tags.get(Tags.AQUA_TAG, None)
1102
+ if not oci_aqua:
1103
+ raise AquaRuntimeError(f"Target model {model_id} is not an Aqua model.")
1104
+ is_registered_model = ds_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None)
1105
+ is_verified_model = ds_model.freeform_tags.get(
1106
+ Tags.AQUA_SERVICE_MODEL_TAG, None
1107
+ )
1108
+ if is_registered_model and not is_verified_model:
1109
+ try:
1110
+ ds_model.create_defined_metadata_artifact(
1111
+ metadata_key_name=metadata_key,
1112
+ artifact_path_or_content=artifact_path_or_content,
1113
+ path_type=path_type,
1114
+ )
1115
+ except Exception as ex:
1116
+ raise AquaRuntimeError(
1117
+ f"Error occurred in creating defined metadata artifact for model {model_id}: {ex}"
1118
+ ) from ex
1119
+ else:
1120
+ raise AquaRuntimeError(
1121
+ f"Cannot create defined metadata artifact for model {model_id}"
1122
+ )
1123
+
1057
1124
  def _create_model_catalog_entry(
1058
1125
  self,
1059
1126
  os_path: str,
@@ -1110,7 +1177,9 @@ class AquaModelApp(AquaApp):
1110
1177
 
1111
1178
  # Remove `ready_to_import` tag that might get copied from service model.
1112
1179
  tags.pop(Tags.READY_TO_IMPORT, None)
1113
-
1180
+ defined_metadata_dict = {}
1181
+ readme_file_path = os_path.rstrip("/") + "/" + README
1182
+ license_file_path = os_path.rstrip("/") + "/" + LICENSE
1114
1183
  if verified_model:
1115
1184
  # Verified model is a model in the service catalog that either has no artifacts but contains all the necessary metadata for deploying and fine tuning.
1116
1185
  # If set, then we copy all the model metadata.
@@ -1119,6 +1188,17 @@ class AquaModelApp(AquaApp):
1119
1188
  model = model.with_model_file_description(
1120
1189
  json_dict=verified_model.model_file_description
1121
1190
  )
1191
+ defined_metadata_list = (
1192
+ verified_model.defined_metadata_list._to_oci_metadata()
1193
+ )
1194
+ for defined_metadata in defined_metadata_list:
1195
+ if defined_metadata.has_artifact:
1196
+ content = (
1197
+ self.ds_client.get_model_defined_metadatum_artifact_content(
1198
+ verified_model.id, defined_metadata.key
1199
+ ).data.content
1200
+ )
1201
+ defined_metadata_dict[defined_metadata.key] = content
1122
1202
  else:
1123
1203
  metadata = ModelCustomMetadata()
1124
1204
  if not inference_container:
@@ -1140,12 +1220,14 @@ class AquaModelApp(AquaApp):
1140
1220
  category="Other",
1141
1221
  )
1142
1222
 
1143
- inference_containers = AquaContainerConfig.from_container_index_json(
1144
- config=get_container_config()
1145
- ).inference
1146
- smc_container_set = {
1147
- container.family for container in inference_containers.values()
1148
- }
1223
+ inference_containers = (
1224
+ AquaContainerConfig.from_service_config(
1225
+ service_containers=self.list_service_containers()
1226
+ )
1227
+ .to_dict()
1228
+ .get("inference")
1229
+ )
1230
+ smc_container_set = {container.family for container in inference_containers}
1149
1231
  # only add cmd vars if inference container is not an SMC
1150
1232
  if (
1151
1233
  inference_container not in smc_container_set
@@ -1214,6 +1296,33 @@ class AquaModelApp(AquaApp):
1214
1296
  .with_defined_tags(**(defined_tags or {}))
1215
1297
  ).create(model_by_reference=True)
1216
1298
  logger.debug(f"Created model catalog entry for the model:\n{model}")
1299
+ for key, value in defined_metadata_dict.items():
1300
+ model.create_defined_metadata_artifact(
1301
+ key, value, MetadataArtifactPathType.CONTENT
1302
+ )
1303
+
1304
+ if is_path_exists(readme_file_path):
1305
+ try:
1306
+ model.create_defined_metadata_artifact(
1307
+ AquaModelMetadataKeys.README,
1308
+ readme_file_path,
1309
+ MetadataArtifactPathType.OSS,
1310
+ )
1311
+ except Exception as ex:
1312
+ logger.error(
1313
+ f"Error Uploading Readme in defined metadata for model: {model.id} : {str(ex)}"
1314
+ )
1315
+ if not verified_model and is_path_exists(license_file_path):
1316
+ try:
1317
+ model.create_defined_metadata_artifact(
1318
+ AquaModelMetadataKeys.LICENSE,
1319
+ license_file_path,
1320
+ MetadataArtifactPathType.OSS,
1321
+ )
1322
+ except Exception as ex:
1323
+ logger.error(
1324
+ f"Error Uploading License in defined metadata for model: {model.id} : {str(ex)}"
1325
+ )
1217
1326
  return model
1218
1327
 
1219
1328
  @staticmethod
@@ -1728,6 +1837,7 @@ class AquaModelApp(AquaApp):
1728
1837
  ).rstrip("/")
1729
1838
  else:
1730
1839
  artifact_path = import_model_details.os_path.rstrip("/")
1840
+
1731
1841
  # Create Model catalog entry with pass by reference
1732
1842
  ds_model = self._create_model_catalog_entry(
1733
1843
  os_path=artifact_path,
@@ -1766,12 +1876,6 @@ class AquaModelApp(AquaApp):
1766
1876
  aqua_model_attributes = dict(
1767
1877
  **self._process_model(ds_model, self.region),
1768
1878
  project_id=ds_model.project_id,
1769
- model_card=str(
1770
- read_file(
1771
- file_path=f"{artifact_path}/{README}",
1772
- auth=default_signer(),
1773
- )
1774
- ),
1775
1879
  inference_container=inference_container,
1776
1880
  inference_container_uri=inference_container_uri,
1777
1881
  finetuning_container=finetuning_container,
@@ -1845,6 +1949,56 @@ class AquaModelApp(AquaApp):
1845
1949
  separator = " " if description else ""
1846
1950
  return f"{description}{separator}{tags_text}"
1847
1951
 
1952
+ @telemetry(entry_point="plugin=model&action=load_readme", name="aqua")
1953
+ def load_readme(self, model_id: str) -> AquaModelReadme:
1954
+ """Loads the readme or the model card for the given model.
1955
+
1956
+ Parameters
1957
+ ----------
1958
+ model_id: str
1959
+ The model id.
1960
+
1961
+ Returns
1962
+ -------
1963
+ AquaModelReadme:
1964
+ The instance of AquaModelReadme.
1965
+ """
1966
+ oci_model = self.ds_client.get_model(model_id).data
1967
+ artifact_path = get_artifact_path(oci_model.custom_metadata_list)
1968
+ if not artifact_path:
1969
+ raise AquaRuntimeError(
1970
+ f"Readme could not be loaded. Failed to get artifact path from custom metadata for"
1971
+ f"the model {model_id}."
1972
+ )
1973
+
1974
+ content = ""
1975
+ try:
1976
+ content = self.ds_client.get_model_defined_metadatum_artifact_content(
1977
+ model_id, AquaModelMetadataKeys.README
1978
+ ).data.content.decode("utf-8", errors="ignore")
1979
+ logger.info(f"Fetched {README} from defined metadata for model: {model_id}")
1980
+ except Exception as ex:
1981
+ logger.error(
1982
+ f"Readme could not be found for model: {model_id} in defined metadata : {str(ex)}"
1983
+ )
1984
+ artifact_path = get_artifact_path(oci_model.custom_metadata_list)
1985
+ readme_path = os.path.join(os.path.dirname(artifact_path), "artifact")
1986
+ if not is_path_exists(readme_path):
1987
+ readme_path = os.path.join(artifact_path.rstrip("/"), "artifact")
1988
+ if not is_path_exists(readme_path):
1989
+ readme_path = f"{artifact_path.rstrip('/')}/"
1990
+
1991
+ readme_file_path = os.path.join(readme_path, README)
1992
+ logger.info(f"Fetching {README} from {readme_file_path}")
1993
+ if is_path_exists(readme_file_path):
1994
+ try:
1995
+ content = str(read_file(readme_file_path, auth=default_signer()))
1996
+ except Exception as e:
1997
+ logger.debug(
1998
+ f"Error occurred while fetching config {README} at path {readme_file_path} : {str(e)}"
1999
+ )
2000
+ return AquaModelReadme(id=model_id, model_card=content)
2001
+
1848
2002
  @telemetry(entry_point="plugin=model&action=load_license", name="aqua")
1849
2003
  def load_license(self, model_id: str) -> AquaModelLicense:
1850
2004
  """Loads the license full text for the given model.
@@ -1867,13 +2021,34 @@ class AquaModelApp(AquaApp):
1867
2021
  f"the model {model_id}."
1868
2022
  )
1869
2023
 
1870
- content = str(
1871
- read_file(
1872
- file_path=f"{os.path.dirname(artifact_path)}/{LICENSE_TXT}",
1873
- auth=default_signer(),
2024
+ content = ""
2025
+ try:
2026
+ content = self.ds_client.get_model_defined_metadatum_artifact_content(
2027
+ model_id, AquaModelMetadataKeys.LICENSE
2028
+ ).data.content.decode("utf-8", errors="ignore")
2029
+ logger.info(
2030
+ f"Fetched {LICENSE} from defined metadata for model: {model_id}"
1874
2031
  )
1875
- )
1876
-
2032
+ except Exception as ex:
2033
+ logger.error(
2034
+ f"License could not be found for model: {model_id} in defined metadata : {str(ex)}"
2035
+ )
2036
+ artifact_path = get_artifact_path(oci_model.custom_metadata_list)
2037
+ license_path = os.path.join(os.path.dirname(artifact_path), "config")
2038
+ if not is_path_exists(license_path):
2039
+ license_path = os.path.join(artifact_path.rstrip("/"), "config")
2040
+ if not is_path_exists(license_path):
2041
+ license_path = f"{artifact_path.rstrip('/')}/"
2042
+
2043
+ license_file_path = os.path.join(license_path, LICENSE)
2044
+ logger.info(f"Fetching {LICENSE} from {license_file_path}")
2045
+ if is_path_exists(license_file_path):
2046
+ try:
2047
+ content = str(read_file(license_file_path, auth=default_signer()))
2048
+ except Exception as e:
2049
+ logger.debug(
2050
+ f"Error occurred while fetching config {LICENSE} at path {license_path} : {str(e)}"
2051
+ )
1877
2052
  return AquaModelLicense(id=model_id, license=content)
1878
2053
 
1879
2054
  def _find_matching_aqua_model(self, model_id: str) -> Optional[str]: