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
@@ -16,16 +16,14 @@ from ads.aqua.common.entities import (
16
16
  AquaMultiModelRef,
17
17
  ComputeShapeSummary,
18
18
  ContainerPath,
19
- ContainerSpec,
20
19
  )
21
20
  from ads.aqua.common.enums import InferenceContainerTypeFamily, ModelFormat, Tags
22
21
  from ads.aqua.common.errors import AquaRuntimeError, AquaValueError
23
22
  from ads.aqua.common.utils import (
23
+ DEFINED_METADATA_TO_FILE_MAP,
24
24
  build_params_string,
25
25
  build_pydantic_error_message,
26
26
  get_combined_params,
27
- get_container_config,
28
- get_container_image,
29
27
  get_container_params_type,
30
28
  get_model_by_reference_paths,
31
29
  get_ocid_substring,
@@ -50,7 +48,7 @@ from ads.aqua.constants import (
50
48
  from ads.aqua.data import AquaResourceIdentifier
51
49
  from ads.aqua.finetuning.finetuning import FineTuneCustomMetadata
52
50
  from ads.aqua.model import AquaModelApp
53
- from ads.aqua.model.constants import ModelCustomMetadataFields
51
+ from ads.aqua.model.constants import AquaModelMetadataKeys, ModelCustomMetadataFields
54
52
  from ads.aqua.modeldeployment.entities import (
55
53
  AquaDeployment,
56
54
  AquaDeploymentConfig,
@@ -67,7 +65,6 @@ from ads.config import (
67
65
  AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME,
68
66
  AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
69
67
  AQUA_DEPLOYMENT_CONTAINER_URI_METADATA_NAME,
70
- AQUA_MODEL_DEPLOYMENT_CONFIG,
71
68
  COMPARTMENT_OCID,
72
69
  PROJECT_OCID,
73
70
  )
@@ -182,7 +179,7 @@ class AquaDeploymentApp(AquaApp):
182
179
  available_shapes = [
183
180
  shape.name.lower()
184
181
  for shape in self.list_shapes(
185
- compartment_id=create_deployment_details.compartment_id
182
+ compartment_id=compartment_id
186
183
  )
187
184
  ]
188
185
 
@@ -193,7 +190,7 @@ class AquaDeploymentApp(AquaApp):
193
190
  )
194
191
 
195
192
  # Get container config
196
- container_config = get_container_config()
193
+ container_config = self.get_container_config()
197
194
 
198
195
  # Create an AquaModelApp instance once to perform the deployment creation.
199
196
  model_app = AquaModelApp()
@@ -228,17 +225,13 @@ class AquaDeploymentApp(AquaApp):
228
225
  except ConfigValidationError as err:
229
226
  raise AquaValueError(f"{err}") from err
230
227
 
231
- service_inference_containers = (
232
- AquaContainerConfig.from_container_index_json(
233
- config=container_config
234
- ).inference.values()
235
- )
228
+ service_inference_containers = container_config.inference.values()
236
229
 
237
230
  supported_container_families = [
238
231
  container_config_item.family
239
232
  for container_config_item in service_inference_containers
240
233
  if any(
241
- usage in container_config_item.usages
234
+ usage.upper() in container_config_item.usages
242
235
  for usage in [Usage.MULTI_MODEL, Usage.OTHER]
243
236
  )
244
237
  ]
@@ -409,7 +402,7 @@ class AquaDeploymentApp(AquaApp):
409
402
 
410
403
  container_image_uri = (
411
404
  create_deployment_details.container_image_uri
412
- or get_container_image(container_type=container_type_key)
405
+ or self.get_container_image(container_type=container_type_key)
413
406
  )
414
407
  if not container_image_uri:
415
408
  try:
@@ -475,22 +468,21 @@ class AquaDeploymentApp(AquaApp):
475
468
  env_var.update({"BASE_MODEL_FILE": f"{model_file}"})
476
469
  tags.update({Tags.MODEL_ARTIFACT_FILE: model_file})
477
470
 
478
- # todo: use AquaContainerConfig.from_container_index_json instead.
479
471
  # Fetch the startup cli command for the container
480
472
  # container_index.json will have "containerSpec" section which will provide the cli params for
481
473
  # a given container family
482
- container_spec = container_config.get(ContainerSpec.CONTAINER_SPEC, {}).get(
483
- container_type_key, {}
484
- )
474
+ container_config = self.get_container_config_item(container_type_key)
475
+
476
+ container_spec = container_config.spec if container_config else UNKNOWN
485
477
  # these params cannot be overridden for Aqua deployments
486
- params = container_spec.get(ContainerSpec.CLI_PARM, "")
487
- server_port = create_deployment_details.server_port or container_spec.get(
488
- ContainerSpec.SERVER_PORT
489
- ) # Give precedence to the input parameter
490
- health_check_port = (
491
- create_deployment_details.health_check_port
492
- or container_spec.get(ContainerSpec.HEALTH_CHECK_PORT)
493
- ) # Give precedence to the input parameter
478
+ params = container_spec.cli_param if container_spec else UNKNOWN
479
+ server_port = create_deployment_details.server_port or (
480
+ container_spec.server_port if container_spec else None
481
+ )
482
+ # Give precendece to the input parameter
483
+ health_check_port = create_deployment_details.health_check_port or (
484
+ container_spec.health_check_port if container_spec else None
485
+ )
494
486
 
495
487
  deployment_config = self.get_deployment_config(model_id=config_source_id)
496
488
 
@@ -527,9 +519,10 @@ class AquaDeploymentApp(AquaApp):
527
519
  params = f"{params} {deployment_params}".strip()
528
520
  if params:
529
521
  env_var.update({"PARAMS": params})
530
-
531
- for env in container_spec.get(ContainerSpec.ENV_VARS, []):
522
+ env_vars = container_spec.env_vars if container_spec else []
523
+ for env in env_vars:
532
524
  if isinstance(env, dict):
525
+ env = {k: v for k, v in env.items() if v}
533
526
  for key, _ in env.items():
534
527
  if key not in env_var:
535
528
  env_var.update(env)
@@ -559,7 +552,7 @@ class AquaDeploymentApp(AquaApp):
559
552
  aqua_model: DataScienceModel,
560
553
  model_config_summary: ModelDeploymentConfigSummary,
561
554
  create_deployment_details: CreateModelDeploymentDetails,
562
- container_config: Dict,
555
+ container_config: AquaContainerConfig,
563
556
  ) -> AquaDeployment:
564
557
  """Builds the environment variables required by multi deployment container and creates the deployment.
565
558
 
@@ -587,11 +580,10 @@ class AquaDeploymentApp(AquaApp):
587
580
  model=aqua_model,
588
581
  container_family=create_deployment_details.container_family,
589
582
  )
590
- container_spec = container_config.get(
591
- ContainerSpec.CONTAINER_SPEC, UNKNOWN_DICT
592
- ).get(container_type_key, UNKNOWN_DICT)
583
+ container_config = self.get_container_config_item(container_type_key)
584
+ container_spec = container_config.spec if container_config else UNKNOWN
593
585
 
594
- container_params = container_spec.get(ContainerSpec.CLI_PARM, UNKNOWN).strip()
586
+ container_params = container_spec.cli_param if container_spec else UNKNOWN
595
587
 
596
588
  for model in create_deployment_details.models:
597
589
  user_params = build_params_string(model.env_var)
@@ -658,8 +650,10 @@ class AquaDeploymentApp(AquaApp):
658
650
 
659
651
  env_var.update({AQUA_MULTI_MODEL_CONFIG: json.dumps({"models": model_config})})
660
652
 
661
- for env in container_spec.get(ContainerSpec.ENV_VARS, []):
653
+ env_vars = container_spec.env_vars if container_spec else []
654
+ for env in env_vars:
662
655
  if isinstance(env, dict):
656
+ env = {k: v for k, v in env.items() if v}
663
657
  for key, _ in env.items():
664
658
  if key not in env_var:
665
659
  env_var.update(env)
@@ -668,14 +662,13 @@ class AquaDeploymentApp(AquaApp):
668
662
 
669
663
  container_image_uri = (
670
664
  create_deployment_details.container_image_uri
671
- or get_container_image(container_type=container_type_key)
665
+ or self.get_container_image(container_type=container_type_key)
672
666
  )
673
- server_port = create_deployment_details.server_port or container_spec.get(
674
- ContainerSpec.SERVER_PORT
667
+ server_port = create_deployment_details.server_port or (
668
+ container_spec.server_port if container_spec else None
675
669
  )
676
- health_check_port = (
677
- create_deployment_details.health_check_port
678
- or container_spec.get(ContainerSpec.HEALTH_CHECK_PORT)
670
+ health_check_port = create_deployment_details.health_check_port or (
671
+ container_spec.health_check_port if container_spec else None
679
672
  )
680
673
  tags = {
681
674
  Tags.AQUA_MODEL_ID_TAG: aqua_model.id,
@@ -1054,7 +1047,20 @@ class AquaDeploymentApp(AquaApp):
1054
1047
  AquaDeploymentConfig:
1055
1048
  An instance of AquaDeploymentConfig.
1056
1049
  """
1057
- config = self.get_config(model_id, AQUA_MODEL_DEPLOYMENT_CONFIG).config
1050
+ config = self.get_config_from_metadata(
1051
+ model_id, AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION
1052
+ ).config
1053
+ if config:
1054
+ logger.info(
1055
+ f"Fetched {AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION} from defined metadata for model: {model_id}."
1056
+ )
1057
+ return AquaDeploymentConfig(**(config or UNKNOWN_DICT))
1058
+ config = self.get_config(
1059
+ model_id,
1060
+ DEFINED_METADATA_TO_FILE_MAP.get(
1061
+ AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION.lower()
1062
+ ),
1063
+ ).config
1058
1064
  if not config:
1059
1065
  logger.debug(
1060
1066
  f"Deployment config for custom model: {model_id} is not available. Use defaults."
@@ -1079,7 +1085,7 @@ class AquaDeploymentApp(AquaApp):
1079
1085
  https://github.com/oracle-samples/oci-data-science-ai-samples/blob/main/ai-quick-actions/multimodel-deployment-tips.md#get_multimodel_deployment_config
1080
1086
 
1081
1087
  CLI example:
1082
- ads aqua deployment get_multimodel_deployment_config --model_ids '["ocid1.datasciencemodel.oc1.iad.OCID"]'
1088
+ ads aqua deployment get_multimodel_deployment_config --model_ids '["md_ocid1","md_ocid2"]'
1083
1089
 
1084
1090
  If a primary model ID is provided, GPU allocation will prioritize that model
1085
1091
  when selecting compatible shapes.
@@ -1230,11 +1236,9 @@ class AquaDeploymentApp(AquaApp):
1230
1236
  model=model, container_family=container_family
1231
1237
  )
1232
1238
 
1233
- container_config = get_container_config()
1234
- container_spec = container_config.get(ContainerSpec.CONTAINER_SPEC, {}).get(
1235
- container_type_key, {}
1236
- )
1237
- cli_params = container_spec.get(ContainerSpec.CLI_PARM, "")
1239
+ container_config = self.get_container_config_item(container_type_key)
1240
+ container_spec = container_config.spec if container_config else UNKNOWN
1241
+ cli_params = container_spec.cli_param if container_spec else UNKNOWN
1238
1242
 
1239
1243
  restricted_params = self._find_restricted_params(
1240
1244
  cli_params, params, container_type_key
@@ -11,7 +11,8 @@ from concurrent.futures import ThreadPoolExecutor
11
11
  from typing import Dict, List, Optional
12
12
 
13
13
  from ads.aqua.app import AquaApp
14
- from ads.aqua.common.entities import ComputeShapeSummary
14
+ from ads.aqua.common.entities import ComputeShapeSummary, ModelConfigResult
15
+ from ads.aqua.model.constants import AquaModelMetadataKeys
15
16
  from ads.aqua.modeldeployment.entities import (
16
17
  AquaDeploymentConfig,
17
18
  ConfigurationItem,
@@ -183,17 +184,34 @@ class MultiModelDeploymentConfigLoader:
183
184
  """Fetches deployment configurations in parallel using ThreadPoolExecutor."""
184
185
  with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor:
185
186
  results = executor.map(
186
- lambda model_id: self.deployment_app.get_config(
187
- model_id, AQUA_MODEL_DEPLOYMENT_CONFIG
188
- ).config,
187
+ self._fetch_deployment_config_from_metadata_and_oss,
189
188
  model_ids,
190
189
  )
191
190
 
192
191
  return {
193
- model_id: AquaDeploymentConfig(**config)
192
+ model_id: AquaDeploymentConfig(**config.config)
194
193
  for model_id, config in zip(model_ids, results)
195
194
  }
196
195
 
196
+ def _fetch_deployment_config_from_metadata_and_oss(
197
+ self, model_id
198
+ ) -> ModelConfigResult:
199
+ config = self.deployment_app.get_config_from_metadata(
200
+ model_id, AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION
201
+ )
202
+ if config:
203
+ logger.info(
204
+ f"Fetched metadata key '{AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION}' from defined metadata for model '{model_id}'"
205
+ )
206
+ return config
207
+ else:
208
+ logger.info(
209
+ f"Fetching '{AquaModelMetadataKeys.DEPLOYMENT_CONFIGURATION}' from object storage bucket for {model_id}'"
210
+ )
211
+ return self.deployment_app.get_config(
212
+ model_id, AQUA_MODEL_DEPLOYMENT_CONFIG
213
+ )
214
+
197
215
  def _extract_model_shape_gpu(
198
216
  self,
199
217
  deployment_configs: Dict[str, AquaDeploymentConfig],
ads/aqua/ui.py CHANGED
@@ -13,7 +13,7 @@ from ads.aqua import logger
13
13
  from ads.aqua.app import AquaApp
14
14
  from ads.aqua.common.enums import Tags
15
15
  from ads.aqua.common.errors import AquaResourceAccessError, AquaValueError
16
- from ads.aqua.common.utils import get_container_config, sanitize_response
16
+ from ads.aqua.common.utils import sanitize_response
17
17
  from ads.aqua.config.container_config import AquaContainerConfig
18
18
  from ads.aqua.constants import PRIVATE_ENDPOINT_TYPE
19
19
  from ads.common import oci_client as oc
@@ -494,7 +494,6 @@ class AquaUIApp(AquaApp):
494
494
  AquaContainerConfig
495
495
  The AQUA containers configurations.
496
496
  """
497
- return AquaContainerConfig.from_container_index_json(
498
- config=get_container_config(),
499
- enable_spec=True,
497
+ return AquaContainerConfig.from_service_config(
498
+ service_containers=self.list_service_containers()
500
499
  )
ads/config.py CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
 
3
- # Copyright (c) 2020, 2024 Oracle and/or its affiliates.
3
+ # Copyright (c) 2020, 2025 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
 
6
6
  import contextlib
@@ -56,6 +56,7 @@ AQUA_MODEL_FINETUNING_CONFIG = os.environ.get(
56
56
  AQUA_CONTAINER_INDEX_CONFIG = os.environ.get(
57
57
  "AQUA_CONTAINER_INDEX_CONFIG", "container_index.json"
58
58
  )
59
+ AQUA_SERVICE_MODELS = "aqua-service-models-compartment"
59
60
  AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME = "deployment-container"
60
61
  AQUA_FINETUNING_CONTAINER_METADATA_NAME = "finetune-container"
61
62
  AQUA_EVALUATION_CONTAINER_METADATA_NAME = "evaluation-container"
@@ -84,7 +85,6 @@ USER = "USER"
84
85
  SERVICE = "SERVICE"
85
86
 
86
87
 
87
-
88
88
  THREADED_DEFAULT_TIMEOUT = 5
89
89
  try:
90
90
  THREADED_DEFAULT_TIMEOUT = int(
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8; -*-
3
2
 
4
- # Copyright (c) 2020, 2022 Oracle and/or its affiliates.
3
+ # Copyright (c) 2020, 2025 Oracle and/or its affiliates.
5
4
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
5
 
7
6
  import copy
8
7
  import importlib
9
8
 
10
9
  from ads.common.decorator.runtime_dependency import (
11
- runtime_dependency,
12
10
  OptionalDependency,
11
+ runtime_dependency,
13
12
  )
14
13
  from ads.dataset import logger
15
14
 
@@ -59,11 +58,10 @@ class Recommendation:
59
58
  if change["type"] == "change" and change["name"] == "value":
60
59
  if change["new"] == "Fill missing values with constant":
61
60
  self._show_constant_fill_widget(column)
62
- else:
63
- if change["old"] == "Fill missing values with constant":
64
- text = self.fill_nan_dict.pop(column, None)
65
- if text is not None:
66
- text.close()
61
+ elif change["old"] == "Fill missing values with constant":
62
+ text = self.fill_nan_dict.pop(column, None)
63
+ if text is not None:
64
+ text.close()
67
65
  self.reco_dict[recommendation_type][column]["Selected Action"] = change[
68
66
  "new"
69
67
  ]
@@ -151,7 +149,6 @@ class Recommendation:
151
149
  @runtime_dependency(module="IPython", install_from=OptionalDependency.NOTEBOOK)
152
150
  @runtime_dependency(module="ipywidgets", install_from=OptionalDependency.NOTEBOOK)
153
151
  def _display(self):
154
-
155
152
  from IPython.core.display import display
156
153
 
157
154
  if self.recommendation_type_index != len(self.recommendation_types):
@@ -164,11 +161,7 @@ class Recommendation:
164
161
  ]
165
162
  if self.recommendation_type_index == 0:
166
163
  for column in recommendation:
167
- print(
168
- "Column '{0}' is constant and will be dropped".format(
169
- column
170
- )
171
- )
164
+ print(f"Column '{column}' is constant and will be dropped")
172
165
  self.recommendation_type_index += 1
173
166
  self._display()
174
167
  return
@@ -184,9 +177,9 @@ class Recommendation:
184
177
  self.recommendation_type_labels[
185
178
  self.recommendation_type_index
186
179
  ]
187
- )
180
+ ),
181
+ layout=ipywidgets.Layout(display="flex"),
188
182
  ),
189
- layout=ipywidgets.Layout(display="flex"),
190
183
  )
191
184
  self.action_list[
192
185
  self.recommendation_types[self.recommendation_type_index]
@@ -194,7 +187,7 @@ class Recommendation:
194
187
  self.column_list = []
195
188
  self.message_list = []
196
189
  self.extra_info_list = []
197
- for column in recommendation.keys():
190
+ for column in recommendation:
198
191
  messages = ipywidgets.Label(
199
192
  recommendation[column]["Message"],
200
193
  layout=ipywidgets.Layout(
@@ -240,9 +233,7 @@ class Recommendation:
240
233
  self.column_list.append(
241
234
  ipywidgets.Label(
242
235
  column
243
- + "(type: {0})".format(
244
- self.ds.sampled_df[column].dtype
245
- ),
236
+ + f"(type: {self.ds.sampled_df[column].dtype})",
246
237
  layout=ipywidgets.Layout(flex="auto"),
247
238
  color="grey",
248
239
  )
@@ -2240,6 +2240,35 @@ class DataScienceModel(Builder):
2240
2240
  # model found case
2241
2241
  self.model_file_description["models"].pop(modelSearchIdx)
2242
2242
 
2243
+ def if_model_custom_metadata_artifact_exist(
2244
+ self, metadata_key_name: str, **kwargs
2245
+ ) -> bool:
2246
+ """Checks if the custom metadata artifact exists for the model.
2247
+
2248
+ Parameters
2249
+ ----------
2250
+ metadata_key_name: str
2251
+ Custom metadata key name
2252
+ **kwargs :
2253
+ Additional keyword arguments passed in head_model_artifact.
2254
+
2255
+ Returns
2256
+ -------
2257
+ bool
2258
+ Whether the artifact exists.
2259
+ """
2260
+
2261
+ try:
2262
+ response = self.dsc_model.head_custom_metadata_artifact(
2263
+ metadata_key_name=metadata_key_name, **kwargs
2264
+ )
2265
+ return int(response.status) == 200
2266
+ except Exception as ex:
2267
+ logger.info(
2268
+ f"Error fetching custom metadata: {metadata_key_name} for model {self.id}. {ex}"
2269
+ )
2270
+ return False
2271
+
2243
2272
  def create_custom_metadata_artifact(
2244
2273
  self,
2245
2274
  metadata_key_name: str,
@@ -110,7 +110,7 @@ def check_for_model_id(msg: str = MODEL_NEEDS_TO_BE_SAVED):
110
110
  def convert_model_metadata_response(
111
111
  headers: Union[Dict, CaseInsensitiveDict], status: int
112
112
  ) -> ModelMetadataArtifactDetails:
113
- return ModelMetadataArtifactDetails(headers=headers, status=str(status))
113
+ return ModelMetadataArtifactDetails(headers=dict(headers), status=str(status))
114
114
 
115
115
 
116
116
  class OCIDataScienceModel(
@@ -4,16 +4,18 @@
4
4
  # Copyright (c) 2023 Oracle and/or its affiliates.
5
5
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
6
 
7
- from ..operator_config import AnomalyOperatorSpec
7
+ import pandas as pd
8
+
9
+ from ads.opctl import logger
10
+ from ads.opctl.operator.lowcode.anomaly.const import OutputColumns
11
+ from ads.opctl.operator.lowcode.anomaly.utils import get_frequency_of_datetime
12
+ from ads.opctl.operator.lowcode.common.data import AbstractData
8
13
  from ads.opctl.operator.lowcode.common.utils import (
9
14
  default_signer,
10
15
  merge_category_columns,
11
16
  )
12
- from ads.opctl.operator.lowcode.common.data import AbstractData
13
- from ads.opctl.operator.lowcode.anomaly.utils import get_frequency_of_datetime
14
- from ads.opctl import logger
15
- import pandas as pd
16
- from ads.opctl.operator.lowcode.anomaly.const import OutputColumns
17
+
18
+ from ..operator_config import AnomalyOperatorSpec
17
19
 
18
20
 
19
21
  class AnomalyData(AbstractData):
@@ -53,6 +53,7 @@ class AnomalyOperatorBaseModel(ABC):
53
53
  self.config: AnomalyOperatorConfig = config
54
54
  self.spec: AnomalyOperatorSpec = config.spec
55
55
  self.datasets = datasets
56
+
56
57
  if self.spec.validation_data is not None:
57
58
  self.X_valid_dict = self.datasets.valid_data.X_valid_dict
58
59
  self.y_valid_dict = self.datasets.valid_data.y_valid_dict
@@ -74,7 +75,6 @@ class AnomalyOperatorBaseModel(ABC):
74
75
  logger.warning(f"Found exception: {e}")
75
76
  if self.spec.datetime_column:
76
77
  anomaly_output = self._fallback_build_model()
77
- raise e
78
78
 
79
79
  elapsed_time = time.time() - start_time
80
80
 
@@ -9,15 +9,16 @@ from dataclasses import dataclass, field
9
9
  from typing import Dict, List
10
10
 
11
11
  from ads.common.serializer import DataClassSerializable
12
- from ads.opctl.operator.common.utils import _load_yaml_from_uri
13
12
  from ads.opctl.operator.common.operator_config import (
13
+ InputData,
14
14
  OperatorConfig,
15
15
  OutputDirectory,
16
- InputData,
17
16
  )
18
- from .const import SupportedModels
17
+ from ads.opctl.operator.common.utils import _load_yaml_from_uri
19
18
  from ads.opctl.operator.lowcode.common.utils import find_output_dirname
20
19
 
20
+ from .const import SupportedModels
21
+
21
22
 
22
23
  @dataclass(repr=True)
23
24
  class ValidationData(InputData):
@@ -61,6 +62,7 @@ class AnomalyOperatorSpec(DataClassSerializable):
61
62
  test_data: TestData = field(default_factory=TestData)
62
63
  validation_data: ValidationData = field(default_factory=ValidationData)
63
64
  output_directory: OutputDirectory = field(default_factory=OutputDirectory)
65
+ preprocessing: DataPreprocessor = field(default_factory=DataPreprocessor)
64
66
  report_file_name: str = None
65
67
  report_title: str = None
66
68
  report_theme: str = None
@@ -91,6 +91,8 @@ class Transformations(ABC):
91
91
  logger.info("Skipping outlier treatment because it is disabled")
92
92
  elif self.name == "additional_data":
93
93
  clean_df = self._missing_value_imputation_add(clean_df)
94
+ elif self.name == "input_data" and self.preprocessing.steps.missing_value_imputation:
95
+ clean_df = self._fill_na(clean_df)
94
96
  else:
95
97
  logger.info(
96
98
  "Skipping all preprocessing steps because preprocessing is disabled"
@@ -159,6 +159,7 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
159
159
  self.models[s_id] = {}
160
160
  self.models[s_id]["model"] = model
161
161
  self.models[s_id]["le"] = self.le[s_id]
162
+ self.models[s_id]["score"] = self.get_validation_score_and_metric(model)
162
163
 
163
164
  # In case of Naive model, model.forecast function call does not return confidence intervals.
164
165
  if f"{target}_ci_upper" not in summary_frame:
@@ -511,3 +512,31 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
511
512
  f"Failed to generate explanations for series {s_id} with error: {e}."
512
513
  )
513
514
  logger.debug(f"Full Traceback: {traceback.format_exc()}")
515
+
516
+ def get_validation_score_and_metric(self, model):
517
+ trials = model.completed_trials_summary_
518
+ model_params = model.selected_model_params_
519
+ if len(trials) > 0:
520
+ score_col = [col for col in trials.columns if "Score" in col][0]
521
+ validation_score = trials[trials.Hyperparameters == model_params][score_col].iloc[0]
522
+ else:
523
+ validation_score = 0
524
+ return -1 * validation_score
525
+
526
+ def generate_train_metrics(self) -> pd.DataFrame:
527
+ """
528
+ Generate Training Metrics when fitted data is not available.
529
+ """
530
+ total_metrics = pd.DataFrame()
531
+ for s_id in self.forecast_output.list_series_ids():
532
+ try:
533
+ metrics = {self.spec.metric.upper(): self.models[s_id]["score"]}
534
+ metrics_df = pd.DataFrame.from_dict(metrics, orient="index", columns=[s_id])
535
+ logger.warning("AutoMLX failed to generate training metrics. Recovering validation loss instead")
536
+ total_metrics = pd.concat([total_metrics, metrics_df], axis=1)
537
+ except Exception as e:
538
+ logger.debug(
539
+ f"Failed to generate training metrics for target_series: {s_id}"
540
+ )
541
+ logger.debug(f"Error: {e}")
542
+ return total_metrics