oracle-ads 2.11.8__py3-none-any.whl → 2.11.10__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 (87) hide show
  1. ads/aqua/__init__.py +1 -1
  2. ads/aqua/{base.py → app.py} +27 -7
  3. ads/aqua/cli.py +59 -17
  4. ads/aqua/common/__init__.py +5 -0
  5. ads/aqua/{decorator.py → common/decorator.py} +14 -8
  6. ads/aqua/common/enums.py +69 -0
  7. ads/aqua/{exception.py → common/errors.py} +28 -0
  8. ads/aqua/{utils.py → common/utils.py} +171 -79
  9. ads/aqua/config/config.py +18 -0
  10. ads/aqua/constants.py +51 -33
  11. ads/aqua/data.py +15 -26
  12. ads/aqua/evaluation/__init__.py +8 -0
  13. ads/aqua/evaluation/constants.py +53 -0
  14. ads/aqua/evaluation/entities.py +170 -0
  15. ads/aqua/evaluation/errors.py +71 -0
  16. ads/aqua/{evaluation.py → evaluation/evaluation.py} +122 -370
  17. ads/aqua/extension/__init__.py +2 -0
  18. ads/aqua/extension/aqua_ws_msg_handler.py +97 -0
  19. ads/aqua/extension/base_handler.py +0 -7
  20. ads/aqua/extension/common_handler.py +12 -6
  21. ads/aqua/extension/deployment_handler.py +70 -4
  22. ads/aqua/extension/errors.py +10 -0
  23. ads/aqua/extension/evaluation_handler.py +5 -3
  24. ads/aqua/extension/evaluation_ws_msg_handler.py +43 -0
  25. ads/aqua/extension/finetune_handler.py +41 -3
  26. ads/aqua/extension/model_handler.py +56 -4
  27. ads/aqua/extension/models/__init__.py +0 -0
  28. ads/aqua/extension/models/ws_models.py +69 -0
  29. ads/aqua/extension/ui_handler.py +65 -4
  30. ads/aqua/extension/ui_websocket_handler.py +124 -0
  31. ads/aqua/extension/utils.py +1 -1
  32. ads/aqua/finetuning/__init__.py +7 -0
  33. ads/aqua/finetuning/constants.py +17 -0
  34. ads/aqua/finetuning/entities.py +102 -0
  35. ads/aqua/{finetune.py → finetuning/finetuning.py} +162 -136
  36. ads/aqua/model/__init__.py +8 -0
  37. ads/aqua/model/constants.py +46 -0
  38. ads/aqua/model/entities.py +266 -0
  39. ads/aqua/model/enums.py +26 -0
  40. ads/aqua/{model.py → model/model.py} +401 -309
  41. ads/aqua/modeldeployment/__init__.py +8 -0
  42. ads/aqua/modeldeployment/constants.py +26 -0
  43. ads/aqua/{deployment.py → modeldeployment/deployment.py} +288 -227
  44. ads/aqua/modeldeployment/entities.py +142 -0
  45. ads/aqua/modeldeployment/inference.py +75 -0
  46. ads/aqua/ui.py +88 -8
  47. ads/cli.py +55 -7
  48. ads/common/serializer.py +2 -2
  49. ads/config.py +2 -1
  50. ads/jobs/builders/infrastructure/dsc_job.py +49 -6
  51. ads/model/datascience_model.py +21 -1
  52. ads/model/deployment/model_deployment.py +11 -0
  53. ads/model/model_metadata.py +17 -6
  54. ads/opctl/operator/lowcode/anomaly/README.md +0 -2
  55. ads/opctl/operator/lowcode/anomaly/__main__.py +3 -3
  56. ads/opctl/operator/lowcode/anomaly/environment.yaml +0 -2
  57. ads/opctl/operator/lowcode/anomaly/model/automlx.py +2 -2
  58. ads/opctl/operator/lowcode/anomaly/model/autots.py +1 -1
  59. ads/opctl/operator/lowcode/anomaly/model/base_model.py +13 -17
  60. ads/opctl/operator/lowcode/anomaly/operator_config.py +2 -0
  61. ads/opctl/operator/lowcode/anomaly/schema.yaml +1 -2
  62. ads/opctl/operator/lowcode/anomaly/utils.py +3 -2
  63. ads/opctl/operator/lowcode/common/transformations.py +2 -1
  64. ads/opctl/operator/lowcode/common/utils.py +1 -1
  65. ads/opctl/operator/lowcode/forecast/README.md +1 -3
  66. ads/opctl/operator/lowcode/forecast/__main__.py +3 -18
  67. ads/opctl/operator/lowcode/forecast/const.py +2 -0
  68. ads/opctl/operator/lowcode/forecast/environment.yaml +1 -2
  69. ads/opctl/operator/lowcode/forecast/model/arima.py +1 -0
  70. ads/opctl/operator/lowcode/forecast/model/automlx.py +7 -4
  71. ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
  72. ads/opctl/operator/lowcode/forecast/model/base_model.py +38 -22
  73. ads/opctl/operator/lowcode/forecast/model/factory.py +33 -4
  74. ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +15 -1
  75. ads/opctl/operator/lowcode/forecast/model/ml_forecast.py +234 -0
  76. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +9 -1
  77. ads/opctl/operator/lowcode/forecast/model/prophet.py +1 -0
  78. ads/opctl/operator/lowcode/forecast/model_evaluator.py +147 -0
  79. ads/opctl/operator/lowcode/forecast/operator_config.py +2 -1
  80. ads/opctl/operator/lowcode/forecast/schema.yaml +7 -2
  81. ads/opctl/operator/lowcode/forecast/utils.py +18 -44
  82. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/METADATA +9 -12
  83. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/RECORD +86 -61
  84. ads/aqua/job.py +0 -29
  85. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/LICENSE.txt +0 -0
  86. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/WHEEL +0 -0
  87. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/entry_points.txt +0 -0
@@ -3,292 +3,59 @@
3
3
  # Copyright (c) 2024 Oracle and/or its affiliates.
4
4
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
5
5
  import os
6
- import re
7
- from dataclasses import InitVar, dataclass, field
8
6
  from datetime import datetime, timedelta
9
- from enum import Enum
10
7
  from threading import Lock
11
- from typing import List, Union
8
+ from typing import List, Optional, Union
12
9
 
13
- import oci
14
10
  from cachetools import TTLCache
15
11
  from oci.data_science.models import JobRun, Model
16
12
 
17
- from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID, logger, utils
18
- from ads.aqua.base import AquaApp
19
- from ads.aqua.constants import (
20
- TRAINING_METRICS_FINAL,
21
- TRINING_METRICS,
22
- UNKNOWN_VALUE,
23
- VALIDATION_METRICS,
24
- VALIDATION_METRICS_FINAL,
25
- FineTuningDefinedMetadata,
13
+ from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID
14
+ from ads.aqua.app import AquaApp
15
+ from ads.aqua.common.enums import Tags
16
+ from ads.aqua.common.errors import AquaRuntimeError
17
+ from ads.aqua.common.utils import (
18
+ create_word_icon,
19
+ get_artifact_path,
20
+ read_file,
21
+ copy_model_config,
22
+ load_config,
26
23
  )
27
- from ads.aqua.data import AquaResourceIdentifier, Tags
28
- from ads.aqua.exception import AquaRuntimeError
29
- from ads.aqua.training.exceptions import exit_code_dict
30
- from ads.aqua.utils import (
24
+ from ads.aqua.constants import (
31
25
  LICENSE_TXT,
26
+ MODEL_BY_REFERENCE_OSS_PATH_KEY,
32
27
  README,
33
28
  READY_TO_DEPLOY_STATUS,
34
29
  READY_TO_FINE_TUNE_STATUS,
30
+ READY_TO_IMPORT_STATUS,
31
+ TRAINING_METRICS_FINAL,
32
+ TRINING_METRICS,
35
33
  UNKNOWN,
36
- create_word_icon,
37
- get_artifact_path,
38
- read_file,
34
+ VALIDATION_METRICS,
35
+ VALIDATION_METRICS_FINAL,
36
+ AQUA_MODEL_ARTIFACT_CONFIG,
37
+ AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME,
38
+ AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE,
39
+ AQUA_MODEL_TYPE_CUSTOM,
39
40
  )
41
+ from ads.aqua.model.constants import *
42
+ from ads.aqua.model.entities import *
40
43
  from ads.common.auth import default_signer
41
- from ads.common.object_storage_details import ObjectStorageDetails
42
44
  from ads.common.oci_resource import SEARCH_TYPE, OCIResource
43
- from ads.common.serializer import DataClassSerializable
44
- from ads.common.utils import get_console_link, get_log_links
45
+ from ads.common.utils import get_console_link
45
46
  from ads.config import (
46
- AQUA_SERVICE_MODELS_BUCKET,
47
+ AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
48
+ AQUA_EVALUATION_CONTAINER_METADATA_NAME,
49
+ AQUA_FINETUNING_CONTAINER_METADATA_NAME,
47
50
  COMPARTMENT_OCID,
48
- CONDA_BUCKET_NS,
49
51
  PROJECT_OCID,
50
52
  TENANCY_OCID,
51
53
  )
52
54
  from ads.model import DataScienceModel
53
- from ads.model.model_metadata import MetadataTaxonomyKeys, ModelCustomMetadata
55
+ from ads.model.model_metadata import ModelCustomMetadata, ModelCustomMetadataItem
54
56
  from ads.telemetry import telemetry
55
57
 
56
58
 
57
- class FineTuningMetricCategories(Enum):
58
- VALIDATION = "validation"
59
- TRAINING = "training"
60
-
61
-
62
- @dataclass(repr=False)
63
- class FineTuningShapeInfo(DataClassSerializable):
64
- instance_shape: str = field(default_factory=str)
65
- replica: int = field(default_factory=int)
66
-
67
-
68
- # TODO: give a better name
69
- @dataclass(repr=False)
70
- class AquaFineTuneValidation(DataClassSerializable):
71
- type: str = "Automatic split"
72
- value: str = ""
73
-
74
-
75
- @dataclass(repr=False)
76
- class AquaFineTuningMetric(DataClassSerializable):
77
- name: str = field(default_factory=str)
78
- category: str = field(default_factory=str)
79
- scores: list = field(default_factory=list)
80
-
81
-
82
- @dataclass(repr=False)
83
- class AquaModelLicense(DataClassSerializable):
84
- """Represents the response of Get Model License."""
85
-
86
- id: str = field(default_factory=str)
87
- license: str = field(default_factory=str)
88
-
89
-
90
- @dataclass(repr=False)
91
- class AquaModelSummary(DataClassSerializable):
92
- """Represents a summary of Aqua model."""
93
-
94
- compartment_id: str = None
95
- icon: str = None
96
- id: str = None
97
- is_fine_tuned_model: bool = None
98
- license: str = None
99
- name: str = None
100
- organization: str = None
101
- project_id: str = None
102
- tags: dict = None
103
- task: str = None
104
- time_created: str = None
105
- console_link: str = None
106
- search_text: str = None
107
- ready_to_deploy: bool = True
108
- ready_to_finetune: bool = False
109
-
110
-
111
- @dataclass(repr=False)
112
- class AquaModel(AquaModelSummary, DataClassSerializable):
113
- """Represents an Aqua model."""
114
-
115
- model_card: str = None
116
-
117
-
118
- @dataclass(repr=False)
119
- class AquaEvalFTCommon(DataClassSerializable):
120
- """Represents common fields for evaluation and fine-tuning."""
121
-
122
- lifecycle_state: str = None
123
- lifecycle_details: str = None
124
- job: AquaResourceIdentifier = field(default_factory=AquaResourceIdentifier)
125
- source: AquaResourceIdentifier = field(default_factory=AquaResourceIdentifier)
126
- experiment: AquaResourceIdentifier = field(default_factory=AquaResourceIdentifier)
127
- log_group: AquaResourceIdentifier = field(default_factory=AquaResourceIdentifier)
128
- log: AquaResourceIdentifier = field(default_factory=AquaResourceIdentifier)
129
-
130
- model: InitVar = None
131
- region: InitVar = None
132
- jobrun: InitVar = None
133
-
134
- def __post_init__(
135
- self, model, region: str, jobrun: oci.data_science.models.JobRun = None
136
- ):
137
- try:
138
- log_id = jobrun.log_details.log_id
139
- except Exception as e:
140
- logger.debug(f"No associated log found. {str(e)}")
141
- log_id = ""
142
-
143
- try:
144
- loggroup_id = jobrun.log_details.log_group_id
145
- except Exception as e:
146
- logger.debug(f"No associated loggroup found. {str(e)}")
147
- loggroup_id = ""
148
-
149
- loggroup_url = get_log_links(region=region, log_group_id=loggroup_id)
150
- log_url = (
151
- get_log_links(
152
- region=region,
153
- log_group_id=loggroup_id,
154
- log_id=log_id,
155
- compartment_id=jobrun.compartment_id,
156
- source_id=jobrun.id,
157
- )
158
- if jobrun
159
- else ""
160
- )
161
-
162
- log_name = None
163
- loggroup_name = None
164
-
165
- if log_id:
166
- try:
167
- log = utils.query_resource(log_id, return_all=False)
168
- log_name = log.display_name if log else ""
169
- except:
170
- pass
171
-
172
- if loggroup_id:
173
- try:
174
- loggroup = utils.query_resource(loggroup_id, return_all=False)
175
- loggroup_name = loggroup.display_name if loggroup else ""
176
- except:
177
- pass
178
-
179
- experiment_id, experiment_name = utils._get_experiment_info(model)
180
-
181
- self.log_group = AquaResourceIdentifier(
182
- loggroup_id, loggroup_name, loggroup_url
183
- )
184
- self.log = AquaResourceIdentifier(log_id, log_name, log_url)
185
- self.experiment = utils._build_resource_identifier(
186
- id=experiment_id, name=experiment_name, region=region
187
- )
188
- self.job = utils._build_job_identifier(job_run_details=jobrun, region=region)
189
- self.lifecycle_details = (
190
- utils.LIFECYCLE_DETAILS_MISSING_JOBRUN
191
- if not jobrun
192
- else jobrun.lifecycle_details
193
- )
194
-
195
-
196
- @dataclass(repr=False)
197
- class AquaFineTuneModel(AquaModel, AquaEvalFTCommon, DataClassSerializable):
198
- """Represents an Aqua Fine Tuned Model."""
199
-
200
- dataset: str = field(default_factory=str)
201
- validation: AquaFineTuneValidation = field(default_factory=AquaFineTuneValidation)
202
- shape_info: FineTuningShapeInfo = field(default_factory=FineTuningShapeInfo)
203
- metrics: List[AquaFineTuningMetric] = field(default_factory=list)
204
-
205
- def __post_init__(
206
- self,
207
- model: DataScienceModel,
208
- region: str,
209
- jobrun: oci.data_science.models.JobRun = None,
210
- ):
211
- super().__post_init__(model=model, region=region, jobrun=jobrun)
212
-
213
- if jobrun is not None:
214
- jobrun_env_vars = (
215
- jobrun.job_configuration_override_details.environment_variables or {}
216
- )
217
- self.shape_info = FineTuningShapeInfo(
218
- instance_shape=jobrun.job_infrastructure_configuration_details.shape_name,
219
- # TODO: use variable for `NODE_COUNT` in ads/jobs/builders/runtimes/base.py
220
- replica=jobrun_env_vars.get("NODE_COUNT", UNKNOWN_VALUE),
221
- )
222
-
223
- try:
224
- model_hyperparameters = model.defined_metadata_list.get(
225
- MetadataTaxonomyKeys.HYPERPARAMETERS
226
- ).value
227
- except Exception as e:
228
- logger.debug(
229
- f"Failed to extract model hyperparameters from {model.id}: " f"{str(e)}"
230
- )
231
- model_hyperparameters = {}
232
-
233
- self.dataset = model_hyperparameters.get(
234
- FineTuningDefinedMetadata.TRAINING_DATA.value
235
- )
236
- if not self.dataset:
237
- logger.debug(
238
- f"Key={FineTuningDefinedMetadata.TRAINING_DATA.value} not found in model hyperparameters."
239
- )
240
-
241
- self.validation = AquaFineTuneValidation(
242
- value=model_hyperparameters.get(
243
- FineTuningDefinedMetadata.VAL_SET_SIZE.value
244
- )
245
- )
246
- if not self.validation:
247
- logger.debug(
248
- f"Key={FineTuningDefinedMetadata.VAL_SET_SIZE.value} not found in model hyperparameters."
249
- )
250
-
251
- if self.lifecycle_details:
252
- self.lifecycle_details = self._extract_job_lifecycle_details(
253
- self.lifecycle_details
254
- )
255
-
256
- def _extract_job_lifecycle_details(self, lifecycle_details):
257
- message = lifecycle_details
258
- try:
259
- # Extract exit code
260
- match = re.search(r"exit code (\d+)", lifecycle_details)
261
- if match:
262
- exit_code = int(match.group(1))
263
- if exit_code == 1:
264
- return message
265
- # Match exit code to message
266
- exception = exit_code_dict().get(
267
- exit_code,
268
- lifecycle_details,
269
- )
270
- message = f"{exception.reason} (exit code {exit_code})"
271
- except:
272
- pass
273
-
274
- return message
275
-
276
-
277
- # TODO: merge metadata key used in create FT
278
-
279
-
280
- class FineTuningCustomMetadata(Enum):
281
- FT_SOURCE = "fine_tune_source"
282
- FT_SOURCE_NAME = "fine_tune_source_name"
283
- FT_OUTPUT_PATH = "fine_tune_output_path"
284
- FT_JOB_ID = "fine_tune_job_id"
285
- FT_JOB_RUN_ID = "fine_tune_jobrun_id"
286
- TRAINING_METRICS_FINAL = "train_metrics_final"
287
- VALIDATION_METRICS_FINAL = "val_metrics_final"
288
- TRAINING_METRICS_EPOCH = "train_metrics_epoch"
289
- VALIDATION_METRICS_EPOCH = "val_metrics_epoch"
290
-
291
-
292
59
  class AquaModelApp(AquaApp):
293
60
  """Provides a suite of APIs to interact with Aqua models within the Oracle
294
61
  Cloud Infrastructure Data Science service, serving as an interface for
@@ -305,6 +72,7 @@ class AquaModelApp(AquaApp):
305
72
  Lists all Aqua models within a specified compartment and/or project.
306
73
  clear_model_list_cache()
307
74
  Allows clear list model cache items from the service models compartment.
75
+ register(model: str, os_path: str, local_dir: str = None)
308
76
 
309
77
  Note:
310
78
  This class is designed to work within the Oracle Cloud Infrastructure
@@ -382,13 +150,15 @@ class AquaModelApp(AquaApp):
382
150
  return custom_model
383
151
 
384
152
  @telemetry(entry_point="plugin=model&action=get", name="aqua")
385
- def get(self, model_id) -> "AquaModel":
153
+ def get(self, model_id: str, load_model_card: Optional[bool] = True) -> "AquaModel":
386
154
  """Gets the information of an Aqua model.
387
155
 
388
156
  Parameters
389
157
  ----------
390
158
  model_id: str
391
159
  The model OCID.
160
+ load_model_card: (bool, optional). Defaults to `True`.
161
+ Whether to load model card from artifacts or not.
392
162
 
393
163
  Returns
394
164
  -------
@@ -407,38 +177,57 @@ class AquaModelApp(AquaApp):
407
177
  is_fine_tuned_model = (
408
178
  True
409
179
  if ds_model.freeform_tags
410
- and ds_model.freeform_tags.get(Tags.AQUA_FINE_TUNED_MODEL_TAG.value)
180
+ and ds_model.freeform_tags.get(Tags.AQUA_FINE_TUNED_MODEL_TAG)
411
181
  else False
412
182
  )
413
183
 
414
184
  # todo: consolidate this logic in utils for model and deployment use
415
- try:
416
- artifact_path = ds_model.custom_metadata_list.get(
417
- utils.MODEL_BY_REFERENCE_OSS_PATH_KEY
418
- ).value.rstrip("/")
419
- if not ObjectStorageDetails.is_oci_path(artifact_path):
420
- artifact_path = ObjectStorageDetails(
421
- AQUA_SERVICE_MODELS_BUCKET, CONDA_BUCKET_NS, artifact_path
422
- ).path
423
- except ValueError:
424
- artifact_path = utils.UNKNOWN
185
+ is_verified_type = (
186
+ ds_model.freeform_tags.get(Tags.READY_TO_IMPORT, "false").upper()
187
+ == READY_TO_IMPORT_STATUS
188
+ )
425
189
 
426
- if not artifact_path:
427
- logger.debug("Failed to get artifact path from custom metadata.")
190
+ model_card = ""
191
+ if load_model_card:
192
+ artifact_path = get_artifact_path(
193
+ ds_model.custom_metadata_list._to_oci_metadata()
194
+ )
195
+ if artifact_path != UNKNOWN:
196
+ model_card = str(
197
+ read_file(
198
+ file_path=(
199
+ f"{artifact_path.rstrip('/')}/config/{README}"
200
+ if is_verified_type
201
+ else f"{artifact_path.rstrip('/')}/{README}"
202
+ ),
203
+ auth=default_signer(),
204
+ )
205
+ )
428
206
 
429
- aqua_model_atttributes = dict(
207
+ inference_container = ds_model.custom_metadata_list.get(
208
+ ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
209
+ ModelCustomMetadataItem(key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER),
210
+ ).value
211
+ evaluation_container = ds_model.custom_metadata_list.get(
212
+ ModelCustomMetadataFields.EVALUATION_CONTAINER,
213
+ ModelCustomMetadataItem(key=ModelCustomMetadataFields.EVALUATION_CONTAINER),
214
+ ).value
215
+ finetuning_container: str = ds_model.custom_metadata_list.get(
216
+ ModelCustomMetadataFields.FINETUNE_CONTAINER,
217
+ ModelCustomMetadataItem(key=ModelCustomMetadataFields.FINETUNE_CONTAINER),
218
+ ).value
219
+
220
+ aqua_model_attributes = dict(
430
221
  **self._process_model(ds_model, self.region),
431
222
  project_id=ds_model.project_id,
432
- model_card=str(
433
- read_file(
434
- file_path=f"{artifact_path}/{README}",
435
- auth=self._auth,
436
- )
437
- ),
223
+ model_card=model_card,
224
+ inference_container=inference_container,
225
+ finetuning_container=finetuning_container,
226
+ evaluation_container=evaluation_container,
438
227
  )
439
228
 
440
229
  if not is_fine_tuned_model:
441
- model_details = AquaModel(**aqua_model_atttributes)
230
+ model_details = AquaModel(**aqua_model_attributes)
442
231
  self._service_model_details_cache.__setitem__(
443
232
  key=model_id, value=model_details
444
233
  )
@@ -455,7 +244,7 @@ class AquaModelApp(AquaApp):
455
244
 
456
245
  try:
457
246
  source_id = ds_model.custom_metadata_list.get(
458
- FineTuningCustomMetadata.FT_SOURCE.value
247
+ FineTuningCustomMetadata.FT_SOURCE
459
248
  ).value
460
249
  except ValueError as e:
461
250
  logger.debug(str(e))
@@ -463,7 +252,7 @@ class AquaModelApp(AquaApp):
463
252
 
464
253
  try:
465
254
  source_name = ds_model.custom_metadata_list.get(
466
- FineTuningCustomMetadata.FT_SOURCE_NAME.value
255
+ FineTuningCustomMetadata.FT_SOURCE_NAME
467
256
  ).value
468
257
  except ValueError as e:
469
258
  logger.debug(str(e))
@@ -494,7 +283,7 @@ class AquaModelApp(AquaApp):
494
283
  )
495
284
 
496
285
  model_details = AquaFineTuneModel(
497
- **aqua_model_atttributes,
286
+ **aqua_model_attributes,
498
287
  source=source_identifier,
499
288
  lifecycle_state=(
500
289
  Model.LIFECYCLE_STATE_ACTIVE
@@ -541,29 +330,29 @@ class AquaModelApp(AquaApp):
541
330
 
542
331
  validation_metrics = self._fetch_metric_from_metadata(
543
332
  custom_metadata_list=custom_metadata_list,
544
- target=FineTuningCustomMetadata.VALIDATION_METRICS_EPOCH.value,
545
- category=FineTuningMetricCategories.VALIDATION.value,
333
+ target=FineTuningCustomMetadata.VALIDATION_METRICS_EPOCH,
334
+ category=FineTuningMetricCategories.VALIDATION,
546
335
  metric_name=VALIDATION_METRICS,
547
336
  )
548
337
 
549
338
  training_metrics = self._fetch_metric_from_metadata(
550
339
  custom_metadata_list=custom_metadata_list,
551
- target=FineTuningCustomMetadata.TRAINING_METRICS_EPOCH.value,
552
- category=FineTuningMetricCategories.TRAINING.value,
340
+ target=FineTuningCustomMetadata.TRAINING_METRICS_EPOCH,
341
+ category=FineTuningMetricCategories.TRAINING,
553
342
  metric_name=TRINING_METRICS,
554
343
  )
555
344
 
556
345
  validation_final = self._fetch_metric_from_metadata(
557
346
  custom_metadata_list=custom_metadata_list,
558
- target=FineTuningCustomMetadata.VALIDATION_METRICS_FINAL.value,
559
- category=FineTuningMetricCategories.VALIDATION.value,
347
+ target=FineTuningCustomMetadata.VALIDATION_METRICS_FINAL,
348
+ category=FineTuningMetricCategories.VALIDATION,
560
349
  metric_name=VALIDATION_METRICS_FINAL,
561
350
  )
562
351
 
563
352
  training_final = self._fetch_metric_from_metadata(
564
353
  custom_metadata_list=custom_metadata_list,
565
- target=FineTuningCustomMetadata.TRAINING_METRICS_FINAL.value,
566
- category=FineTuningMetricCategories.TRAINING.value,
354
+ target=FineTuningCustomMetadata.TRAINING_METRICS_FINAL,
355
+ category=FineTuningMetricCategories.TRAINING,
567
356
  metric_name=TRAINING_METRICS_FINAL,
568
357
  )
569
358
 
@@ -623,23 +412,27 @@ class AquaModelApp(AquaApp):
623
412
  )
624
413
 
625
414
  freeform_tags = model.freeform_tags or {}
626
- is_fine_tuned_model = Tags.AQUA_FINE_TUNED_MODEL_TAG.value in freeform_tags
415
+ is_fine_tuned_model = Tags.AQUA_FINE_TUNED_MODEL_TAG in freeform_tags
627
416
  ready_to_deploy = (
628
- freeform_tags.get(Tags.AQUA_TAG.value, "").upper() == READY_TO_DEPLOY_STATUS
417
+ freeform_tags.get(Tags.AQUA_TAG, "").upper() == READY_TO_DEPLOY_STATUS
629
418
  )
630
419
  ready_to_finetune = (
631
- freeform_tags.get(Tags.READY_TO_FINE_TUNE.value, "").upper()
420
+ freeform_tags.get(Tags.READY_TO_FINE_TUNE, "").upper()
632
421
  == READY_TO_FINE_TUNE_STATUS
633
422
  )
423
+ ready_to_import = (
424
+ freeform_tags.get(Tags.READY_TO_IMPORT, "").upper()
425
+ == READY_TO_IMPORT_STATUS
426
+ )
634
427
 
635
428
  return dict(
636
429
  compartment_id=model.compartment_id,
637
430
  icon=icon or UNKNOWN,
638
431
  id=model_id,
639
- license=freeform_tags.get(Tags.LICENSE.value, UNKNOWN),
432
+ license=freeform_tags.get(Tags.LICENSE, UNKNOWN),
640
433
  name=model.display_name,
641
- organization=freeform_tags.get(Tags.ORGANIZATION.value, UNKNOWN),
642
- task=freeform_tags.get(Tags.TASK.value, UNKNOWN),
434
+ organization=freeform_tags.get(Tags.ORGANIZATION, UNKNOWN),
435
+ task=freeform_tags.get(Tags.TASK, UNKNOWN),
643
436
  time_created=model.time_created,
644
437
  is_fine_tuned_model=is_fine_tuned_model,
645
438
  tags=tags,
@@ -647,11 +440,16 @@ class AquaModelApp(AquaApp):
647
440
  search_text=search_text,
648
441
  ready_to_deploy=ready_to_deploy,
649
442
  ready_to_finetune=ready_to_finetune,
443
+ ready_to_import=ready_to_import,
650
444
  )
651
445
 
652
446
  @telemetry(entry_point="plugin=model&action=list", name="aqua")
653
447
  def list(
654
- self, compartment_id: str = None, project_id: str = None, **kwargs
448
+ self,
449
+ compartment_id: str = None,
450
+ project_id: str = None,
451
+ model_type: str = None,
452
+ **kwargs,
655
453
  ) -> List["AquaModelSummary"]:
656
454
  """Lists all Aqua models within a specified compartment and/or project.
657
455
  If `compartment_id` is not specified, the method defaults to returning
@@ -665,6 +463,8 @@ class AquaModelApp(AquaApp):
665
463
  The compartment OCID.
666
464
  project_id: (str, optional). Defaults to `None`.
667
465
  The project OCID.
466
+ model_type: (str, optional). Defaults to `None`.
467
+ Model type represents the type of model in the user compartment, can be either FT or BASE.
668
468
  **kwargs:
669
469
  Additional keyword arguments that can be used to filter the results.
670
470
 
@@ -682,7 +482,8 @@ class AquaModelApp(AquaApp):
682
482
  )
683
483
 
684
484
  logger.info(f"Fetching custom models from compartment_id={compartment_id}.")
685
- models = self._rqs(compartment_id)
485
+ model_type = model_type.upper() if model_type else ModelType.FT
486
+ models = self._rqs(compartment_id, model_type=model_type)
686
487
  else:
687
488
  # tracks number of times service model listing was called
688
489
  self.telemetry.record_event_async(
@@ -751,16 +552,275 @@ class AquaModelApp(AquaApp):
751
552
  }
752
553
  return res
753
554
 
555
+ def _create_model_catalog_entry(
556
+ self,
557
+ os_path: str,
558
+ model_name: str,
559
+ inference_container: str,
560
+ finetuning_container: str,
561
+ verified_model: DataScienceModel,
562
+ compartment_id: Optional[str],
563
+ project_id: Optional[str],
564
+ ) -> DataScienceModel:
565
+ """Create model by reference from the object storage path
566
+
567
+ Args:
568
+ os_path (str): OCI where the model is uploaded - oci://bucket@namespace/prefix
569
+ model_name (str): name of the model
570
+ inference_container (str): selects service defaults
571
+ finetuning_container (str): selects service defaults
572
+ verified_model (DataScienceModel): If set, then copies all the tags and custom metadata information from the service verified model
573
+ compartment_id (Optional[str]): Compartment Id of the compartment where the model has to be created
574
+ project_id (Optional[str]): Project id of the project where the model has to be created
575
+
576
+ Returns:
577
+ DataScienceModel: Returns Datascience model instance.
578
+ """
579
+ model = DataScienceModel()
580
+ tags = (
581
+ {
582
+ **verified_model.freeform_tags,
583
+ Tags.AQUA_SERVICE_MODEL_TAG: verified_model.id,
584
+ }
585
+ if verified_model
586
+ else {Tags.AQUA_TAG: "active", Tags.BASE_MODEL_CUSTOM: "true"}
587
+ )
588
+ tags.update({Tags.BASE_MODEL_CUSTOM: "true"})
589
+
590
+ # Remove `ready_to_import` tag that might get copied from service model.
591
+ tags.pop(Tags.READY_TO_IMPORT, None)
592
+ metadata = None
593
+ if verified_model:
594
+ # 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.
595
+ # If set, then we copy all the model metadata.
596
+ metadata = verified_model.custom_metadata_list
597
+ if verified_model.model_file_description:
598
+ model = model.with_model_file_description(
599
+ json_dict=verified_model.model_file_description
600
+ )
601
+
602
+ else:
603
+ metadata = ModelCustomMetadata()
604
+ if not inference_container:
605
+ raise AquaRuntimeError(
606
+ f"Require Inference container information. Model: {model_name} does not have associated inference container defaults. Check docs for more information on how to pass inference container."
607
+ )
608
+ if finetuning_container:
609
+ tags[Tags.READY_TO_FINE_TUNE] = "true"
610
+ metadata.add(
611
+ key=AQUA_FINETUNING_CONTAINER_METADATA_NAME,
612
+ value=finetuning_container,
613
+ description=f"Fine-tuning container mapping for {model_name}",
614
+ category="Other",
615
+ )
616
+ else:
617
+ logger.warn(
618
+ f"Proceeding with model registration without the fine-tuning container information. "
619
+ f"This model will not be available for fine tuning."
620
+ )
621
+
622
+ metadata.add(
623
+ key=AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
624
+ value=inference_container,
625
+ description=f"Inference container mapping for {model_name}",
626
+ category="Other",
627
+ )
628
+ metadata.add(
629
+ key=AQUA_EVALUATION_CONTAINER_METADATA_NAME,
630
+ value="odsc-llm-evaluate",
631
+ description="Evaluation container mapping for SMC",
632
+ category="Other",
633
+ )
634
+ # TODO: either get task and organization from user or a config file
635
+ # tags["task"] = "UNKNOWN"
636
+ # tags["organization"] = "UNKNOWN"
637
+
638
+ try:
639
+ # If verified model already has a artifact json, use that.
640
+ artifact_path = metadata.get(MODEL_BY_REFERENCE_OSS_PATH_KEY).value
641
+ logger.info(
642
+ f"Found model artifact in the service bucket. "
643
+ f"Using artifact from service bucket instead of {os_path}"
644
+ )
645
+
646
+ # todo: implement generic copy_folder method
647
+ # copy model config from artifact path to user bucket
648
+ copy_model_config(
649
+ artifact_path=artifact_path, os_path=os_path, auth=default_signer()
650
+ )
651
+
652
+ except:
653
+ # Add artifact from user bucket
654
+ metadata.add(
655
+ key=MODEL_BY_REFERENCE_OSS_PATH_KEY,
656
+ value=os_path,
657
+ description="artifact location",
658
+ category="Other",
659
+ )
660
+
661
+ model = (
662
+ model.with_custom_metadata_list(metadata)
663
+ .with_compartment_id(compartment_id or COMPARTMENT_OCID)
664
+ .with_project_id(project_id or PROJECT_OCID)
665
+ .with_artifact(os_path)
666
+ .with_display_name(model_name)
667
+ .with_freeform_tags(**tags)
668
+ ).create(model_by_reference=True)
669
+ logger.debug(model)
670
+ return model
671
+
672
+ def register(
673
+ self, import_model_details: ImportModelDetails = None, **kwargs
674
+ ) -> AquaModel:
675
+ """Loads the model from object storage and registers as Model in Data Science Model catalog
676
+ The inference container and finetuning container could be of type Service Manged Container(SMC) or custom.
677
+ If it is custom, full container URI is expected. If it of type SMC, only the container family name is expected.
678
+
679
+ Args:
680
+ import_model_details (ImportModelDetails): Model details for importing the model.
681
+ kwargs:
682
+ model (str): name of the model or OCID of the service model that has inference and finetuning information
683
+ os_path (str): Object storage destination URI to store the downloaded model. Format: oci://bucket-name@namespace/prefix
684
+ inference_container (str): selects service defaults
685
+ finetuning_container (str): selects service defaults
686
+
687
+ Returns:
688
+ AquaModel:
689
+ The registered model as a AquaModel object.
690
+ """
691
+ verified_model_details: DataScienceModel = None
692
+
693
+ if not import_model_details:
694
+ import_model_details = ImportModelDetails(**kwargs)
695
+
696
+ try:
697
+ model_config = load_config(
698
+ file_path=import_model_details.os_path,
699
+ config_file_name=AQUA_MODEL_ARTIFACT_CONFIG,
700
+ )
701
+ except Exception as ex:
702
+ logger.error(
703
+ f"Exception occurred while loading config file from {import_model_details.os_path}"
704
+ f"Exception message: {ex}"
705
+ )
706
+ raise AquaRuntimeError(
707
+ f"The model path {import_model_details.os_path} does not contain the file config.json. "
708
+ f"Please check if the path is correct or the model artifacts are available at this location."
709
+ )
710
+
711
+ model_service_id = None
712
+ # If OCID of a model is passed, we need to copy the defaults for Tags and metadata from the service model.
713
+ if (
714
+ import_model_details.model.startswith("ocid")
715
+ and "datasciencemodel" in import_model_details.model
716
+ ):
717
+ model_service_id = import_model_details.model
718
+ else:
719
+ # If users passes model name, check if there is model with the same name in the service model catalog. If it is there, then use that model
720
+ model_service_id = self._find_matching_aqua_model(
721
+ import_model_details.model
722
+ )
723
+ logger.info(
724
+ f"Found service model for {import_model_details.model}: {model_service_id}"
725
+ )
726
+ if model_service_id:
727
+ verified_model_details = DataScienceModel.from_id(model_service_id)
728
+ try:
729
+ metadata_model_type = verified_model_details.custom_metadata_list.get(
730
+ AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE
731
+ ).value
732
+ if metadata_model_type:
733
+ if AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE in model_config:
734
+ if (
735
+ model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE]
736
+ != metadata_model_type
737
+ ):
738
+ raise AquaRuntimeError(
739
+ f"The {AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE} attribute in {AQUA_MODEL_ARTIFACT_CONFIG}"
740
+ f" at {import_model_details.os_path} is invalid, expected {metadata_model_type} for "
741
+ f"the model {import_model_details.model}. Please check if the path is correct or "
742
+ f"the correct model artifacts are available at this location."
743
+ f""
744
+ )
745
+ else:
746
+ logger.debug(
747
+ f"Could not find {AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE} attribute in "
748
+ f"{AQUA_MODEL_ARTIFACT_CONFIG}. Proceeding with model registration."
749
+ )
750
+ except:
751
+ pass
752
+
753
+ # Copy the model name from the service model if `model` is ocid
754
+ model_name = (
755
+ verified_model_details.display_name
756
+ if verified_model_details
757
+ else import_model_details.model
758
+ )
759
+
760
+ # Create Model catalog entry with pass by reference
761
+ ds_model = self._create_model_catalog_entry(
762
+ os_path=import_model_details.os_path,
763
+ model_name=model_name,
764
+ inference_container=import_model_details.inference_container,
765
+ finetuning_container=import_model_details.finetuning_container,
766
+ verified_model=verified_model_details,
767
+ compartment_id=import_model_details.compartment_id,
768
+ project_id=import_model_details.project_id,
769
+ )
770
+ # registered model will always have inference and evaluation container, but
771
+ # fine-tuning container may be not set
772
+ inference_container = ds_model.custom_metadata_list.get(
773
+ ModelCustomMetadataFields.DEPLOYMENT_CONTAINER
774
+ ).value
775
+ evaluation_container = ds_model.custom_metadata_list.get(
776
+ ModelCustomMetadataFields.EVALUATION_CONTAINER,
777
+ ).value
778
+ try:
779
+ finetuning_container = ds_model.custom_metadata_list.get(
780
+ ModelCustomMetadataFields.FINETUNE_CONTAINER,
781
+ ).value
782
+ except:
783
+ finetuning_container = None
784
+
785
+ aqua_model_attributes = dict(
786
+ **self._process_model(ds_model, self.region),
787
+ project_id=ds_model.project_id,
788
+ model_card=str(
789
+ read_file(
790
+ file_path=f"{import_model_details.os_path.rstrip('/')}/{README}",
791
+ auth=default_signer(),
792
+ )
793
+ ),
794
+ inference_container=inference_container,
795
+ finetuning_container=finetuning_container,
796
+ evaluation_container=evaluation_container,
797
+ )
798
+
799
+ if verified_model_details:
800
+ telemetry_model_name = model_name
801
+ else:
802
+ if AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME in model_config:
803
+ telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME]}"
804
+ elif AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE in model_config:
805
+ telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE]}"
806
+ else:
807
+ telemetry_model_name = AQUA_MODEL_TYPE_CUSTOM
808
+
809
+ self.telemetry.record_event_async(
810
+ category="aqua/model",
811
+ action="register",
812
+ detail=telemetry_model_name,
813
+ )
814
+
815
+ return AquaModel(**aqua_model_attributes)
816
+
754
817
  def _if_show(self, model: DataScienceModel) -> bool:
755
818
  """Determine if the given model should be return by `list`."""
756
819
  if model.freeform_tags is None:
757
820
  return False
758
821
 
759
822
  TARGET_TAGS = model.freeform_tags.keys()
760
- return (
761
- Tags.AQUA_TAG.value in TARGET_TAGS
762
- or Tags.AQUA_TAG.value.lower() in TARGET_TAGS
763
- )
823
+ return Tags.AQUA_TAG in TARGET_TAGS or Tags.AQUA_TAG.lower() in TARGET_TAGS
764
824
 
765
825
  def _load_icon(self, model_name: str) -> str:
766
826
  """Loads icon."""
@@ -772,10 +832,18 @@ class AquaModelApp(AquaApp):
772
832
  logger.debug(f"Failed to load icon for the model={model_name}: {str(e)}.")
773
833
  return None
774
834
 
775
- def _rqs(self, compartment_id: str, **kwargs):
835
+ def _rqs(self, compartment_id: str, model_type="FT", **kwargs):
776
836
  """Use RQS to fetch models in the user tenancy."""
837
+ if model_type == ModelType.FT:
838
+ filter_tag = Tags.AQUA_FINE_TUNED_MODEL_TAG
839
+ elif model_type == ModelType.BASE:
840
+ filter_tag = Tags.BASE_MODEL_CUSTOM
841
+ else:
842
+ raise ValueError(
843
+ f"Model of type {model_type} is unknown. The values should be in {ModelType.values()}"
844
+ )
777
845
 
778
- condition_tags = f"&& (freeformTags.key = '{Tags.AQUA_TAG.value}' && freeformTags.key = '{Tags.AQUA_FINE_TUNED_MODEL_TAG.value}')"
846
+ condition_tags = f"&& (freeformTags.key = '{Tags.AQUA_TAG}' && freeformTags.key = '{filter_tag}')"
779
847
  condition_lifecycle = "&& lifecycleState = 'ACTIVE'"
780
848
  query = f"query datasciencemodel resources where (compartmentId = '{compartment_id}' {condition_lifecycle} {condition_tags})"
781
849
  logger.info(query)
@@ -820,3 +888,27 @@ class AquaModelApp(AquaApp):
820
888
  )
821
889
 
822
890
  return AquaModelLicense(id=model_id, license=content)
891
+
892
+ def _find_matching_aqua_model(self, model_id: str) -> Optional[str]:
893
+ """
894
+ Finds a matching model in AQUA based on the model ID from list of verified models.
895
+
896
+ Parameters
897
+ ----------
898
+ model_id (str): Verified model ID to match.
899
+
900
+ Returns
901
+ -------
902
+ Optional[str]
903
+ Returns model ocid that matches the model in the service catalog else returns None.
904
+ """
905
+ # Convert the model ID to lowercase once
906
+ model_id_lower = model_id.lower()
907
+
908
+ aqua_model_list = self.list()
909
+
910
+ for aqua_model_summary in aqua_model_list:
911
+ if aqua_model_summary.name.lower() == model_id_lower:
912
+ return aqua_model_summary.id
913
+
914
+ return None