oracle-ads 2.12.8__py3-none-any.whl → 2.12.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 (82) hide show
  1. ads/aqua/__init__.py +4 -3
  2. ads/aqua/app.py +40 -18
  3. ads/aqua/client/__init__.py +3 -0
  4. ads/aqua/client/client.py +799 -0
  5. ads/aqua/common/enums.py +3 -0
  6. ads/aqua/common/utils.py +62 -2
  7. ads/aqua/data.py +2 -19
  8. ads/aqua/evaluation/entities.py +6 -0
  9. ads/aqua/evaluation/evaluation.py +45 -15
  10. ads/aqua/extension/aqua_ws_msg_handler.py +14 -7
  11. ads/aqua/extension/base_handler.py +12 -9
  12. ads/aqua/extension/deployment_handler.py +8 -4
  13. ads/aqua/extension/finetune_handler.py +8 -14
  14. ads/aqua/extension/model_handler.py +30 -6
  15. ads/aqua/extension/ui_handler.py +13 -1
  16. ads/aqua/finetuning/constants.py +5 -2
  17. ads/aqua/finetuning/entities.py +73 -17
  18. ads/aqua/finetuning/finetuning.py +110 -82
  19. ads/aqua/model/entities.py +5 -1
  20. ads/aqua/model/model.py +230 -104
  21. ads/aqua/modeldeployment/deployment.py +35 -11
  22. ads/aqua/modeldeployment/entities.py +7 -4
  23. ads/aqua/ui.py +24 -2
  24. ads/cli.py +16 -8
  25. ads/common/auth.py +9 -9
  26. ads/llm/autogen/__init__.py +2 -0
  27. ads/llm/autogen/constants.py +15 -0
  28. ads/llm/autogen/reports/__init__.py +2 -0
  29. ads/llm/autogen/reports/base.py +67 -0
  30. ads/llm/autogen/reports/data.py +103 -0
  31. ads/llm/autogen/reports/session.py +526 -0
  32. ads/llm/autogen/reports/templates/chat_box.html +13 -0
  33. ads/llm/autogen/reports/templates/chat_box_lt.html +5 -0
  34. ads/llm/autogen/reports/templates/chat_box_rt.html +6 -0
  35. ads/llm/autogen/reports/utils.py +56 -0
  36. ads/llm/autogen/v02/__init__.py +4 -0
  37. ads/llm/autogen/{client_v02.py → v02/client.py} +23 -10
  38. ads/llm/autogen/v02/log_handlers/__init__.py +2 -0
  39. ads/llm/autogen/v02/log_handlers/oci_file_handler.py +83 -0
  40. ads/llm/autogen/v02/loggers/__init__.py +6 -0
  41. ads/llm/autogen/v02/loggers/metric_logger.py +320 -0
  42. ads/llm/autogen/v02/loggers/session_logger.py +580 -0
  43. ads/llm/autogen/v02/loggers/utils.py +86 -0
  44. ads/llm/autogen/v02/runtime_logging.py +163 -0
  45. ads/llm/guardrails/base.py +6 -5
  46. ads/llm/langchain/plugins/chat_models/oci_data_science.py +46 -20
  47. ads/llm/langchain/plugins/llms/oci_data_science_model_deployment_endpoint.py +38 -11
  48. ads/model/__init__.py +11 -13
  49. ads/model/artifact.py +47 -8
  50. ads/model/extractor/embedding_onnx_extractor.py +80 -0
  51. ads/model/framework/embedding_onnx_model.py +438 -0
  52. ads/model/generic_model.py +26 -24
  53. ads/model/model_metadata.py +8 -7
  54. ads/opctl/config/merger.py +13 -14
  55. ads/opctl/operator/common/operator_config.py +4 -4
  56. ads/opctl/operator/lowcode/common/transformations.py +50 -8
  57. ads/opctl/operator/lowcode/common/utils.py +22 -6
  58. ads/opctl/operator/lowcode/forecast/__main__.py +10 -0
  59. ads/opctl/operator/lowcode/forecast/const.py +3 -0
  60. ads/opctl/operator/lowcode/forecast/model/arima.py +19 -13
  61. ads/opctl/operator/lowcode/forecast/model/automlx.py +129 -36
  62. ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
  63. ads/opctl/operator/lowcode/forecast/model/base_model.py +58 -17
  64. ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +1 -1
  65. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +10 -3
  66. ads/opctl/operator/lowcode/forecast/model/prophet.py +25 -18
  67. ads/opctl/operator/lowcode/forecast/model_evaluator.py +3 -2
  68. ads/opctl/operator/lowcode/forecast/operator_config.py +31 -0
  69. ads/opctl/operator/lowcode/forecast/schema.yaml +76 -0
  70. ads/opctl/operator/lowcode/forecast/utils.py +8 -6
  71. ads/opctl/operator/lowcode/forecast/whatifserve/__init__.py +7 -0
  72. ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py +233 -0
  73. ads/opctl/operator/lowcode/forecast/whatifserve/score.py +238 -0
  74. ads/telemetry/base.py +18 -11
  75. ads/telemetry/client.py +33 -13
  76. ads/templates/schemas/openapi.json +1740 -0
  77. ads/templates/score_embedding_onnx.jinja2 +202 -0
  78. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/METADATA +11 -10
  79. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/RECORD +82 -56
  80. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/LICENSE.txt +0 -0
  81. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/WHEEL +0 -0
  82. {oracle_ads-2.12.8.dist-info → oracle_ads-2.12.10.dist-info}/entry_points.txt +0 -0
ads/aqua/model/model.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python
2
- # Copyright (c) 2024 Oracle and/or its affiliates.
2
+ # Copyright (c) 2024, 2025 Oracle and/or its affiliates.
3
3
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
4
  import os
5
5
  import pathlib
@@ -15,15 +15,20 @@ from oci.data_science.models import JobRun, Metadata, Model, UpdateModelDetails
15
15
  from ads.aqua import ODSC_MODEL_COMPARTMENT_OCID, logger
16
16
  from ads.aqua.app import AquaApp
17
17
  from ads.aqua.common.enums import (
18
+ CustomInferenceContainerTypeFamily,
18
19
  FineTuningContainerTypeFamily,
19
20
  InferenceContainerTypeFamily,
20
21
  Tags,
21
22
  )
22
- from ads.aqua.common.errors import AquaRuntimeError, AquaValueError
23
+ from ads.aqua.common.errors import (
24
+ AquaFileNotFoundError,
25
+ AquaRuntimeError,
26
+ AquaValueError,
27
+ )
23
28
  from ads.aqua.common.utils import (
24
29
  LifecycleStatus,
25
30
  _build_resource_identifier,
26
- copy_model_config,
31
+ cleanup_local_hf_model_artifact,
27
32
  create_word_icon,
28
33
  generate_tei_cmd_var,
29
34
  get_artifact_path,
@@ -127,7 +132,13 @@ class AquaModelApp(AquaApp):
127
132
 
128
133
  @telemetry(entry_point="plugin=model&action=create", name="aqua")
129
134
  def create(
130
- self, model_id: str, project_id: str, compartment_id: str = None, **kwargs
135
+ self,
136
+ model_id: str,
137
+ project_id: str,
138
+ compartment_id: str = None,
139
+ freeform_tags: Optional[dict] = None,
140
+ defined_tags: Optional[dict] = None,
141
+ **kwargs,
131
142
  ) -> DataScienceModel:
132
143
  """Creates custom aqua model from service model.
133
144
 
@@ -140,7 +151,10 @@ class AquaModelApp(AquaApp):
140
151
  compartment_id: str
141
152
  The compartment id for custom model. Defaults to None.
142
153
  If not provided, compartment id will be fetched from environment variables.
143
-
154
+ freeform_tags: dict
155
+ Freeform tags for the model
156
+ defined_tags: dict
157
+ Defined tags for the model
144
158
  Returns
145
159
  -------
146
160
  DataScienceModel:
@@ -151,12 +165,22 @@ class AquaModelApp(AquaApp):
151
165
  target_compartment = compartment_id or COMPARTMENT_OCID
152
166
 
153
167
  if service_model.compartment_id != ODSC_MODEL_COMPARTMENT_OCID:
154
- logger.debug(
168
+ logger.info(
155
169
  f"Aqua Model {model_id} already exists in user's compartment."
156
170
  "Skipped copying."
157
171
  )
158
172
  return service_model
159
173
 
174
+ # combine tags
175
+ combined_freeform_tags = {
176
+ **(service_model.freeform_tags or {}),
177
+ **(freeform_tags or {}),
178
+ }
179
+ combined_defined_tags = {
180
+ **(service_model.defined_tags or {}),
181
+ **(defined_tags or {}),
182
+ }
183
+
160
184
  custom_model = (
161
185
  DataScienceModel()
162
186
  .with_compartment_id(target_compartment)
@@ -164,16 +188,16 @@ class AquaModelApp(AquaApp):
164
188
  .with_model_file_description(json_dict=service_model.model_file_description)
165
189
  .with_display_name(service_model.display_name)
166
190
  .with_description(service_model.description)
167
- .with_freeform_tags(**(service_model.freeform_tags or {}))
168
- .with_defined_tags(**(service_model.defined_tags or {}))
191
+ .with_freeform_tags(**combined_freeform_tags)
192
+ .with_defined_tags(**combined_defined_tags)
169
193
  .with_custom_metadata_list(service_model.custom_metadata_list)
170
194
  .with_defined_metadata_list(service_model.defined_metadata_list)
171
195
  .with_provenance_metadata(service_model.provenance_metadata)
172
196
  # TODO: decide what kwargs will be needed.
173
197
  .create(model_by_reference=True, **kwargs)
174
198
  )
175
- logger.debug(
176
- f"Aqua Model {custom_model.id} created with the service model {model_id}"
199
+ logger.info(
200
+ f"Aqua Model {custom_model.id} created with the service model {model_id}."
177
201
  )
178
202
 
179
203
  # tracks unique models that were created in the user compartment
@@ -204,11 +228,16 @@ class AquaModelApp(AquaApp):
204
228
 
205
229
  cached_item = self._service_model_details_cache.get(model_id)
206
230
  if cached_item:
231
+ logger.info(f"Fetching model details for model {model_id} from cache.")
207
232
  return cached_item
208
233
 
234
+ logger.info(f"Fetching model details for model {model_id}.")
209
235
  ds_model = DataScienceModel.from_id(model_id)
210
236
  if not self._if_show(ds_model):
211
- raise AquaRuntimeError(f"Target model `{ds_model.id} `is not Aqua model.")
237
+ raise AquaRuntimeError(
238
+ f"Target model `{ds_model.id} `is not an Aqua model as it does not contain "
239
+ f"{Tags.AQUA_TAG} tag."
240
+ )
212
241
 
213
242
  is_fine_tuned_model = bool(
214
243
  ds_model.freeform_tags
@@ -227,16 +256,21 @@ class AquaModelApp(AquaApp):
227
256
  ds_model.custom_metadata_list._to_oci_metadata()
228
257
  )
229
258
  if artifact_path != UNKNOWN:
259
+ model_card_path = (
260
+ f"{artifact_path.rstrip('/')}/config/{README}"
261
+ if is_verified_type
262
+ else f"{artifact_path.rstrip('/')}/{README}"
263
+ )
230
264
  model_card = str(
231
265
  read_file(
232
- file_path=(
233
- f"{artifact_path.rstrip('/')}/config/{README}"
234
- if is_verified_type
235
- else f"{artifact_path.rstrip('/')}/{README}"
236
- ),
266
+ file_path=model_card_path,
237
267
  auth=default_signer(),
238
268
  )
239
269
  )
270
+ if not model_card:
271
+ logger.warn(
272
+ f"Model card for {model_id} is empty or could not be loaded from {model_card_path}."
273
+ )
240
274
 
241
275
  inference_container = ds_model.custom_metadata_list.get(
242
276
  ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
@@ -282,9 +316,10 @@ class AquaModelApp(AquaApp):
282
316
  try:
283
317
  jobrun_ocid = ds_model.provenance_metadata.training_id
284
318
  jobrun = self.ds_client.get_job_run(jobrun_ocid).data
285
- except Exception:
319
+ except Exception as e:
286
320
  logger.debug(
287
321
  f"Missing jobrun information in the provenance metadata of the given model {model_id}."
322
+ f"\nError: {str(e)}"
288
323
  )
289
324
  jobrun = None
290
325
 
@@ -293,7 +328,10 @@ class AquaModelApp(AquaApp):
293
328
  FineTuningCustomMetadata.FT_SOURCE
294
329
  ).value
295
330
  except ValueError as e:
296
- logger.debug(str(e))
331
+ logger.debug(
332
+ f"Custom metadata is missing {FineTuningCustomMetadata.FT_SOURCE} key for "
333
+ f"model {model_id}.\nError: {str(e)}"
334
+ )
297
335
  source_id = UNKNOWN
298
336
 
299
337
  try:
@@ -301,7 +339,10 @@ class AquaModelApp(AquaApp):
301
339
  FineTuningCustomMetadata.FT_SOURCE_NAME
302
340
  ).value
303
341
  except ValueError as e:
304
- logger.debug(str(e))
342
+ logger.debug(
343
+ f"Custom metadata is missing {FineTuningCustomMetadata.FT_SOURCE_NAME} key for "
344
+ f"model {model_id}.\nError: {str(e)}"
345
+ )
305
346
  source_name = UNKNOWN
306
347
 
307
348
  source_identifier = _build_resource_identifier(
@@ -351,14 +392,17 @@ class AquaModelApp(AquaApp):
351
392
  Tags.AQUA_FINE_TUNED_MODEL_TAG, None
352
393
  )
353
394
  if is_registered_model or is_fine_tuned_model:
395
+ logger.info(f"Deleting model {model_id}.")
354
396
  return ds_model.delete()
355
397
  else:
356
398
  raise AquaRuntimeError(
357
399
  f"Failed to delete model:{model_id}. Only registered models or finetuned model can be deleted."
358
400
  )
359
401
 
360
- @telemetry(entry_point="plugin=model&action=delete", name="aqua")
361
- def edit_registered_model(self, id, inference_container, enable_finetuning, task):
402
+ @telemetry(entry_point="plugin=model&action=edit", name="aqua")
403
+ def edit_registered_model(
404
+ self, id, inference_container, inference_container_uri, enable_finetuning, task
405
+ ):
362
406
  """Edits the default config of unverified registered model.
363
407
 
364
408
  Parameters
@@ -367,6 +411,8 @@ class AquaModelApp(AquaApp):
367
411
  The model OCID.
368
412
  inference_container: str.
369
413
  The inference container family name
414
+ inference_container_uri: str
415
+ The inference container uri for embedding models
370
416
  enable_finetuning: str
371
417
  Flag to enable or disable finetuning over the model. Defaults to None
372
418
  task:
@@ -382,19 +428,44 @@ class AquaModelApp(AquaApp):
382
428
  if ds_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None):
383
429
  if ds_model.freeform_tags.get(Tags.AQUA_SERVICE_MODEL_TAG, None):
384
430
  raise AquaRuntimeError(
385
- f"Failed to edit model:{id}. Only registered unverified models can be edited."
431
+ "Only registered unverified models can be edited."
386
432
  )
387
433
  else:
388
434
  custom_metadata_list = ds_model.custom_metadata_list
389
435
  freeform_tags = ds_model.freeform_tags
390
436
  if inference_container:
391
- custom_metadata_list.add(
392
- key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
393
- value=inference_container,
394
- category=MetadataCustomCategory.OTHER,
395
- description="Deployment container mapping for SMC",
396
- replace=True,
397
- )
437
+ if (
438
+ inference_container in CustomInferenceContainerTypeFamily
439
+ and inference_container_uri is None
440
+ ):
441
+ raise AquaRuntimeError(
442
+ "Inference container URI must be provided."
443
+ )
444
+ else:
445
+ custom_metadata_list.add(
446
+ key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER,
447
+ value=inference_container,
448
+ category=MetadataCustomCategory.OTHER,
449
+ description="Deployment container mapping for SMC",
450
+ replace=True,
451
+ )
452
+ if inference_container_uri:
453
+ if (
454
+ inference_container in CustomInferenceContainerTypeFamily
455
+ or inference_container is None
456
+ ):
457
+ custom_metadata_list.add(
458
+ key=ModelCustomMetadataFields.DEPLOYMENT_CONTAINER_URI,
459
+ value=inference_container_uri,
460
+ category=MetadataCustomCategory.OTHER,
461
+ description=f"Inference container URI for {ds_model.display_name}",
462
+ replace=True,
463
+ )
464
+ else:
465
+ raise AquaRuntimeError(
466
+ f"Inference container URI can be edited only with container values: {CustomInferenceContainerTypeFamily.values()}"
467
+ )
468
+
398
469
  if enable_finetuning is not None:
399
470
  if enable_finetuning.lower() == "true":
400
471
  custom_metadata_list.add(
@@ -414,7 +485,7 @@ class AquaModelApp(AquaApp):
414
485
  except Exception as ex:
415
486
  raise AquaRuntimeError(
416
487
  f"The given model already doesn't support finetuning: {ex}"
417
- )
488
+ ) from ex
418
489
 
419
490
  custom_metadata_list.remove("modelDescription")
420
491
  if task:
@@ -428,10 +499,9 @@ class AquaModelApp(AquaApp):
428
499
  freeform_tags=freeform_tags,
429
500
  )
430
501
  AquaApp().update_model(id, update_model_details)
502
+ logger.info(f"Updated model details for the model {id}.")
431
503
  else:
432
- raise AquaRuntimeError(
433
- f"Failed to edit model:{id}. Only registered unverified models can be edited."
434
- )
504
+ raise AquaRuntimeError("Only registered unverified models can be edited.")
435
505
 
436
506
  def _fetch_metric_from_metadata(
437
507
  self,
@@ -687,7 +757,7 @@ class AquaModelApp(AquaApp):
687
757
  )
688
758
 
689
759
  logger.info(
690
- f"Fetch {len(models)} model in compartment_id={compartment_id or ODSC_MODEL_COMPARTMENT_OCID}."
760
+ f"Fetched {len(models)} model in compartment_id={compartment_id or ODSC_MODEL_COMPARTMENT_OCID}."
691
761
  )
692
762
 
693
763
  aqua_models = []
@@ -717,10 +787,12 @@ class AquaModelApp(AquaApp):
717
787
  dict with the key used, and True if cache has the key that needs to be deleted.
718
788
  """
719
789
  res = {}
720
- logger.info("Clearing _service_models_cache")
721
790
  with self._cache_lock:
722
791
  if ODSC_MODEL_COMPARTMENT_OCID in self._service_models_cache:
723
792
  self._service_models_cache.pop(key=ODSC_MODEL_COMPARTMENT_OCID)
793
+ logger.info(
794
+ f"Cleared models cache for service compartment {ODSC_MODEL_COMPARTMENT_OCID}."
795
+ )
724
796
  res = {
725
797
  "key": {
726
798
  "compartment_id": ODSC_MODEL_COMPARTMENT_OCID,
@@ -737,10 +809,10 @@ class AquaModelApp(AquaApp):
737
809
  dict with the key used, and True if cache has the key that needs to be deleted.
738
810
  """
739
811
  res = {}
740
- logger.info(f"Clearing _service_model_details_cache for {model_id}")
741
812
  with self._cache_lock:
742
813
  if model_id in self._service_model_details_cache:
743
814
  self._service_model_details_cache.pop(key=model_id)
815
+ logger.info(f"Clearing model details cache for model {model_id}.")
744
816
  res = {"key": {"model_id": model_id}, "cache_deleted": True}
745
817
 
746
818
  return res
@@ -766,6 +838,8 @@ class AquaModelApp(AquaApp):
766
838
  compartment_id: Optional[str],
767
839
  project_id: Optional[str],
768
840
  inference_container_uri: Optional[str],
841
+ freeform_tags: Optional[dict] = None,
842
+ defined_tags: Optional[dict] = None,
769
843
  ) -> DataScienceModel:
770
844
  """Create model by reference from the object storage path
771
845
 
@@ -778,6 +852,8 @@ class AquaModelApp(AquaApp):
778
852
  compartment_id (Optional[str]): Compartment Id of the compartment where the model has to be created
779
853
  project_id (Optional[str]): Project id of the project where the model has to be created
780
854
  inference_container_uri (Optional[str]): Inference container uri for BYOC
855
+ freeform_tags (dict): Freeform tags for the model
856
+ defined_tags (dict): Defined tags for the model
781
857
 
782
858
  Returns:
783
859
  DataScienceModel: Returns Datascience model instance.
@@ -821,7 +897,8 @@ class AquaModelApp(AquaApp):
821
897
  metadata = ModelCustomMetadata()
822
898
  if not inference_container:
823
899
  raise AquaRuntimeError(
824
- 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."
900
+ f"Require Inference container information. Model: {model_name} does not have associated inference "
901
+ f"container defaults. Check docs for more information on how to pass inference container."
825
902
  )
826
903
  metadata.add(
827
904
  key=AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
@@ -846,8 +923,7 @@ class AquaModelApp(AquaApp):
846
923
  # only add cmd vars if inference container is not an SMC
847
924
  if (
848
925
  inference_container not in smc_container_set
849
- and inference_container
850
- == InferenceContainerTypeFamily.AQUA_TEI_CONTAINER_FAMILY
926
+ and inference_container in CustomInferenceContainerTypeFamily.values()
851
927
  ):
852
928
  cmd_vars = generate_tei_cmd_var(os_path)
853
929
  metadata.add(
@@ -892,24 +968,6 @@ class AquaModelApp(AquaApp):
892
968
  )
893
969
  tags[Tags.LICENSE] = validation_result.tags.get(Tags.LICENSE, UNKNOWN)
894
970
 
895
- try:
896
- # If verified model already has a artifact json, use that.
897
- artifact_path = metadata.get(MODEL_BY_REFERENCE_OSS_PATH_KEY).value
898
- logger.info(
899
- f"Found model artifact in the service bucket. "
900
- f"Using artifact from service bucket instead of {os_path}"
901
- )
902
-
903
- # todo: implement generic copy_folder method
904
- # copy model config from artifact path to user bucket
905
- copy_model_config(
906
- artifact_path=artifact_path, os_path=os_path, auth=default_signer()
907
- )
908
- except Exception:
909
- logger.debug(
910
- f"Proceeding with model registration without copying model config files at {os_path}. "
911
- f"Default configuration will be used for deployment and fine-tuning."
912
- )
913
971
  # Set artifact location to user bucket, and replace existing key if present.
914
972
  metadata.add(
915
973
  key=MODEL_BY_REFERENCE_OSS_PATH_KEY,
@@ -918,6 +976,8 @@ class AquaModelApp(AquaApp):
918
976
  category="Other",
919
977
  replace=True,
920
978
  )
979
+ # override tags with freeform tags if set
980
+ tags = {**tags, **(freeform_tags or {})}
921
981
  model = (
922
982
  model.with_custom_metadata_list(metadata)
923
983
  .with_compartment_id(compartment_id or COMPARTMENT_OCID)
@@ -925,8 +985,9 @@ class AquaModelApp(AquaApp):
925
985
  .with_artifact(os_path)
926
986
  .with_display_name(model_name)
927
987
  .with_freeform_tags(**tags)
988
+ .with_defined_tags(**(defined_tags or {}))
928
989
  ).create(model_by_reference=True)
929
- logger.debug(model)
990
+ logger.debug(f"Created model catalog entry for the model:\n{model}")
930
991
  return model
931
992
 
932
993
  @staticmethod
@@ -946,13 +1007,23 @@ class AquaModelApp(AquaApp):
946
1007
  # todo: revisit this logic to account for .bin files. In the current state, .bin and .safetensor models
947
1008
  # are grouped in one category and validation checks for config.json files only.
948
1009
  if model_format == ModelFormat.SAFETENSORS:
1010
+ model_files.extend(
1011
+ list_os_files_with_extension(oss_path=os_path, extension=".safetensors")
1012
+ )
949
1013
  try:
950
1014
  load_config(
951
1015
  file_path=os_path,
952
1016
  config_file_name=AQUA_MODEL_ARTIFACT_CONFIG,
953
1017
  )
954
- except Exception:
955
- pass
1018
+ except Exception as ex:
1019
+ message = (
1020
+ f"The model path {os_path} does not contain the file config.json. "
1021
+ f"Please check if the path is correct or the model artifacts are available at this location."
1022
+ )
1023
+ logger.warning(
1024
+ f"{message}\n"
1025
+ f"Details: {ex.reason if isinstance(ex, AquaFileNotFoundError) else str(ex)}\n"
1026
+ )
956
1027
  else:
957
1028
  model_files.append(AQUA_MODEL_ARTIFACT_CONFIG)
958
1029
 
@@ -960,6 +1031,9 @@ class AquaModelApp(AquaApp):
960
1031
  model_files.extend(
961
1032
  list_os_files_with_extension(oss_path=os_path, extension=".gguf")
962
1033
  )
1034
+ logger.debug(
1035
+ f"Fetched {len(model_files)} model files from {os_path} for model format {model_format}."
1036
+ )
963
1037
  return model_files
964
1038
 
965
1039
  @staticmethod
@@ -996,12 +1070,17 @@ class AquaModelApp(AquaApp):
996
1070
 
997
1071
  for model_sibling in model_siblings:
998
1072
  extension = pathlib.Path(model_sibling.rfilename).suffix[1:].upper()
999
- if model_format == ModelFormat.SAFETENSORS:
1000
- if model_sibling.rfilename == AQUA_MODEL_ARTIFACT_CONFIG:
1001
- model_files.append(model_sibling.rfilename)
1002
- elif extension == model_format.value:
1073
+ if (
1074
+ model_format == ModelFormat.SAFETENSORS
1075
+ and model_sibling.rfilename == AQUA_MODEL_ARTIFACT_CONFIG
1076
+ ):
1077
+ model_files.append(model_sibling.rfilename)
1078
+ if extension == model_format.value:
1003
1079
  model_files.append(model_sibling.rfilename)
1004
1080
 
1081
+ logger.debug(
1082
+ f"Fetched {len(model_files)} model files for the model {model_name} for model format {model_format}."
1083
+ )
1005
1084
  return model_files
1006
1085
 
1007
1086
  def _validate_model(
@@ -1035,7 +1114,10 @@ class AquaModelApp(AquaApp):
1035
1114
  safetensors_model_files = self.get_hf_model_files(
1036
1115
  model_name, ModelFormat.SAFETENSORS
1037
1116
  )
1038
- if safetensors_model_files:
1117
+ if (
1118
+ safetensors_model_files
1119
+ and AQUA_MODEL_ARTIFACT_CONFIG in safetensors_model_files
1120
+ ):
1039
1121
  hf_download_config_present = True
1040
1122
  gguf_model_files = self.get_hf_model_files(model_name, ModelFormat.GGUF)
1041
1123
  else:
@@ -1091,8 +1173,11 @@ class AquaModelApp(AquaApp):
1091
1173
  Tags.LICENSE: license_value,
1092
1174
  }
1093
1175
  validation_result.tags = hf_tags
1094
- except Exception:
1095
- pass
1176
+ except Exception as ex:
1177
+ logger.debug(
1178
+ f"An error occurred while getting tag information for model {model_name}. "
1179
+ f"Error: {str(ex)}"
1180
+ )
1096
1181
 
1097
1182
  validation_result.model_formats = model_formats
1098
1183
 
@@ -1147,40 +1232,55 @@ class AquaModelApp(AquaApp):
1147
1232
  model_name: str = None,
1148
1233
  ):
1149
1234
  if import_model_details.download_from_hf:
1150
- # validates config.json exists for safetensors model from hugginface
1151
- if not hf_download_config_present:
1235
+ # validates config.json exists for safetensors model from huggingface
1236
+ if not (
1237
+ hf_download_config_present
1238
+ or import_model_details.ignore_model_artifact_check
1239
+ ):
1152
1240
  raise AquaRuntimeError(
1153
1241
  f"The model {model_name} does not contain {AQUA_MODEL_ARTIFACT_CONFIG} file as required "
1154
1242
  f"by {ModelFormat.SAFETENSORS.value} format model."
1155
1243
  f" Please check if the model name is correct in Hugging Face repository."
1156
1244
  )
1245
+ validation_result.telemetry_model_name = model_name
1157
1246
  else:
1247
+ # validate if config.json is available from object storage, and get model name for telemetry
1248
+ model_config = None
1158
1249
  try:
1159
1250
  model_config = load_config(
1160
1251
  file_path=import_model_details.os_path,
1161
1252
  config_file_name=AQUA_MODEL_ARTIFACT_CONFIG,
1162
1253
  )
1163
1254
  except Exception as ex:
1164
- logger.error(
1165
- f"Exception occurred while loading config file from {import_model_details.os_path}"
1166
- f"Exception message: {ex}"
1167
- )
1168
- raise AquaRuntimeError(
1255
+ message = (
1169
1256
  f"The model path {import_model_details.os_path} does not contain the file config.json. "
1170
1257
  f"Please check if the path is correct or the model artifacts are available at this location."
1171
- ) from ex
1172
- else:
1258
+ )
1259
+ if not import_model_details.ignore_model_artifact_check:
1260
+ logger.error(
1261
+ f"{message}\n"
1262
+ f"Details: {ex.reason if isinstance(ex, AquaFileNotFoundError) else str(ex)}"
1263
+ )
1264
+ raise AquaRuntimeError(message) from ex
1265
+ else:
1266
+ logger.warning(
1267
+ f"{message}\n"
1268
+ f"Proceeding with model registration as ignore_model_artifact_check field is set."
1269
+ )
1270
+
1271
+ if verified_model:
1272
+ # model_type validation, log message if metadata field doesn't match.
1173
1273
  try:
1174
1274
  metadata_model_type = verified_model.custom_metadata_list.get(
1175
1275
  AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE
1176
1276
  ).value
1177
- if metadata_model_type:
1277
+ if metadata_model_type and model_config is not None:
1178
1278
  if AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE in model_config:
1179
1279
  if (
1180
1280
  model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE]
1181
1281
  != metadata_model_type
1182
1282
  ):
1183
- raise AquaRuntimeError(
1283
+ logger.debug(
1184
1284
  f"The {AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE} attribute in {AQUA_MODEL_ARTIFACT_CONFIG}"
1185
1285
  f" at {import_model_details.os_path} is invalid, expected {metadata_model_type} for "
1186
1286
  f"the model {model_name}. Please check if the path is correct or "
@@ -1192,22 +1292,26 @@ class AquaModelApp(AquaApp):
1192
1292
  f"Could not find {AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE} attribute in "
1193
1293
  f"{AQUA_MODEL_ARTIFACT_CONFIG}. Proceeding with model registration."
1194
1294
  )
1195
- except Exception:
1196
- pass
1197
- if verified_model:
1198
- validation_result.telemetry_model_name = verified_model.display_name
1199
- elif (
1200
- model_config is not None
1201
- and AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME in model_config
1202
- ):
1203
- validation_result.telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME]}"
1204
- elif (
1205
- model_config is not None
1206
- and AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE in model_config
1207
- ):
1208
- validation_result.telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE]}"
1209
- else:
1210
- validation_result.telemetry_model_name = AQUA_MODEL_TYPE_CUSTOM
1295
+ except Exception as ex:
1296
+ # todo: raise exception if model_type doesn't match. Currently log message and pass since service
1297
+ # models do not have this metadata.
1298
+ logger.debug(
1299
+ f"Error occurred while processing metadata for model {model_name}. "
1300
+ f"Exception: {str(ex)}"
1301
+ )
1302
+ validation_result.telemetry_model_name = verified_model.display_name
1303
+ elif (
1304
+ model_config is not None
1305
+ and AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME in model_config
1306
+ ):
1307
+ validation_result.telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_NAME]}"
1308
+ elif (
1309
+ model_config is not None
1310
+ and AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE in model_config
1311
+ ):
1312
+ validation_result.telemetry_model_name = f"{AQUA_MODEL_TYPE_CUSTOM}_{model_config[AQUA_MODEL_ARTIFACT_CONFIG_MODEL_TYPE]}"
1313
+ else:
1314
+ validation_result.telemetry_model_name = AQUA_MODEL_TYPE_CUSTOM
1211
1315
 
1212
1316
  @staticmethod
1213
1317
  def _validate_gguf_format(
@@ -1296,25 +1400,29 @@ class AquaModelApp(AquaApp):
1296
1400
  Returns
1297
1401
  -------
1298
1402
  model_artifact_path (str): Location where the model artifacts are downloaded.
1299
-
1300
1403
  """
1301
1404
  # Download the model from hub
1302
- if not local_dir:
1303
- local_dir = os.path.join(os.path.expanduser("~"), "cached-model")
1304
- local_dir = os.path.join(local_dir, model_name)
1305
- os.makedirs(local_dir, exist_ok=True)
1306
- snapshot_download(
1405
+ if local_dir:
1406
+ local_dir = os.path.join(local_dir, model_name)
1407
+ os.makedirs(local_dir, exist_ok=True)
1408
+
1409
+ # if local_dir is not set, the return value points to the cached data folder
1410
+ local_dir = snapshot_download(
1307
1411
  repo_id=model_name,
1308
1412
  local_dir=local_dir,
1309
1413
  allow_patterns=allow_patterns,
1310
1414
  ignore_patterns=ignore_patterns,
1311
1415
  )
1312
1416
  # Upload to object storage and skip .cache/huggingface/ folder
1417
+ logger.debug(
1418
+ f"Uploading local artifacts from local directory {local_dir} to {os_path}."
1419
+ )
1420
+ # Upload to object storage
1313
1421
  model_artifact_path = upload_folder(
1314
1422
  os_path=os_path,
1315
1423
  local_dir=local_dir,
1316
1424
  model_name=model_name,
1317
- exclude_pattern=f"{HF_METADATA_FOLDER}*"
1425
+ exclude_pattern=f"{HF_METADATA_FOLDER}*",
1318
1426
  )
1319
1427
 
1320
1428
  return model_artifact_path
@@ -1339,6 +1447,8 @@ class AquaModelApp(AquaApp):
1339
1447
  ignore_patterns (list): Model files matching any of the patterns are not downloaded.
1340
1448
  Example: ["*.json"] will ignore all .json files. ["folder/*"] will ignore all files under `folder`.
1341
1449
  Patterns are Standard Wildcards (globbing patterns) and rules can be found here: https://docs.python.org/3/library/fnmatch.html
1450
+ cleanup_model_cache (bool): Deletes downloaded files from local machine after model is successfully
1451
+ registered. Set to True by default.
1342
1452
 
1343
1453
  Returns:
1344
1454
  AquaModel:
@@ -1353,6 +1463,7 @@ class AquaModelApp(AquaApp):
1353
1463
  import_model_details.model.startswith("ocid")
1354
1464
  and "datasciencemodel" in import_model_details.model
1355
1465
  ):
1466
+ logger.info(f"Fetching details for model {import_model_details.model}.")
1356
1467
  verified_model = DataScienceModel.from_id(import_model_details.model)
1357
1468
  else:
1358
1469
  # 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
@@ -1390,7 +1501,6 @@ class AquaModelApp(AquaApp):
1390
1501
  ).rstrip("/")
1391
1502
  else:
1392
1503
  artifact_path = import_model_details.os_path.rstrip("/")
1393
-
1394
1504
  # Create Model catalog entry with pass by reference
1395
1505
  ds_model = self._create_model_catalog_entry(
1396
1506
  os_path=artifact_path,
@@ -1402,6 +1512,8 @@ class AquaModelApp(AquaApp):
1402
1512
  compartment_id=import_model_details.compartment_id,
1403
1513
  project_id=import_model_details.project_id,
1404
1514
  inference_container_uri=import_model_details.inference_container_uri,
1515
+ freeform_tags=import_model_details.freeform_tags,
1516
+ defined_tags=import_model_details.defined_tags,
1405
1517
  )
1406
1518
  # registered model will always have inference and evaluation container, but
1407
1519
  # fine-tuning container may be not set
@@ -1446,6 +1558,14 @@ class AquaModelApp(AquaApp):
1446
1558
  detail=validation_result.telemetry_model_name,
1447
1559
  )
1448
1560
 
1561
+ if (
1562
+ import_model_details.download_from_hf
1563
+ and import_model_details.cleanup_model_cache
1564
+ ):
1565
+ cleanup_local_hf_model_artifact(
1566
+ model_name=model_name, local_dir=import_model_details.local_dir
1567
+ )
1568
+
1449
1569
  return AquaModel(**aqua_model_attributes)
1450
1570
 
1451
1571
  def _if_show(self, model: DataScienceModel) -> bool:
@@ -1473,7 +1593,7 @@ class AquaModelApp(AquaApp):
1473
1593
  elif model_type == ModelType.BASE:
1474
1594
  filter_tag = Tags.BASE_MODEL_CUSTOM
1475
1595
  else:
1476
- raise ValueError(
1596
+ raise AquaValueError(
1477
1597
  f"Model of type {model_type} is unknown. The values should be in {ModelType.values()}"
1478
1598
  )
1479
1599
 
@@ -1513,7 +1633,10 @@ class AquaModelApp(AquaApp):
1513
1633
  oci_model = self.ds_client.get_model(model_id).data
1514
1634
  artifact_path = get_artifact_path(oci_model.custom_metadata_list)
1515
1635
  if not artifact_path:
1516
- raise AquaRuntimeError("Failed to get artifact path from custom metadata.")
1636
+ raise AquaRuntimeError(
1637
+ f"License could not be loaded. Failed to get artifact path from custom metadata for"
1638
+ f"the model {model_id}."
1639
+ )
1517
1640
 
1518
1641
  content = str(
1519
1642
  read_file(
@@ -1544,6 +1667,9 @@ class AquaModelApp(AquaApp):
1544
1667
 
1545
1668
  for aqua_model_summary in aqua_model_list:
1546
1669
  if aqua_model_summary.name.lower() == model_id_lower:
1670
+ logger.info(
1671
+ f"Found matching verified model id {aqua_model_summary.id} for the model {model_id}"
1672
+ )
1547
1673
  return aqua_model_summary.id
1548
1674
 
1549
1675
  return None