oracle-ads 2.13.7__py3-none-any.whl → 2.13.9__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.
ads/aqua/app.py CHANGED
@@ -64,6 +64,7 @@ class AquaApp:
64
64
  set_auth("resource_principal")
65
65
  self._auth = default_signer({"service_endpoint": OCI_ODSC_SERVICE_ENDPOINT})
66
66
  self.ds_client = oc.OCIClientFactory(**self._auth).data_science
67
+ self.compute_client = oc.OCIClientFactory(**default_signer()).compute
67
68
  self.logging_client = oc.OCIClientFactory(**default_signer()).logging_management
68
69
  self.identity_client = oc.OCIClientFactory(**default_signer()).identity
69
70
  self.region = extract_region(self._auth)
@@ -151,10 +151,14 @@ class AquaMultiModelRef(Serializable):
151
151
  The name of the model.
152
152
  gpu_count : Optional[int]
153
153
  Number of GPUs required for deployment.
154
+ model_task : Optional[str]
155
+ The task that model operates on. Supported tasks are in MultiModelSupportedTaskType
154
156
  env_var : Optional[Dict[str, Any]]
155
157
  Optional environment variables to override during deployment.
156
158
  artifact_location : Optional[str]
157
159
  Artifact path of model in the multimodel group.
160
+ fine_tune_weights_location : Optional[str]
161
+ For fine tuned models, the artifact path of the modified model weights
158
162
  """
159
163
 
160
164
  model_id: str = Field(..., description="The model OCID to deploy.")
@@ -162,12 +166,16 @@ class AquaMultiModelRef(Serializable):
162
166
  gpu_count: Optional[int] = Field(
163
167
  None, description="The gpu count allocation for the model."
164
168
  )
169
+ model_task: Optional[str] = Field(None, description="The task that model operates on. Supported tasks are in MultiModelSupportedTaskType")
165
170
  env_var: Optional[dict] = Field(
166
171
  default_factory=dict, description="The environment variables of the model."
167
172
  )
168
173
  artifact_location: Optional[str] = Field(
169
174
  None, description="Artifact path of model in the multimodel group."
170
175
  )
176
+ fine_tune_weights_location: Optional[str] = Field(
177
+ None, description="For fine tuned models, the artifact path of the modified model weights"
178
+ )
171
179
 
172
180
  class Config:
173
181
  extra = "ignore"
ads/aqua/common/enums.py CHANGED
@@ -49,6 +49,7 @@ class InferenceContainerType(ExtendedEnum):
49
49
  class InferenceContainerTypeFamily(ExtendedEnum):
50
50
  AQUA_VLLM_CONTAINER_FAMILY = "odsc-vllm-serving"
51
51
  AQUA_VLLM_V1_CONTAINER_FAMILY = "odsc-vllm-serving-v1"
52
+ AQUA_VLLM_LLAMA4_CONTAINER_FAMILY = "odsc-vllm-serving-llama4"
52
53
  AQUA_TGI_CONTAINER_FAMILY = "odsc-tgi-serving"
53
54
  AQUA_LLAMA_CPP_CONTAINER_FAMILY = "odsc-llama-cpp-serving"
54
55
 
@@ -119,4 +120,9 @@ CONTAINER_FAMILY_COMPATIBILITY: Dict[str, List[str]] = {
119
120
  InferenceContainerTypeFamily.AQUA_VLLM_V1_CONTAINER_FAMILY,
120
121
  InferenceContainerTypeFamily.AQUA_VLLM_CONTAINER_FAMILY,
121
122
  ],
123
+ InferenceContainerTypeFamily.AQUA_VLLM_LLAMA4_CONTAINER_FAMILY: [
124
+ InferenceContainerTypeFamily.AQUA_VLLM_LLAMA4_CONTAINER_FAMILY,
125
+ InferenceContainerTypeFamily.AQUA_VLLM_V1_CONTAINER_FAMILY,
126
+ InferenceContainerTypeFamily.AQUA_VLLM_CONTAINER_FAMILY,
127
+ ],
122
128
  }
ads/aqua/common/utils.py CHANGED
@@ -832,7 +832,9 @@ def get_params_dict(params: Union[str, List[str]]) -> dict:
832
832
  """
833
833
  params_list = get_params_list(params) if isinstance(params, str) else params
834
834
  return {
835
- split_result[0]: split_result[1] if len(split_result) > 1 else UNKNOWN
835
+ split_result[0]: " ".join(split_result[1:])
836
+ if len(split_result) > 1
837
+ else UNKNOWN
836
838
  for split_result in (x.split() for x in params_list)
837
839
  }
838
840
 
@@ -881,7 +883,9 @@ def build_params_string(params: dict) -> str:
881
883
  A params string.
882
884
  """
883
885
  return (
884
- " ".join(f"{name} {value}" for name, value in params.items()).strip()
886
+ " ".join(
887
+ f"{name} {value}" if value else f"{name}" for name, value in params.items()
888
+ ).strip()
885
889
  if params
886
890
  else UNKNOWN
887
891
  )
@@ -1158,9 +1162,11 @@ def validate_cmd_var(cmd_var: List[str], overrides: List[str]) -> List[str]:
1158
1162
 
1159
1163
 
1160
1164
  def build_pydantic_error_message(ex: ValidationError):
1161
- """Added to handle error messages from pydantic model validator.
1165
+ """
1166
+ Added to handle error messages from pydantic model validator.
1162
1167
  Combine both loc and msg for errors where loc (field) is present in error details, else only build error
1163
- message using msg field."""
1168
+ message using msg field.
1169
+ """
1164
1170
 
1165
1171
  return {
1166
1172
  ".".join(map(str, e["loc"])): e["msg"]
@@ -1185,67 +1191,71 @@ def is_pydantic_model(obj: object) -> bool:
1185
1191
 
1186
1192
  @cached(cache=TTLCache(maxsize=1, ttl=timedelta(minutes=5), timer=datetime.now))
1187
1193
  def load_gpu_shapes_index(
1188
- auth: Optional[Dict] = None,
1194
+ auth: Optional[Dict[str, Any]] = None,
1189
1195
  ) -> GPUShapesIndex:
1190
1196
  """
1191
- Loads the GPU shapes index from Object Storage or a local resource folder.
1197
+ Load the GPU shapes index, preferring the OS bucket copy over the local one.
1192
1198
 
1193
- The function first attempts to load the file from an Object Storage bucket using fsspec.
1194
- If the loading fails (due to connection issues, missing file, etc.), it falls back to
1195
- loading the index from a local file.
1199
+ Attempts to read `gpu_shapes_index.json` from OCI Object Storage first;
1200
+ if that succeeds, those entries will override the local defaults.
1196
1201
 
1197
1202
  Parameters
1198
1203
  ----------
1199
- auth: (Dict, optional). Defaults to None.
1200
- The default authentication is set using `ads.set_auth` API. If you need to override the
1201
- default, use the `ads.common.auth.api_keys` or `ads.common.auth.resource_principal` to create appropriate
1202
- authentication signer and kwargs required to instantiate IdentityClient object.
1204
+ auth
1205
+ Optional auth dict (as returned by `ads.common.auth.default_signer()`)
1206
+ to pass through to `fsspec.open()`.
1203
1207
 
1204
1208
  Returns
1205
1209
  -------
1206
- GPUShapesIndex: The parsed GPU shapes index.
1210
+ GPUShapesIndex
1211
+ Merged index where any shape present remotely supersedes the local entry.
1207
1212
 
1208
1213
  Raises
1209
1214
  ------
1210
- FileNotFoundError: If the GPU shapes index cannot be found in either Object Storage or locally.
1211
- json.JSONDecodeError: If the JSON is malformed.
1215
+ json.JSONDecodeError
1216
+ If any of the JSON is malformed.
1212
1217
  """
1213
1218
  file_name = "gpu_shapes_index.json"
1214
- data: Dict[str, Any] = {}
1215
1219
 
1216
- # Check if the CONDA_BUCKET_NS environment variable is set.
1220
+ # Try remote load
1221
+ remote_data: Dict[str, Any] = {}
1217
1222
  if CONDA_BUCKET_NS:
1218
1223
  try:
1219
1224
  auth = auth or authutil.default_signer()
1220
- # Construct the object storage path. Adjust bucket name and path as needed.
1221
1225
  storage_path = (
1222
1226
  f"oci://{CONDA_BUCKET_NAME}@{CONDA_BUCKET_NS}/service_pack/{file_name}"
1223
1227
  )
1224
- logger.debug("Loading GPU shapes index from Object Storage")
1225
- with fsspec.open(storage_path, mode="r", **auth) as file_obj:
1226
- data = json.load(file_obj)
1227
- logger.debug("Successfully loaded GPU shapes index.")
1228
- except Exception as ex:
1229
1228
  logger.debug(
1230
- f"Failed to load GPU shapes index from Object Storage. Details: {ex}"
1231
- )
1232
-
1233
- # If loading from Object Storage failed, load from the local resource folder.
1234
- if not data:
1235
- try:
1236
- local_path = os.path.join(
1237
- os.path.dirname(__file__), "../resources", file_name
1229
+ "Loading GPU shapes index from Object Storage: %s", storage_path
1238
1230
  )
1239
- logger.debug(f"Loading GPU shapes index from {local_path}.")
1240
- with open(local_path) as file_obj:
1241
- data = json.load(file_obj)
1242
- logger.debug("Successfully loaded GPU shapes index.")
1243
- except Exception as e:
1231
+ with fsspec.open(storage_path, mode="r", **auth) as f:
1232
+ remote_data = json.load(f)
1244
1233
  logger.debug(
1245
- f"Failed to load GPU shapes index from {local_path}. Details: {e}"
1234
+ "Loaded %d shapes from Object Storage",
1235
+ len(remote_data.get("shapes", {})),
1246
1236
  )
1237
+ except Exception as ex:
1238
+ logger.debug("Remote load failed (%s); falling back to local", ex)
1239
+
1240
+ # Load local copy
1241
+ local_data: Dict[str, Any] = {}
1242
+ local_path = os.path.join(os.path.dirname(__file__), "../resources", file_name)
1243
+ try:
1244
+ logger.debug("Loading GPU shapes index from local file: %s", local_path)
1245
+ with open(local_path) as f:
1246
+ local_data = json.load(f)
1247
+ logger.debug(
1248
+ "Loaded %d shapes from local file", len(local_data.get("shapes", {}))
1249
+ )
1250
+ except Exception as ex:
1251
+ logger.debug("Local load GPU shapes index failed (%s)", ex)
1252
+
1253
+ # Merge: remote shapes override local
1254
+ local_shapes = local_data.get("shapes", {})
1255
+ remote_shapes = remote_data.get("shapes", {})
1256
+ merged_shapes = {**local_shapes, **remote_shapes}
1247
1257
 
1248
- return GPUShapesIndex(**data)
1258
+ return GPUShapesIndex(shapes=merged_shapes)
1249
1259
 
1250
1260
 
1251
1261
  def get_preferred_compatible_family(selected_families: set[str]) -> str:
@@ -7,6 +7,7 @@ from typing import Dict, List, Optional
7
7
  from oci.data_science.models import ContainerSummary
8
8
  from pydantic import Field
9
9
 
10
+ from ads.aqua import logger
10
11
  from ads.aqua.config.utils.serializer import Serializable
11
12
  from ads.aqua.constants import (
12
13
  SERVICE_MANAGED_CONTAINER_URI_SCHEME,
@@ -168,50 +169,47 @@ class AquaContainerConfig(Serializable):
168
169
  container_type = container.family_name
169
170
  usages = [x.upper() for x in container.usages]
170
171
  if "INFERENCE" in usages or "MULTI_MODEL" in usages:
172
+ # Extract additional configurations
173
+ additional_configurations = {}
174
+ try:
175
+ additional_configurations = (
176
+ container.workload_configuration_details_list[
177
+ 0
178
+ ].additional_configurations
179
+ )
180
+ except (AttributeError, IndexError) as ex:
181
+ logger.debug(
182
+ "Failed to extract `additional_configurations` for container '%s': %s",
183
+ getattr(container, "container_name", "<unknown>"),
184
+ ex,
185
+ )
186
+
171
187
  container_item.platforms.append(
172
- container.workload_configuration_details_list[
173
- 0
174
- ].additional_configurations.get("platforms")
188
+ additional_configurations.get("platforms")
175
189
  )
176
190
  container_item.model_formats.append(
177
- container.workload_configuration_details_list[
178
- 0
179
- ].additional_configurations.get("modelFormats")
191
+ additional_configurations.get("modelFormats")
180
192
  )
193
+
194
+ # Parse environment variables from `additional_configurations`.
195
+ # Only keys present in the configuration will be added to the result.
196
+ config_keys = {
197
+ "MODEL_DEPLOY_PREDICT_ENDPOINT": UNKNOWN,
198
+ "MODEL_DEPLOY_HEALTH_ENDPOINT": UNKNOWN,
199
+ "MODEL_DEPLOY_ENABLE_STREAMING": UNKNOWN,
200
+ "PORT": UNKNOWN,
201
+ "HEALTH_CHECK_PORT": UNKNOWN,
202
+ "VLLM_USE_V1": UNKNOWN,
203
+ }
204
+
181
205
  env_vars = [
182
- {
183
- "MODEL_DEPLOY_PREDICT_ENDPOINT": container.workload_configuration_details_list[
184
- 0
185
- ].additional_configurations.get(
186
- "MODEL_DEPLOY_PREDICT_ENDPOINT", UNKNOWN
187
- )
188
- },
189
- {
190
- "MODEL_DEPLOY_HEALTH_ENDPOINT": container.workload_configuration_details_list[
191
- 0
192
- ].additional_configurations.get(
193
- "MODEL_DEPLOY_HEALTH_ENDPOINT", UNKNOWN
194
- )
195
- },
196
- {
197
- "MODEL_DEPLOY_ENABLE_STREAMING": container.workload_configuration_details_list[
198
- 0
199
- ].additional_configurations.get(
200
- "MODEL_DEPLOY_ENABLE_STREAMING", UNKNOWN
201
- )
202
- },
203
- {
204
- "PORT": container.workload_configuration_details_list[
205
- 0
206
- ].additional_configurations.get("PORT", "")
207
- },
208
- {
209
- "HEALTH_CHECK_PORT": container.workload_configuration_details_list[
210
- 0
211
- ].additional_configurations.get("HEALTH_CHECK_PORT", UNKNOWN),
212
- },
206
+ {key: additional_configurations.get(key, default)}
207
+ for key, default in config_keys.items()
208
+ if key in additional_configurations
213
209
  ]
214
- container_spec = AquaContainerConfigSpec(
210
+
211
+ # Build container spec
212
+ container_item.spec = AquaContainerConfigSpec(
215
213
  cli_param=container.workload_configuration_details_list[0].cmd,
216
214
  server_port=str(
217
215
  container.workload_configuration_details_list[0].server_port
@@ -236,13 +234,14 @@ class AquaContainerConfig(Serializable):
236
234
  )
237
235
  ),
238
236
  )
239
- container_item.spec = container_spec
237
+
240
238
  if "INFERENCE" in usages or "MULTI_MODEL" in usages:
241
239
  inference_items[container_type] = container_item
242
240
  if "FINE_TUNE" in usages:
243
241
  finetune_items[container_type] = container_item
244
242
  if "EVALUATION" in usages:
245
243
  evaluate_items[container_type] = container_item
244
+
246
245
  return cls(
247
246
  inference=inference_items, finetune=finetune_items, evaluate=evaluate_items
248
247
  )
ads/aqua/constants.py CHANGED
@@ -43,6 +43,8 @@ HF_METADATA_FOLDER = ".cache/"
43
43
  HF_LOGIN_DEFAULT_TIMEOUT = 2
44
44
  MODEL_NAME_DELIMITER = ";"
45
45
  AQUA_TROUBLESHOOTING_LINK = "https://github.com/oracle-samples/oci-data-science-ai-samples/blob/main/ai-quick-actions/troubleshooting-tips.md"
46
+ MODEL_FILE_DESCRIPTION_VERSION = "1.0"
47
+ MODEL_FILE_DESCRIPTION_TYPE = "modelOSSReferenceDescription"
46
48
 
47
49
  TRAINING_METRICS_FINAL = "training_metrics_final"
48
50
  VALIDATION_METRICS_FINAL = "validation_metrics_final"
@@ -15,7 +15,7 @@ from ads.aqua.extension.errors import Errors
15
15
  from ads.aqua.extension.utils import validate_function_parameters
16
16
  from ads.aqua.model.entities import ImportModelDetails
17
17
  from ads.aqua.ui import AquaUIApp
18
- from ads.config import COMPARTMENT_OCID
18
+ from ads.config import COMPARTMENT_OCID, IS_BYOR_ENABLED
19
19
 
20
20
 
21
21
  @dataclass
@@ -82,6 +82,10 @@ class AquaUIHandler(AquaAPIhandler):
82
82
  return self.is_bucket_versioned()
83
83
  elif paths.startswith("aqua/containers"):
84
84
  return self.list_containers()
85
+ elif paths.startswith("aqua/capacityreservations/enabled"):
86
+ return self.is_capacity_reservations_enabled()
87
+ elif paths.startswith("aqua/capacityreservations"):
88
+ return self.list_capacity_reservations()
85
89
  else:
86
90
  raise HTTPError(400, f"The request {self.request.path} is invalid.")
87
91
 
@@ -103,6 +107,19 @@ class AquaUIHandler(AquaAPIhandler):
103
107
  AquaUIApp().list_log_groups(compartment_id=compartment_id, **kwargs)
104
108
  )
105
109
 
110
+ def is_capacity_reservations_enabled(self):
111
+ """Checks if the tenant is whitelisted for BYOR (Bring your own reservation) feature."""
112
+ return self.finish({"status": str(IS_BYOR_ENABLED).strip().lower() == "true"})
113
+
114
+ def list_capacity_reservations(self, **kwargs):
115
+ """Lists users compute reservations in a specified compartment."""
116
+ compartment_id = self.get_argument("compartment_id", default=COMPARTMENT_OCID)
117
+ return self.finish(
118
+ AquaUIApp().list_capacity_reservations(
119
+ compartment_id=compartment_id, **kwargs
120
+ )
121
+ )
122
+
106
123
  def list_logs(self, log_group_id: str, **kwargs):
107
124
  """Lists the specified log group's log objects."""
108
125
  return self.finish(AquaUIApp().list_logs(log_group_id=log_group_id, **kwargs))
@@ -279,4 +296,5 @@ __handlers__ = [
279
296
  ("bucket/versioning/?([^/]*)", AquaUIHandler),
280
297
  ("containers/?([^/]*)", AquaUIHandler),
281
298
  ("cli/?([^/]*)", AquaCLIHandler),
299
+ ("capacityreservations/?([^/]*)", AquaUIHandler),
282
300
  ]
@@ -58,6 +58,7 @@ from ads.jobs.ads_job import Job
58
58
  from ads.jobs.builders.infrastructure.dsc_job import DataScienceJob
59
59
  from ads.jobs.builders.runtimes.base import Runtime
60
60
  from ads.jobs.builders.runtimes.container_runtime import ContainerRuntime
61
+ from ads.model.common.utils import MetadataArtifactPathType
61
62
  from ads.model.model_metadata import (
62
63
  MetadataTaxonomyKeys,
63
64
  ModelCustomMetadata,
@@ -315,6 +316,23 @@ class AquaFineTuningApp(AquaApp):
315
316
  model_by_reference=True,
316
317
  defined_tags=create_fine_tuning_details.defined_tags,
317
318
  )
319
+ defined_metadata_dict = {}
320
+ defined_metadata_list_source = source.defined_metadata_list._to_oci_metadata()
321
+ for defined_metadata in defined_metadata_list_source:
322
+ if (
323
+ defined_metadata.has_artifact
324
+ and defined_metadata.key.lower()
325
+ != AquaModelMetadataKeys.FINE_TUNING_CONFIGURATION.lower()
326
+ ):
327
+ content = self.ds_client.get_model_defined_metadatum_artifact_content(
328
+ source.id, defined_metadata.key
329
+ ).data.content
330
+ defined_metadata_dict[defined_metadata.key] = content
331
+
332
+ for key, value in defined_metadata_dict.items():
333
+ ft_model.create_defined_metadata_artifact(
334
+ key, value, MetadataArtifactPathType.CONTENT
335
+ )
318
336
 
319
337
  ft_job_freeform_tags = {
320
338
  Tags.AQUA_TAG: UNKNOWN,
@@ -15,12 +15,19 @@ from typing import List, Optional
15
15
 
16
16
  import oci
17
17
  from huggingface_hub import hf_api
18
- from pydantic import BaseModel
18
+ from pydantic import BaseModel, Field
19
+ from pydantic.alias_generators import to_camel
19
20
 
20
21
  from ads.aqua import logger
21
22
  from ads.aqua.app import CLIBuilderMixin
22
23
  from ads.aqua.common import utils
23
- from ads.aqua.constants import LIFECYCLE_DETAILS_MISSING_JOBRUN, UNKNOWN_VALUE
24
+ from ads.aqua.config.utils.serializer import Serializable
25
+ from ads.aqua.constants import (
26
+ LIFECYCLE_DETAILS_MISSING_JOBRUN,
27
+ MODEL_FILE_DESCRIPTION_TYPE,
28
+ MODEL_FILE_DESCRIPTION_VERSION,
29
+ UNKNOWN_VALUE,
30
+ )
24
31
  from ads.aqua.data import AquaResourceIdentifier
25
32
  from ads.aqua.model.enums import FineTuningDefinedMetadata
26
33
  from ads.aqua.training.exceptions import exit_code_dict
@@ -304,3 +311,75 @@ class ImportModelDetails(CLIBuilderMixin):
304
311
 
305
312
  def __post_init__(self):
306
313
  self._command = "model register"
314
+
315
+
316
+ class ModelFileInfo(Serializable):
317
+ """Describes the file information of this model.
318
+
319
+ Attributes:
320
+ name (str): The name of the model artifact file.
321
+ version (str): The version of the model artifact file.
322
+ size_in_bytes (int): The size of the model artifact file in bytes.
323
+ """
324
+
325
+ name: str = Field(..., description="The name of model artifact file.")
326
+ version: str = Field(..., description="The version of model artifact file.")
327
+ size_in_bytes: int = Field(
328
+ ..., description="The size of model artifact file in bytes."
329
+ )
330
+
331
+ class Config:
332
+ alias_generator = to_camel
333
+ extra = "allow"
334
+
335
+
336
+ class ModelArtifactInfo(Serializable):
337
+ """Describes the artifact information of this model.
338
+
339
+ Attributes:
340
+ namespace (str): The namespace of the model artifact location.
341
+ bucket_name (str): The bucket name of model artifact location.
342
+ prefix (str): The prefix of model artifact location.
343
+ objects: (List[ModelFileInfo]): A list of model artifact objects.
344
+ """
345
+
346
+ namespace: str = Field(
347
+ ..., description="The name space of model artifact location."
348
+ )
349
+ bucket_name: str = Field(
350
+ ..., description="The bucket name of model artifact location."
351
+ )
352
+ prefix: str = Field(..., description="The prefix of model artifact location.")
353
+ objects: List[ModelFileInfo] = Field(
354
+ ..., description="List of model artifact objects."
355
+ )
356
+
357
+ class Config:
358
+ alias_generator = to_camel
359
+ extra = "allow"
360
+
361
+
362
+ class ModelFileDescription(Serializable):
363
+ """Describes the model file description.
364
+
365
+ Attributes:
366
+ version (str): The version of the model file description. Defaults to `1.0`.
367
+ type (str): The type of model file description. Defaults to `modelOSSReferenceDescription`.
368
+ models List[ModelArtifactInfo]: A list of model artifact information.
369
+ """
370
+
371
+ version: str = Field(
372
+ default=MODEL_FILE_DESCRIPTION_VERSION,
373
+ description="The version of model file description.",
374
+ )
375
+ type: str = Field(
376
+ default=MODEL_FILE_DESCRIPTION_TYPE,
377
+ description="The type of model file description.",
378
+ )
379
+ models: List[ModelArtifactInfo] = Field(
380
+ ..., description="List of model artifact information."
381
+ )
382
+
383
+ class Config:
384
+ alias_generator = to_camel
385
+ extra = "allow"
ads/aqua/model/enums.py CHANGED
@@ -26,5 +26,7 @@ class FineTuningCustomMetadata(ExtendedEnum):
26
26
 
27
27
 
28
28
  class MultiModelSupportedTaskType(ExtendedEnum):
29
- TEXT_GENERATION = "text-generation"
30
- TEXT_GENERATION_ALT = "text_generation"
29
+ TEXT_GENERATION = "text_generation"
30
+ IMAGE_TEXT_TO_TEXT = "image_text_to_text"
31
+ CODE_SYNTHESIS = "code_synthesis"
32
+ EMBEDDING = "text_embedding"
ads/aqua/model/model.py CHANGED
@@ -4,6 +4,7 @@
4
4
  import json
5
5
  import os
6
6
  import pathlib
7
+ import re
7
8
  from datetime import datetime, timedelta
8
9
  from threading import Lock
9
10
  from typing import Any, Dict, List, Optional, Set, Union
@@ -78,9 +79,14 @@ from ads.aqua.model.entities import (
78
79
  AquaModelReadme,
79
80
  AquaModelSummary,
80
81
  ImportModelDetails,
82
+ ModelFileDescription,
81
83
  ModelValidationResult,
82
84
  )
83
85
  from ads.aqua.model.enums import MultiModelSupportedTaskType
86
+ from ads.aqua.model.utils import (
87
+ extract_base_model_from_ft,
88
+ extract_fine_tune_artifacts_path,
89
+ )
84
90
  from ads.common.auth import default_signer
85
91
  from ads.common.oci_resource import SEARCH_TYPE, OCIResource
86
92
  from ads.common.utils import (
@@ -184,8 +190,12 @@ class AquaModelApp(AquaApp):
184
190
  target_project = project_id or PROJECT_OCID
185
191
  target_compartment = compartment_id or COMPARTMENT_OCID
186
192
 
187
- # Skip model copying if it is registered model
188
- if service_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None) is not None:
193
+ # Skip model copying if it is registered model or fine-tuned model
194
+ if (
195
+ service_model.freeform_tags.get(Tags.BASE_MODEL_CUSTOM, None) is not None
196
+ or service_model.freeform_tags.get(Tags.AQUA_FINE_TUNED_MODEL_TAG)
197
+ is not None
198
+ ):
189
199
  logger.info(
190
200
  f"Aqua Model {model_id} already exists in the user's compartment."
191
201
  "Skipped copying."
@@ -266,8 +276,8 @@ class AquaModelApp(AquaApp):
266
276
  "Model list cannot be empty. Please provide at least one model for deployment."
267
277
  )
268
278
 
269
- artifact_list = []
270
279
  display_name_list = []
280
+ model_file_description_list: List[ModelFileDescription] = []
271
281
  model_custom_metadata = ModelCustomMetadata()
272
282
 
273
283
  service_inference_containers = (
@@ -294,6 +304,7 @@ class AquaModelApp(AquaApp):
294
304
  for model in models:
295
305
  source_model = DataScienceModel.from_id(model.model_id)
296
306
  display_name = source_model.display_name
307
+ model_file_description = source_model.model_file_description
297
308
  # Update model name in user's input model
298
309
  model.model_name = model.model_name or display_name
299
310
 
@@ -304,19 +315,27 @@ class AquaModelApp(AquaApp):
304
315
  # "Currently only service models are supported for multi model deployment."
305
316
  # )
306
317
 
307
- if (
308
- source_model.freeform_tags.get(Tags.TASK, UNKNOWN).lower()
309
- not in MultiModelSupportedTaskType
310
- ):
311
- raise AquaValueError(
312
- f"Invalid or missing {Tags.TASK} tag for selected model {display_name}. "
313
- f"Currently only `{MultiModelSupportedTaskType.values()}` models are supported for multi model deployment."
318
+ # check if model is a fine-tuned model and if so, add the fine tuned weights path to the fine_tune_weights_location pydantic field
319
+ is_fine_tuned_model = (
320
+ Tags.AQUA_FINE_TUNED_MODEL_TAG in source_model.freeform_tags
321
+ )
322
+
323
+ if is_fine_tuned_model:
324
+ model.model_id, model.model_name = extract_base_model_from_ft(
325
+ source_model
326
+ )
327
+ model_artifact_path, model.fine_tune_weights_location = (
328
+ extract_fine_tune_artifacts_path(source_model)
314
329
  )
315
330
 
331
+ else:
332
+ # Retrieve model artifact for base models
333
+ model_artifact_path = source_model.artifact
334
+
316
335
  display_name_list.append(display_name)
317
336
 
318
- # Retrieve model artifact
319
- model_artifact_path = source_model.artifact
337
+ self._extract_model_task(model, source_model)
338
+
320
339
  if not model_artifact_path:
321
340
  raise AquaValueError(
322
341
  f"Model '{display_name}' (ID: {model.model_id}) has no artifacts. "
@@ -326,7 +345,15 @@ class AquaModelApp(AquaApp):
326
345
  # Update model artifact location in user's input model
327
346
  model.artifact_location = model_artifact_path
328
347
 
329
- artifact_list.append(model_artifact_path)
348
+ if not model_file_description:
349
+ raise AquaValueError(
350
+ f"Model '{display_name}' (ID: {model.model_id}) has no file description. "
351
+ "Please register the model first."
352
+ )
353
+
354
+ model_file_description_list.append(
355
+ ModelFileDescription(**model_file_description)
356
+ )
330
357
 
331
358
  # Validate deployment container consistency
332
359
  deployment_container = source_model.custom_metadata_list.get(
@@ -359,7 +386,8 @@ class AquaModelApp(AquaApp):
359
386
  raise AquaValueError(
360
387
  "The selected models are associated with different container families: "
361
388
  f"{list(selected_models_deployment_containers)}."
362
- "For multi-model deployment, all models in the group must share the same container family."
389
+ "For multi-model deployment, all models in the group must belong to the same container "
390
+ "family or to compatible container families."
363
391
  )
364
392
  else:
365
393
  deployment_container = selected_models_deployment_containers.pop()
@@ -404,9 +432,16 @@ class AquaModelApp(AquaApp):
404
432
  .with_custom_metadata_list(model_custom_metadata)
405
433
  )
406
434
 
407
- # Attach artifacts
408
- for artifact in artifact_list:
409
- custom_model.add_artifact(uri=artifact)
435
+ # Update multi model file description to attach artifacts
436
+ custom_model.with_model_file_description(
437
+ json_dict=ModelFileDescription(
438
+ models=[
439
+ models
440
+ for model_file_description in model_file_description_list
441
+ for models in model_file_description.models
442
+ ]
443
+ ).model_dump(by_alias=True)
444
+ )
410
445
 
411
446
  # Finalize creation
412
447
  custom_model.create(model_by_reference=True)
@@ -703,6 +738,26 @@ class AquaModelApp(AquaApp):
703
738
  else:
704
739
  raise AquaRuntimeError("Only registered unverified models can be edited.")
705
740
 
741
+ def _extract_model_task(
742
+ self,
743
+ model: AquaMultiModelRef,
744
+ source_model: DataScienceModel,
745
+ ) -> None:
746
+ """In a Multi Model Deployment, will set model_task parameter in AquaMultiModelRef from freeform tags or user"""
747
+ # user does not supply model task, we extract from model metadata
748
+ if not model.model_task:
749
+ model.model_task = source_model.freeform_tags.get(Tags.TASK, UNKNOWN)
750
+
751
+ task_tag = re.sub(r"-", "_", model.model_task).lower()
752
+ # re-visit logic when more model task types are supported
753
+ if task_tag in MultiModelSupportedTaskType:
754
+ model.model_task = task_tag
755
+ else:
756
+ raise AquaValueError(
757
+ f"Invalid or missing {task_tag} tag for selected model {source_model.display_name}. "
758
+ f"Currently only `{MultiModelSupportedTaskType.values()}` models are supported for multi model deployment."
759
+ )
760
+
706
761
  def _fetch_metric_from_metadata(
707
762
  self,
708
763
  custom_metadata_list: ModelCustomMetadata,
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python
2
+ # Copyright (c) 2025 Oracle and/or its affiliates.
3
+ # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
+ """AQUA model utils"""
5
+
6
+ from typing import Dict, Optional, Tuple
7
+
8
+ from ads.aqua.common.entities import AquaMultiModelRef
9
+ from ads.aqua.common.errors import AquaValueError
10
+ from ads.aqua.common.utils import get_model_by_reference_paths
11
+ from ads.aqua.finetuning.constants import FineTuneCustomMetadata
12
+ from ads.common.object_storage_details import ObjectStorageDetails
13
+ from ads.model.datascience_model import DataScienceModel
14
+
15
+
16
+ def extract_base_model_from_ft(aqua_model: DataScienceModel) -> Tuple[str, str]:
17
+ """Extracts the model_name and base model OCID (config_source_id) OCID for a fine-tuned model"""
18
+
19
+ config_source_id = aqua_model.custom_metadata_list.get(
20
+ FineTuneCustomMetadata.FINE_TUNE_SOURCE
21
+ ).value
22
+ model_name = aqua_model.custom_metadata_list.get(
23
+ FineTuneCustomMetadata.FINE_TUNE_SOURCE_NAME
24
+ ).value
25
+
26
+ if not config_source_id or not model_name:
27
+ raise AquaValueError(
28
+ f"Either {FineTuneCustomMetadata.FINE_TUNE_SOURCE} or {FineTuneCustomMetadata.FINE_TUNE_SOURCE_NAME} is missing "
29
+ f"from custom metadata for the model {config_source_id}"
30
+ )
31
+
32
+ return config_source_id, model_name
33
+
34
+
35
+ def extract_fine_tune_artifacts_path(aqua_model: DataScienceModel) -> Tuple[str, str]:
36
+ """Extracts the fine tuning source (fine_tune_output_path) and base model path from the DataScienceModel Object"""
37
+
38
+ base_model_path, fine_tune_output_path = get_model_by_reference_paths(
39
+ aqua_model.model_file_description
40
+ )
41
+
42
+ if not fine_tune_output_path or not ObjectStorageDetails.is_oci_path(
43
+ fine_tune_output_path
44
+ ):
45
+ raise AquaValueError(
46
+ "Fine tuned output path is not available in the model artifact."
47
+ )
48
+
49
+ os_path = ObjectStorageDetails.from_path(fine_tune_output_path)
50
+ fine_tune_output_path = os_path.filepath.rstrip("/")
51
+
52
+ return base_model_path, fine_tune_output_path
@@ -25,7 +25,6 @@ from ads.aqua.common.utils import (
25
25
  build_pydantic_error_message,
26
26
  get_combined_params,
27
27
  get_container_params_type,
28
- get_model_by_reference_paths,
29
28
  get_ocid_substring,
30
29
  get_params_dict,
31
30
  get_params_list,
@@ -46,9 +45,12 @@ from ads.aqua.constants import (
46
45
  UNKNOWN_DICT,
47
46
  )
48
47
  from ads.aqua.data import AquaResourceIdentifier
49
- from ads.aqua.finetuning.finetuning import FineTuneCustomMetadata
50
48
  from ads.aqua.model import AquaModelApp
51
49
  from ads.aqua.model.constants import AquaModelMetadataKeys, ModelCustomMetadataFields
50
+ from ads.aqua.model.utils import (
51
+ extract_base_model_from_ft,
52
+ extract_fine_tune_artifacts_path,
53
+ )
52
54
  from ads.aqua.modeldeployment.entities import (
53
55
  AquaDeployment,
54
56
  AquaDeploymentConfig,
@@ -178,9 +180,7 @@ class AquaDeploymentApp(AquaApp):
178
180
  # validate instance shape availability in compartment
179
181
  available_shapes = [
180
182
  shape.name.lower()
181
- for shape in self.list_shapes(
182
- compartment_id=compartment_id
183
- )
183
+ for shape in self.list_shapes(compartment_id=compartment_id)
184
184
  ]
185
185
 
186
186
  if create_deployment_details.instance_shape.lower() not in available_shapes:
@@ -213,6 +213,7 @@ class AquaDeploymentApp(AquaApp):
213
213
  )
214
214
  else:
215
215
  model_ids = [model.model_id for model in create_deployment_details.models]
216
+
216
217
  try:
217
218
  model_config_summary = self.get_multimodel_deployment_config(
218
219
  model_ids=model_ids, compartment_id=compartment_id
@@ -345,22 +346,6 @@ class AquaDeploymentApp(AquaApp):
345
346
  config_source_id = create_deployment_details.model_id
346
347
  model_name = aqua_model.display_name
347
348
 
348
- is_fine_tuned_model = Tags.AQUA_FINE_TUNED_MODEL_TAG in aqua_model.freeform_tags
349
-
350
- if is_fine_tuned_model:
351
- try:
352
- config_source_id = aqua_model.custom_metadata_list.get(
353
- FineTuneCustomMetadata.FINE_TUNE_SOURCE
354
- ).value
355
- model_name = aqua_model.custom_metadata_list.get(
356
- FineTuneCustomMetadata.FINE_TUNE_SOURCE_NAME
357
- ).value
358
- except ValueError as err:
359
- raise AquaValueError(
360
- f"Either {FineTuneCustomMetadata.FINE_TUNE_SOURCE} or {FineTuneCustomMetadata.FINE_TUNE_SOURCE_NAME} is missing "
361
- f"from custom metadata for the model {config_source_id}"
362
- ) from err
363
-
364
349
  # set up env and cmd var
365
350
  env_var = create_deployment_details.env_var or {}
366
351
  cmd_var = create_deployment_details.cmd_var or []
@@ -380,19 +365,11 @@ class AquaDeploymentApp(AquaApp):
380
365
 
381
366
  env_var.update({"BASE_MODEL": f"{model_path_prefix}"})
382
367
 
383
- if is_fine_tuned_model:
384
- _, fine_tune_output_path = get_model_by_reference_paths(
385
- aqua_model.model_file_description
386
- )
387
-
388
- if not fine_tune_output_path:
389
- raise AquaValueError(
390
- "Fine tuned output path is not available in the model artifact."
391
- )
392
-
393
- os_path = ObjectStorageDetails.from_path(fine_tune_output_path)
394
- fine_tune_output_path = os_path.filepath.rstrip("/")
368
+ is_fine_tuned_model = Tags.AQUA_FINE_TUNED_MODEL_TAG in aqua_model.freeform_tags
395
369
 
370
+ if is_fine_tuned_model:
371
+ config_source_id, model_name = extract_base_model_from_ft(aqua_model)
372
+ _, fine_tune_output_path = extract_fine_tune_artifacts_path(aqua_model)
396
373
  env_var.update({"FT_MODEL": f"{fine_tune_output_path}"})
397
374
 
398
375
  container_type_key = self._get_container_type_key(
@@ -645,7 +622,15 @@ class AquaDeploymentApp(AquaApp):
645
622
  os_path = ObjectStorageDetails.from_path(artifact_path_prefix)
646
623
  artifact_path_prefix = os_path.filepath.rstrip("/")
647
624
 
648
- model_config.append({"params": params, "model_path": artifact_path_prefix})
625
+ # override by-default completion/ chat endpoint with other endpoint (embedding)
626
+ config_data = {"params": params, "model_path": artifact_path_prefix}
627
+ if model.model_task:
628
+ config_data["model_task"] = model.model_task
629
+
630
+ if model.fine_tune_weights_location:
631
+ config_data["fine_tune_weights_location"] = model.fine_tune_weights_location
632
+
633
+ model_config.append(config_data)
649
634
  model_name_list.append(model.model_name)
650
635
 
651
636
  env_var.update({AQUA_MULTI_MODEL_CONFIG: json.dumps({"models": model_config})})
@@ -803,6 +788,9 @@ class AquaDeploymentApp(AquaApp):
803
788
  # we arbitrarily choose last 8 characters of OCID to identify MD in telemetry
804
789
  telemetry_kwargs = {"ocid": get_ocid_substring(deployment_id, key_len=8)}
805
790
 
791
+ if Tags.BASE_MODEL_CUSTOM in tags:
792
+ telemetry_kwargs[ "custom_base_model"] = True
793
+
806
794
  # tracks unique deployments that were created in the user compartment
807
795
  self.telemetry.record_event_async(
808
796
  category=f"aqua/{model_type}/deployment",
@@ -1321,4 +1309,4 @@ class AquaDeploymentApp(AquaApp):
1321
1309
  or gpu_specs.shapes.get(oci_shape.name.upper()),
1322
1310
  )
1323
1311
  for oci_shape in oci_shapes
1324
- ]
1312
+ ]
ads/aqua/ui.py CHANGED
@@ -90,6 +90,26 @@ class AquaUIApp(AquaApp):
90
90
  res = self.logging_client.list_logs(log_group_id=log_group_id, **kwargs).data
91
91
  return sanitize_response(oci_client=self.logging_client, response=res)
92
92
 
93
+ @telemetry(entry_point="plugin=ui&action=list_capacity_reservations", name="aqua")
94
+ def list_capacity_reservations(self, **kwargs) -> list:
95
+ """
96
+ Lists users compute reservations in a specified compartment
97
+
98
+ Returns
99
+ -------
100
+ json representation of `oci.core.models.ComputeCapacityReservationSummary`.
101
+
102
+ """
103
+ compartment_id = kwargs.pop("compartment_id", COMPARTMENT_OCID)
104
+ logger.info(f"Loading Capacity reservations from compartment: {compartment_id}")
105
+
106
+ reservations = self.compute_client.list_compute_capacity_reservations(
107
+ compartment_id=compartment_id, **kwargs
108
+ )
109
+ return sanitize_response(
110
+ oci_client=self.compute_client, response=reservations.data
111
+ )
112
+
93
113
  @telemetry(entry_point="plugin=ui&action=list_compartments", name="aqua")
94
114
  def list_compartments(self) -> str:
95
115
  """Lists the compartments in a tenancy specified by TENANCY_OCID env variable. This is a pass through the OCI list_compartments
ads/common/oci_client.py CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python
2
- # -*- coding: utf-8; -*-
3
2
 
4
3
  # Copyright (c) 2021, 2024 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/
@@ -9,20 +8,20 @@ import logging
9
8
  import oci.artifacts
10
9
  from oci.ai_language import AIServiceLanguageClient
11
10
  from oci.artifacts import ArtifactsClient
11
+ from oci.core import ComputeClient, VirtualNetworkClient
12
12
  from oci.data_catalog import DataCatalogClient
13
13
  from oci.data_flow import DataFlowClient
14
14
  from oci.data_labeling_service import DataLabelingManagementClient
15
15
  from oci.data_labeling_service_dataplane import DataLabelingClient
16
16
  from oci.data_science import DataScienceClient
17
17
  from oci.identity import IdentityClient
18
+ from oci.limits import LimitsClient
19
+ from oci.logging import LoggingManagementClient
18
20
  from oci.marketplace import MarketplaceClient
19
21
  from oci.object_storage import ObjectStorageClient
20
22
  from oci.resource_search import ResourceSearchClient
21
23
  from oci.secrets import SecretsClient
22
24
  from oci.vault import VaultsClient
23
- from oci.logging import LoggingManagementClient
24
- from oci.core import VirtualNetworkClient
25
- from oci.limits import LimitsClient
26
25
 
27
26
  logger = logging.getLogger(__name__)
28
27
 
@@ -69,6 +68,7 @@ class OCIClientFactory:
69
68
  "secret": SecretsClient,
70
69
  "vault": VaultsClient,
71
70
  "identity": IdentityClient,
71
+ "compute": ComputeClient,
72
72
  "ai_language": AIServiceLanguageClient,
73
73
  "data_labeling_dp": DataLabelingClient,
74
74
  "data_labeling_cp": DataLabelingManagementClient,
@@ -114,6 +114,10 @@ class OCIClientFactory:
114
114
  def object_storage(self):
115
115
  return self.create_client("object_storage")
116
116
 
117
+ @property
118
+ def compute(self):
119
+ return self.create_client("compute")
120
+
117
121
  @property
118
122
  def identity(self):
119
123
  return self.create_client("identity")
ads/config.py CHANGED
@@ -14,6 +14,7 @@ OCI_ODSC_SERVICE_ENDPOINT = os.environ.get("OCI_ODSC_SERVICE_ENDPOINT")
14
14
  OCI_IDENTITY_SERVICE_ENDPOINT = os.environ.get("OCI_IDENTITY_SERVICE_ENDPOINT")
15
15
  NB_SESSION_COMPARTMENT_OCID = os.environ.get("NB_SESSION_COMPARTMENT_OCID")
16
16
  PROJECT_OCID = os.environ.get("PROJECT_OCID") or os.environ.get("PIPELINE_PROJECT_OCID")
17
+ IS_BYOR_ENABLED = os.environ.get("ALLOWLISTED_FOR_BYOR", False)
17
18
  NB_SESSION_OCID = os.environ.get("NB_SESSION_OCID")
18
19
  USER_OCID = os.environ.get("USER_OCID")
19
20
  OCI_RESOURCE_PRINCIPAL_VERSION = os.environ.get("OCI_RESOURCE_PRINCIPAL_VERSION")
@@ -573,9 +573,14 @@ class ForecastOperatorBaseModel(ABC):
573
573
  if self.spec.generate_explanations:
574
574
  try:
575
575
  if not self.formatted_global_explanation.empty:
576
+ # Round to 4 decimal places before writing
577
+ global_expl_rounded = self.formatted_global_explanation.copy()
578
+ global_expl_rounded = global_expl_rounded.apply(
579
+ lambda col: np.round(col, 4) if np.issubdtype(col.dtype, np.number) else col
580
+ )
576
581
  if self.spec.generate_explanation_files:
577
582
  write_data(
578
- data=self.formatted_global_explanation,
583
+ data=global_expl_rounded,
579
584
  filename=os.path.join(
580
585
  unique_output_dir, self.spec.global_explanation_filename
581
586
  ),
@@ -583,16 +588,21 @@ class ForecastOperatorBaseModel(ABC):
583
588
  storage_options=storage_options,
584
589
  index=True,
585
590
  )
586
- results.set_global_explanations(self.formatted_global_explanation)
591
+ results.set_global_explanations(global_expl_rounded)
587
592
  else:
588
593
  logger.warning(
589
594
  f"Attempted to generate global explanations for the {self.spec.global_explanation_filename} file, but an issue occured in formatting the explanations."
590
595
  )
591
596
 
592
597
  if not self.formatted_local_explanation.empty:
598
+ # Round to 4 decimal places before writing
599
+ local_expl_rounded = self.formatted_local_explanation.copy()
600
+ local_expl_rounded = local_expl_rounded.apply(
601
+ lambda col: np.round(col, 4) if np.issubdtype(col.dtype, np.number) else col
602
+ )
593
603
  if self.spec.generate_explanation_files:
594
604
  write_data(
595
- data=self.formatted_local_explanation,
605
+ data=local_expl_rounded,
596
606
  filename=os.path.join(
597
607
  unique_output_dir, self.spec.local_explanation_filename
598
608
  ),
@@ -600,7 +610,7 @@ class ForecastOperatorBaseModel(ABC):
600
610
  storage_options=storage_options,
601
611
  index=True,
602
612
  )
603
- results.set_local_explanations(self.formatted_local_explanation)
613
+ results.set_local_explanations(local_expl_rounded)
604
614
  else:
605
615
  logger.warning(
606
616
  f"Attempted to generate local explanations for the {self.spec.local_explanation_filename} file, but an issue occured in formatting the explanations."
@@ -304,7 +304,7 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
304
304
  # Global Expl
305
305
  g_expl = self.drop_horizon(expl_df).mean()
306
306
  g_expl.name = s_id
307
- global_expl.append(g_expl)
307
+ global_expl.append(np.abs(g_expl))
308
308
  self.global_explanation = pd.concat(global_expl, axis=1)
309
309
  self.formatted_global_explanation = (
310
310
  self.global_explanation / self.global_explanation.sum(axis=0) * 100
ads/telemetry/client.py CHANGED
@@ -8,6 +8,7 @@ import threading
8
8
  import traceback
9
9
  import urllib.parse
10
10
  from typing import Optional
11
+ import concurrent.futures
11
12
 
12
13
  import oci
13
14
 
@@ -16,7 +17,8 @@ from ads.config import DEBUG_TELEMETRY
16
17
  from .base import TelemetryBase
17
18
 
18
19
  logger = logging.getLogger(__name__)
19
-
20
+ THREAD_POOL_SIZE = 16
21
+ thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=THREAD_POOL_SIZE)
20
22
 
21
23
  class TelemetryClient(TelemetryBase):
22
24
  """Represents a telemetry python client providing functions to record an event.
@@ -102,7 +104,7 @@ class TelemetryClient(TelemetryBase):
102
104
 
103
105
  def record_event_async(
104
106
  self, category: str = None, action: str = None, detail: str = None, **kwargs
105
- ):
107
+ )-> None:
106
108
  """Send a head request to generate an event record.
107
109
 
108
110
  Parameters
@@ -117,9 +119,4 @@ class TelemetryClient(TelemetryBase):
117
119
  Thread
118
120
  A started thread to send a head request to generate an event record.
119
121
  """
120
- thread = threading.Thread(
121
- target=self.record_event, args=(category, action, detail), kwargs=kwargs
122
- )
123
- thread.daemon = True
124
- thread.start()
125
- return thread
122
+ thread_pool.submit(self.record_event, args=(category, action, detail), kwargs=kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oracle_ads
3
- Version: 2.13.7
3
+ Version: 2.13.9
4
4
  Summary: Oracle Accelerated Data Science SDK
5
5
  Keywords: Oracle Cloud Infrastructure,OCI,Machine Learning,ML,Artificial Intelligence,AI,Data Science,Cloud,Oracle
6
6
  Author: Oracle Data Science
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  License-File: LICENSE.txt
18
- Requires-Dist: PyYAML>=6
18
+ Requires-Dist: PyYAML>=6.0.1
19
19
  Requires-Dist: asteval>=0.9.25
20
20
  Requires-Dist: cerberus>=1.3.4
21
21
  Requires-Dist: cloudpickle>=1.6.0
@@ -99,13 +99,14 @@ Requires-Dist: ipython>=7.23.1, <8.0 ; extra == "notebook"
99
99
  Requires-Dist: ipywidgets~=7.6.3 ; extra == "notebook"
100
100
  Requires-Dist: lightgbm ; extra == "onnx"
101
101
  Requires-Dist: onnx>=1.12.0,<=1.15.0 ; extra == "onnx" and ( python_version < '3.12')
102
- Requires-Dist: onnx>=1.12.0 ; extra == "onnx" and ( python_version >= '3.12')
103
- Requires-Dist: onnxmltools>=1.10.0 ; extra == "onnx"
102
+ Requires-Dist: onnx~=1.17.0 ; extra == "onnx" and ( python_version >= '3.12')
103
+ Requires-Dist: onnxmltools~=1.13.0 ; extra == "onnx"
104
104
  Requires-Dist: onnxruntime~=1.17.0,!=1.16.0 ; extra == "onnx" and ( python_version < '3.12')
105
- Requires-Dist: onnxruntime ; extra == "onnx" and ( python_version >= '3.12')
105
+ Requires-Dist: onnxruntime~=1.22.0 ; extra == "onnx" and ( python_version >= '3.12')
106
106
  Requires-Dist: oracle_ads[viz] ; extra == "onnx"
107
107
  Requires-Dist: protobuf ; extra == "onnx"
108
- Requires-Dist: skl2onnx>=1.10.4 ; extra == "onnx"
108
+ Requires-Dist: skl2onnx>=1.10.4 ; extra == "onnx" and ( python_version < '3.12')
109
+ Requires-Dist: skl2onnx~=1.18.0 ; extra == "onnx" and ( python_version >= '3.12')
109
110
  Requires-Dist: tf2onnx ; extra == "onnx"
110
111
  Requires-Dist: xgboost<=1.7 ; extra == "onnx"
111
112
  Requires-Dist: conda-pack ; extra == "opctl"
@@ -1,23 +1,23 @@
1
1
  ads/__init__.py,sha256=OxHySbHbMqPgZ8sUj33Bxy-smSiNgRjtcSUV77oBL08,3787
2
2
  ads/cli.py,sha256=WkOpZv8jWgFYN9BNkt2LJBs9KzJHgFqq3pIymsqc8Q4,4292
3
- ads/config.py,sha256=yrCvWEEYcMwWkk9_6IJJZnxbvrOVzsQNMBrCJVafYU8,8106
3
+ ads/config.py,sha256=ZFZpp0SgKgB1-7yQ6MEdVdyJ2nwzixbH91sJ4_XnG8Y,8170
4
4
  ads/aqua/__init__.py,sha256=7DjwtmZaX-_atIkmZu6XQKHqJUEeemJGR2TlxzMHSXs,973
5
- ads/aqua/app.py,sha256=KesfIyVm3T8mj3ugsdVSp05b9RwQAEVw7QN1UB4o4qU,18397
5
+ ads/aqua/app.py,sha256=PmG8-XnOfMRJ0GQgJDl6TSGF95EBTrMXGRO8p1FG43Y,18475
6
6
  ads/aqua/cli.py,sha256=8S0JnhWY9IBZjMyB-5r4I-2nl-WK6yw1iirPsAXICF0,3358
7
- ads/aqua/constants.py,sha256=E_7eaHTMkKjY1VMe8os8xW337giIjESUYvMAnbN9bKw,4981
7
+ ads/aqua/constants.py,sha256=dUl02j5XTAG6sL7XJ9HS5fT0Z869WceRLFIbBwCzmtw,5081
8
8
  ads/aqua/data.py,sha256=HfxLfKiNiPJecMQy0JAztUsT3IdZilHHHOrCJnjZMc4,408
9
- ads/aqua/ui.py,sha256=AyX1vFW9f6hoyKN55M6s4iKBLHsOHC41hwRjDfD4NlI,20191
9
+ ads/aqua/ui.py,sha256=PYyr46ewx9Qygcsv6BryUF6rLHU0t5YjUgKSb1uZK2Y,20971
10
10
  ads/aqua/client/__init__.py,sha256=-46EcKQjnWEXxTt85bQzXjA5xsfoBXIGm_syKFlVL1c,178
11
11
  ads/aqua/client/client.py,sha256=zlscNhFZVgGnkJ-aj5iZ5v5FedOzpQc4RJDxGPl9VvQ,31388
12
12
  ads/aqua/client/openai_client.py,sha256=Gi8nSrtPAUOjxRNu-6UUAqtxWyQIQ5CAvatnm7XfnaM,12501
13
13
  ads/aqua/common/__init__.py,sha256=rZrmh1nho40OCeabXCNWtze-mXi-PGKetcZdxZSn3_0,204
14
14
  ads/aqua/common/decorator.py,sha256=JEN6Cy4DYgQbmIR3ShCjTuBMCnilDxq7jkYMJse1rcM,4112
15
- ads/aqua/common/entities.py,sha256=kLUJu77Sg97VrHb76PvFAAaSWEUum9nYTwzMtOnUo50,8922
16
- ads/aqua/common/enums.py,sha256=rTZDOQzTfcgwEl7gjVY3_JotHXkz7wB_edEIB0i6AeQ,3739
15
+ ads/aqua/common/entities.py,sha256=ZHlnW2gf6gQ-TmRCjRcobR5qWoM3Vj7RaGYpAIzFKro,9474
16
+ ads/aqua/common/enums.py,sha256=jAo2JMANIUPkAUEaJeHT6e7-TmrkqH-a2n6smAQQPpA,4088
17
17
  ads/aqua/common/errors.py,sha256=QONm-2jKBg8AjgOKXm6x-arAV1KIW9pdhfNN1Ys21Wo,3044
18
- ads/aqua/common/utils.py,sha256=z93NqufjGzmEpsd7VmSvIpFUawcaoLjBFPSiBCjq2Wk,42001
18
+ ads/aqua/common/utils.py,sha256=OBFfvtAyI-CkM0ezF8unfYH596KzNkul-o8XMFgwep0,41833
19
19
  ads/aqua/config/__init__.py,sha256=2a_1LI4jWtJpbic5_v4EoOUTXCAH7cmsy9BW5prDHjU,179
20
- ads/aqua/config/container_config.py,sha256=WkxaBZ-6TlKXbhrLD5q-BAmXXZp_crLoZGp8TNtbGHg,9844
20
+ ads/aqua/config/container_config.py,sha256=W6V5VZOLyEAbiY3rEbHyv4KY8yRhj8Qp6k1nWX_FwWg,9590
21
21
  ads/aqua/config/evaluation/__init__.py,sha256=2a_1LI4jWtJpbic5_v4EoOUTXCAH7cmsy9BW5prDHjU,179
22
22
  ads/aqua/config/evaluation/evaluation_service_config.py,sha256=NuaQoLVYPHJiWjGfq1-F6-DK0DyOAGjVS87K1SXFVvw,4497
23
23
  ads/aqua/config/utils/__init__.py,sha256=2a_1LI4jWtJpbic5_v4EoOUTXCAH7cmsy9BW5prDHjU,179
@@ -44,7 +44,7 @@ ads/aqua/extension/evaluation_ws_msg_handler.py,sha256=dv0iwOSTxYj1kQ1rPEoDmGgFB
44
44
  ads/aqua/extension/finetune_handler.py,sha256=97obbhITswTrBvl88g7gk4GvF2SUHBGUAq4rOylFbtQ,3079
45
45
  ads/aqua/extension/model_handler.py,sha256=LlfBqGI4YVXio0gUnqi7Tpe3yfkc7-ToZCcQ3cds6rY,14094
46
46
  ads/aqua/extension/models_ws_msg_handler.py,sha256=VyPbtBZrbRMIYJqOy5DR7j4M4qJK1RBqkxX6RbIvPGE,1851
47
- ads/aqua/extension/ui_handler.py,sha256=Q0LkrV6VtVUI4GpNgqJQt8SGzxHzp4X5hdHF6KgPp9M,11217
47
+ ads/aqua/extension/ui_handler.py,sha256=OAyRgEQ26rpNA5CwBGG6YXM9qCuPrZAUXW490a-fkxs,12136
48
48
  ads/aqua/extension/ui_websocket_handler.py,sha256=oLFjaDrqkSERbhExdvxjLJX0oRcP-DVJ_aWn0qy0uvo,5084
49
49
  ads/aqua/extension/utils.py,sha256=-uppIKADKl8TFzZB2QWEIei_wtVwWN63qffhuh4Q_KA,5159
50
50
  ads/aqua/extension/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -52,15 +52,16 @@ ads/aqua/extension/models/ws_models.py,sha256=IgAwu324zlT0XII2nFWQUTeEzqvbFch_9K
52
52
  ads/aqua/finetuning/__init__.py,sha256=vwYT5PluMR0mDQwVIavn_8Icms7LmvfV_FOrJ8fJx8I,296
53
53
  ads/aqua/finetuning/constants.py,sha256=Fx-8LMyF9ZbV9zo5LUYgCv9VniV7djGnM2iW7js2ILE,844
54
54
  ads/aqua/finetuning/entities.py,sha256=ax6tpqrzuF54YNdwJNRSpzhAnkvOeXdnJ18EA-GfIlw,6885
55
- ads/aqua/finetuning/finetuning.py,sha256=SizHmPN1kOlzriQ2GHUvyhL9LxEmntoBFusHhYAz6SI,30220
55
+ ads/aqua/finetuning/finetuning.py,sha256=11DJEEZPa0yu8k0wZvp9IuYEU7IdOd_ZQFUigTqvG0k,31094
56
56
  ads/aqua/model/__init__.py,sha256=j2iylvERdANxgrEDp7b_mLcKMz1CF5Go0qgYCiMwdos,278
57
57
  ads/aqua/model/constants.py,sha256=oOAb4ulsdWBtokCE5SPX7wg8X8SaisLPayua58zhWfY,1856
58
- ads/aqua/model/entities.py,sha256=8P9BEXCroruJHA1RhL66NdmScL-Lql1_7SjnFYk273Y,10089
59
- ads/aqua/model/enums.py,sha256=iJi-AZRh7KR_HK5HUwTkgnTOGVna2Ai5WEzqCjk7Y3s,1079
60
- ads/aqua/model/model.py,sha256=A_s4a4B6jh-VHgfqzuxDzfPHEe9c2O4P_Dm8ZA56LjA,86719
58
+ ads/aqua/model/entities.py,sha256=JiKB8SnaUxerRMlwrgpyfQLRuTOB8I14J-Rg5RFPwqw,12660
59
+ ads/aqua/model/enums.py,sha256=bN8GKmgRl40PQrTmd1r-Pqr9VXTIV8gic5-3SAGNnwg,1152
60
+ ads/aqua/model/model.py,sha256=nq9PehumfFyh_UZDhltVl7Vi_L4TMM9KCx6yqZL3oDw,89027
61
+ ads/aqua/model/utils.py,sha256=8lYaD51JpAPb7U_xQzRSzFflRefU5Yz6vA80cuZOL0w,2087
61
62
  ads/aqua/modeldeployment/__init__.py,sha256=RJCfU1yazv3hVWi5rS08QVLTpTwZLnlC8wU8diwFjnM,391
62
63
  ads/aqua/modeldeployment/constants.py,sha256=lJF77zwxmlECljDYjwFAMprAUR_zctZHmawiP-4alLg,296
63
- ads/aqua/modeldeployment/deployment.py,sha256=8HWFkc50_DdTM4MEPVzUXYOxvAmXeEHplqsPzK-II8k,56071
64
+ ads/aqua/modeldeployment/deployment.py,sha256=5dAN-Tl1C75w6zSKtKCuzfPRjS-GfZ_LP25hq8h6b5k,55549
64
65
  ads/aqua/modeldeployment/entities.py,sha256=qwNH-8eHv-C2QPMITGQkb6haaJRvZ5c0i1H0Aoxeiu4,27100
65
66
  ads/aqua/modeldeployment/inference.py,sha256=rjTF-AM_rHLzL5HCxdLRTrsaSMdB-SzFYUp9dIy5ejw,2109
66
67
  ads/aqua/modeldeployment/utils.py,sha256=Aky4WZ5E564JVZ96X9RYJz_KlB_cAHGzV6mihtd3HV8,22009
@@ -98,7 +99,7 @@ ads/common/model_artifact_schema.json,sha256=aNwC9bW5HHMXJK-XAWV56RosqOqiCkzKHBi
98
99
  ads/common/model_export_util.py,sha256=rQjL_bb0ecGV2fj0jQXS2-aRbXl4ONDwuNVqdr3DiAQ,26574
99
100
  ads/common/model_metadata.py,sha256=Md1soURHww8GHMG3q_HV0RHVb6dPtg9FZ_7Wmd9L-Yc,641
100
101
  ads/common/object_storage_details.py,sha256=bvqIyB-zLpr5NMnZW8YtSupVH3RpWLBgbR3wPYlQhPU,9531
101
- ads/common/oci_client.py,sha256=SmME1qdCFtOdMtC7-703C732e4lEK71kNfTSqBRFkSM,6053
102
+ ads/common/oci_client.py,sha256=S7Idou_9tQAd-_fPjAZBkHzkPnmJ_NPRI93lQsFcUiI,6164
102
103
  ads/common/oci_datascience.py,sha256=biBgm-udtSYRL46XYfBFJjpkPFcw2ew-xvp3rbbpwmI,1698
103
104
  ads/common/oci_logging.py,sha256=U0HRAUkpnycGpo2kWMrT3wjQVFZaWqLL6pZ2B6_epsM,41925
104
105
  ads/common/oci_mixin.py,sha256=mhja5UomrhXH43uB0jT-u2KaT37op9tM-snxvtGfc40,34548
@@ -723,12 +724,12 @@ ads/opctl/operator/lowcode/forecast/model/__init__.py,sha256=sAqmLhogrLXb3xI7dPO
723
724
  ads/opctl/operator/lowcode/forecast/model/arima.py,sha256=PvHoTdDr6RIC4I-YLzed91td6Pq6uxbgluEdu_h0e3c,11766
724
725
  ads/opctl/operator/lowcode/forecast/model/automlx.py,sha256=4XwS60f7Cs9-oexAn_v0hiWHmrw4jBY_o-_VLzuOd-4,22891
725
726
  ads/opctl/operator/lowcode/forecast/model/autots.py,sha256=UThBBGsEiC3WLSn-BPAuNWT_ZFa3bYMu52keB0vvSt8,13137
726
- ads/opctl/operator/lowcode/forecast/model/base_model.py,sha256=s9WwPpo61YY7teAcmL2MK7cl1GGYAKZu7IkxoReD1I0,35969
727
+ ads/opctl/operator/lowcode/forecast/model/base_model.py,sha256=NEc8DknGQWUXdLhqbLNtpwsYowGB9GBly0m7cYBrwaM,36585
727
728
  ads/opctl/operator/lowcode/forecast/model/factory.py,sha256=5a9A3ql-bU412BiTB20ob6OxQlkdk8z_tGONMwDXT1k,3900
728
729
  ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py,sha256=2NsWE2WtD_O7uAXw42_3tmG3vb5lk3mdnzCZTph4hao,18903
729
730
  ads/opctl/operator/lowcode/forecast/model/ml_forecast.py,sha256=t5x6EBxOd7XwfT3FGdt-n9gscxaHMm3R2A4Evvxbj38,9646
730
731
  ads/opctl/operator/lowcode/forecast/model/neuralprophet.py,sha256=60nfNGxjRDsD09Sg7s1YG8G8Qxfcyw0_2rW2PcNy1-c,20021
731
- ads/opctl/operator/lowcode/forecast/model/prophet.py,sha256=jb8bshJf5lDdGJkNH-2SrwN4tdHImP7iD9I8KS4EmZU,17321
732
+ ads/opctl/operator/lowcode/forecast/model/prophet.py,sha256=M9WpL0A5yAr10MljAz9Tnbp6Xd4Zg4kf4a_RiQ4l0Lg,17329
732
733
  ads/opctl/operator/lowcode/forecast/whatifserve/__init__.py,sha256=JNDDjLrNorKXMHUuXMifqXea3eheST-lnrcwCl2bWrk,242
733
734
  ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py,sha256=w42anuqAScEQ0vBG3vW4LVLNq1bPdpAWGQEmNhMwZ08,12052
734
735
  ads/opctl/operator/lowcode/forecast/whatifserve/score.py,sha256=JjEDtrqUfL4x9t-vvafXMLNwY9-vgc6QPX_Ee-wmI58,8709
@@ -807,7 +808,7 @@ ads/secrets/oracledb.py,sha256=VAbsY206gc_Ru8FBOCKNGnIlX4VprIIQ9PmBoujRy_k,6146
807
808
  ads/secrets/secrets.py,sha256=k_f3hwh5RI7Hv7zEKDDuxh7kGB-F29bK15jxgu4sxVw,15197
808
809
  ads/telemetry/__init__.py,sha256=_pKx4hDK7DUXHjvAq4Zbeg6wl9_UkgGE7O77KWezu8g,230
809
810
  ads/telemetry/base.py,sha256=6kKVSfTxx2Dr0XL7R8OfjTy3hJoJpKMvP6D7RdVddl4,2199
810
- ads/telemetry/client.py,sha256=33xG082IGoWCbhD5xmYkDgI2znqL9LgqLM2kfvbg6fw,4975
811
+ ads/telemetry/client.py,sha256=71hUqn-fF24zlb84ew5A0gugdkhBmzn30JEuNHDnMyM,5000
811
812
  ads/telemetry/telemetry.py,sha256=xCu64tUEzYzFaXEMxnMdtQJPme5-9sW8l3uxibN4E04,7930
812
813
  ads/templates/dataflow_pyspark.jinja2,sha256=JiTuaGt5LC7z1mrDoDIbZ5UskBETLpm0lMpn5VZYNGs,254
813
814
  ads/templates/dataflow_sparksql.jinja2,sha256=8MvGzj7PWgUNo-9dg6zil8WTpBL3eNcR815fz64v1yM,466
@@ -851,8 +852,8 @@ ads/type_discovery/unknown_detector.py,sha256=yZuYQReO7PUyoWZE7onhhtYaOg6088wf1y
851
852
  ads/type_discovery/zipcode_detector.py,sha256=3AlETg_ZF4FT0u914WXvTT3F3Z6Vf51WiIt34yQMRbw,1421
852
853
  ads/vault/__init__.py,sha256=x9tMdDAOdF5iDHk9u2di_K-ze5Nq068x25EWOBoWwqY,245
853
854
  ads/vault/vault.py,sha256=hFBkpYE-Hfmzu1L0sQwUfYcGxpWmgG18JPndRl0NOXI,8624
854
- oracle_ads-2.13.7.dist-info/entry_points.txt,sha256=9VFnjpQCsMORA4rVkvN8eH6D3uHjtegb9T911t8cqV0,35
855
- oracle_ads-2.13.7.dist-info/licenses/LICENSE.txt,sha256=zoGmbfD1IdRKx834U0IzfFFFo5KoFK71TND3K9xqYqo,1845
856
- oracle_ads-2.13.7.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
857
- oracle_ads-2.13.7.dist-info/METADATA,sha256=KdppdpPFasY7kL7tn_VDR0DK67piMATXa9XdW80TNqA,16639
858
- oracle_ads-2.13.7.dist-info/RECORD,,
855
+ oracle_ads-2.13.9.dist-info/entry_points.txt,sha256=9VFnjpQCsMORA4rVkvN8eH6D3uHjtegb9T911t8cqV0,35
856
+ oracle_ads-2.13.9.dist-info/licenses/LICENSE.txt,sha256=zoGmbfD1IdRKx834U0IzfFFFo5KoFK71TND3K9xqYqo,1845
857
+ oracle_ads-2.13.9.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
858
+ oracle_ads-2.13.9.dist-info/METADATA,sha256=TW6czNo8lhOSi7ZdTRIEKk9lEbaEWSenFfdQksFrrUE,16764
859
+ oracle_ads-2.13.9.dist-info/RECORD,,