oracle-ads 2.13.11__py3-none-any.whl → 2.13.13__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 (43) hide show
  1. ads/aqua/app.py +73 -15
  2. ads/aqua/cli.py +17 -0
  3. ads/aqua/client/client.py +38 -21
  4. ads/aqua/client/openai_client.py +20 -10
  5. ads/aqua/common/entities.py +78 -12
  6. ads/aqua/common/utils.py +35 -0
  7. ads/aqua/constants.py +2 -0
  8. ads/aqua/evaluation/evaluation.py +5 -4
  9. ads/aqua/extension/common_handler.py +47 -2
  10. ads/aqua/extension/model_handler.py +51 -9
  11. ads/aqua/model/constants.py +1 -0
  12. ads/aqua/model/enums.py +19 -1
  13. ads/aqua/model/model.py +119 -51
  14. ads/aqua/model/utils.py +1 -2
  15. ads/aqua/modeldeployment/config_loader.py +815 -0
  16. ads/aqua/modeldeployment/constants.py +4 -1
  17. ads/aqua/modeldeployment/deployment.py +178 -129
  18. ads/aqua/modeldeployment/entities.py +150 -178
  19. ads/aqua/modeldeployment/model_group_config.py +233 -0
  20. ads/aqua/modeldeployment/utils.py +0 -539
  21. ads/aqua/verify_policies/__init__.py +8 -0
  22. ads/aqua/verify_policies/constants.py +13 -0
  23. ads/aqua/verify_policies/entities.py +29 -0
  24. ads/aqua/verify_policies/messages.py +101 -0
  25. ads/aqua/verify_policies/utils.py +432 -0
  26. ads/aqua/verify_policies/verify.py +345 -0
  27. ads/aqua/version.json +3 -0
  28. ads/common/oci_logging.py +4 -7
  29. ads/common/work_request.py +39 -38
  30. ads/jobs/builders/infrastructure/dsc_job.py +121 -24
  31. ads/jobs/builders/infrastructure/dsc_job_runtime.py +71 -24
  32. ads/jobs/builders/runtimes/base.py +7 -5
  33. ads/jobs/builders/runtimes/pytorch_runtime.py +6 -8
  34. ads/jobs/templates/driver_pytorch.py +486 -172
  35. ads/jobs/templates/driver_utils.py +27 -11
  36. ads/model/deployment/model_deployment.py +51 -38
  37. ads/model/service/oci_datascience_model_deployment.py +6 -11
  38. ads/telemetry/client.py +4 -4
  39. {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.13.dist-info}/METADATA +2 -1
  40. {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.13.dist-info}/RECORD +43 -34
  41. {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.13.dist-info}/WHEEL +0 -0
  42. {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.13.dist-info}/entry_points.txt +0 -0
  43. {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.13.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python
2
2
  # Copyright (c) 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
-
5
-
4
+ import json
5
+ import os
6
6
  from importlib import metadata
7
7
 
8
8
  import huggingface_hub
@@ -18,6 +18,10 @@ from ads.aqua.common.utils import (
18
18
  )
19
19
  from ads.aqua.extension.base_handler import AquaAPIhandler
20
20
  from ads.aqua.extension.errors import Errors
21
+ from ads.common.object_storage_details import ObjectStorageDetails
22
+ from ads.common.utils import read_file
23
+ from ads.config import CONDA_BUCKET_NAME, CONDA_BUCKET_NS
24
+ from ads.opctl.operator.common.utils import default_signer
21
25
 
22
26
 
23
27
  class ADSVersionHandler(AquaAPIhandler):
@@ -28,6 +32,46 @@ class ADSVersionHandler(AquaAPIhandler):
28
32
  self.finish({"data": metadata.version("oracle_ads")})
29
33
 
30
34
 
35
+ class AquaVersionHandler(AquaAPIhandler):
36
+ @handle_exceptions
37
+ def get(self):
38
+ """
39
+ Returns the current and latest deployed version of AQUA
40
+
41
+ {
42
+ "installed": {
43
+ "aqua": "0.1.3.0",
44
+ "ads": "2.14.2"
45
+ },
46
+ "latest": {
47
+ "aqua": "0.1.4.0",
48
+ "ads": "2.14.4"
49
+ }
50
+ }
51
+
52
+ """
53
+
54
+ current_aqua_version_path = os.path.join(
55
+ os.path.dirname(os.path.abspath(__file__)), "..", "version.json"
56
+ )
57
+ current_aqua_version = json.loads(read_file(current_aqua_version_path))
58
+ current_ads_version = {"ads": metadata.version("oracle_ads")}
59
+ current_version = {"installed": {**current_aqua_version, **current_ads_version}}
60
+ try:
61
+ latest_version_artifact_path = ObjectStorageDetails(
62
+ CONDA_BUCKET_NAME,
63
+ CONDA_BUCKET_NS,
64
+ "service_pack/aqua_latest_version.json",
65
+ ).path
66
+ latest_version = json.loads(
67
+ read_file(latest_version_artifact_path, auth=default_signer())
68
+ )
69
+ except Exception:
70
+ latest_version = {"latest": current_version["installed"]}
71
+ response = {**current_version, **latest_version}
72
+ return self.finish(response)
73
+
74
+
31
75
  class CompatibilityCheckHandler(AquaAPIhandler):
32
76
  """The handler to check if the extension is compatible."""
33
77
 
@@ -118,4 +162,5 @@ __handlers__ = [
118
162
  ("network_status", NetworkStatusHandler),
119
163
  ("hf_login", HFLoginHandler),
120
164
  ("hf_logged_in", HFUserStatusHandler),
165
+ ("aqua_version", AquaVersionHandler),
121
166
  ]
@@ -11,12 +11,15 @@ from ads.aqua.common.decorator import handle_exceptions
11
11
  from ads.aqua.common.enums import CustomInferenceContainerTypeFamily
12
12
  from ads.aqua.common.errors import AquaRuntimeError
13
13
  from ads.aqua.common.utils import get_hf_model_info, is_valid_ocid, list_hf_models
14
+ from ads.aqua.constants import AQUA_CHAT_TEMPLATE_METADATA_KEY
14
15
  from ads.aqua.extension.base_handler import AquaAPIhandler
15
16
  from ads.aqua.extension.errors import Errors
16
17
  from ads.aqua.model import AquaModelApp
17
18
  from ads.aqua.model.entities import AquaModelSummary, HFModelSummary
18
19
  from ads.config import SERVICE
20
+ from ads.model import DataScienceModel
19
21
  from ads.model.common.utils import MetadataArtifactPathType
22
+ from ads.model.service.oci_datascience_model import OCIDataScienceModel
20
23
 
21
24
 
22
25
  class AquaModelHandler(AquaAPIhandler):
@@ -241,7 +244,7 @@ class AquaHuggingFaceHandler(AquaAPIhandler):
241
244
  aqua_model_app = AquaModelApp()
242
245
  model_ocid = aqua_model_app._find_matching_aqua_model(model_id=model_id_lower)
243
246
  if model_ocid:
244
- return aqua_model_app.get(model_ocid, load_model_card=False)
247
+ return aqua_model_app.get(model_ocid)
245
248
 
246
249
  return None
247
250
 
@@ -320,26 +323,65 @@ class AquaHuggingFaceHandler(AquaAPIhandler):
320
323
  )
321
324
 
322
325
 
323
- class AquaModelTokenizerConfigHandler(AquaAPIhandler):
326
+ class AquaModelChatTemplateHandler(AquaAPIhandler):
324
327
  def get(self, model_id):
325
328
  """
326
- Handles requests for retrieving the Hugging Face tokenizer configuration of a specified model.
327
- Expected request format: GET /aqua/models/<model-ocid>/tokenizer
329
+ Handles requests for retrieving the chat template from custom metadata of a specified model.
330
+ Expected request format: GET /aqua/models/<model-ocid>/chat-template
328
331
 
329
332
  """
330
333
 
331
334
  path_list = urlparse(self.request.path).path.strip("/").split("/")
332
- # Path should be /aqua/models/ocid1.iad.ahdxxx/tokenizer
333
- # path_list=['aqua','models','<model-ocid>','tokenizer']
335
+ # Path should be /aqua/models/ocid1.iad.ahdxxx/chat-template
336
+ # path_list=['aqua','models','<model-ocid>','chat-template']
334
337
  if (
335
338
  len(path_list) == 4
336
339
  and is_valid_ocid(path_list[2])
337
- and path_list[3] == "tokenizer"
340
+ and path_list[3] == "chat-template"
338
341
  ):
339
- return self.finish(AquaModelApp().get_hf_tokenizer_config(model_id))
342
+ try:
343
+ oci_data_science_model = OCIDataScienceModel.from_id(model_id)
344
+ except Exception as e:
345
+ raise HTTPError(404, f"Model not found for id: {model_id}. Details: {str(e)}")
346
+ return self.finish(oci_data_science_model.get_custom_metadata_artifact("chat_template"))
340
347
 
341
348
  raise HTTPError(400, f"The request {self.request.path} is invalid.")
342
349
 
350
+ @handle_exceptions
351
+ def post(self, model_id: str):
352
+ """
353
+ Handles POST requests to add a custom chat_template metadata artifact to a model.
354
+
355
+ Expected request format:
356
+ POST /aqua/models/<model-ocid>/chat-template
357
+ Body: { "chat_template": "<your_template_string>" }
358
+
359
+ """
360
+ try:
361
+ input_body = self.get_json_body()
362
+ except Exception as e:
363
+ raise HTTPError(400, f"Invalid JSON body: {str(e)}")
364
+
365
+ chat_template = input_body.get("chat_template")
366
+ if not chat_template:
367
+ raise HTTPError(400, "Missing required field: 'chat_template'")
368
+
369
+ try:
370
+ data_science_model = DataScienceModel.from_id(model_id)
371
+ except Exception as e:
372
+ raise HTTPError(404, f"Model not found for id: {model_id}. Details: {str(e)}")
373
+
374
+ try:
375
+ result = data_science_model.create_custom_metadata_artifact(
376
+ metadata_key_name=AQUA_CHAT_TEMPLATE_METADATA_KEY,
377
+ path_type=MetadataArtifactPathType.CONTENT,
378
+ artifact_path_or_content=chat_template.encode()
379
+ )
380
+ except Exception as e:
381
+ raise HTTPError(500, f"Failed to create metadata artifact: {str(e)}")
382
+
383
+ return self.finish(result)
384
+
343
385
 
344
386
  class AquaModelDefinedMetadataArtifactHandler(AquaAPIhandler):
345
387
  """
@@ -381,7 +423,7 @@ __handlers__ = [
381
423
  ("model/?([^/]*)", AquaModelHandler),
382
424
  ("model/?([^/]*)/license", AquaModelLicenseHandler),
383
425
  ("model/?([^/]*)/readme", AquaModelReadmeHandler),
384
- ("model/?([^/]*)/tokenizer", AquaModelTokenizerConfigHandler),
426
+ ("model/?([^/]*)/chat-template", AquaModelChatTemplateHandler),
385
427
  ("model/hf/search/?([^/]*)", AquaHuggingFaceHandler),
386
428
  (
387
429
  "model/?([^/]*)/definedMetadata/?([^/]*)",
@@ -26,6 +26,7 @@ class ModelTask(ExtendedEnum):
26
26
  TEXT_GENERATION = "text-generation"
27
27
  IMAGE_TEXT_TO_TEXT = "image-text-to-text"
28
28
  IMAGE_TO_TEXT = "image-to-text"
29
+ TIME_SERIES_FORECASTING = "time-series-forecasting"
29
30
 
30
31
 
31
32
  class FineTuningMetricCategories(ExtendedEnum):
ads/aqua/model/enums.py CHANGED
@@ -27,6 +27,24 @@ class FineTuningCustomMetadata(ExtendedEnum):
27
27
 
28
28
  class MultiModelSupportedTaskType(ExtendedEnum):
29
29
  TEXT_GENERATION = "text_generation"
30
+ TEXT_GENERATION_INFERENCE = "text_generation_inference"
31
+ TEXT2TEXT_GENERATION = "text2text_generation"
32
+ SUMMARIZATION = "summarization"
33
+ TRANSLATION = "translation"
34
+ CONVERSATIONAL = "conversational"
35
+ FEATURE_EXTRACTION = "feature_extraction"
36
+ SENTENCE_SIMILARITY = "sentence_similarity"
37
+ AUTOMATIC_SPEECH_RECOGNITION = "automatic_speech_recognition"
38
+ TEXT_TO_SPEECH = "text_to_speech"
39
+ TEXT_TO_IMAGE = "text_to_image"
40
+ TEXT_EMBEDDING = "text_embedding"
30
41
  IMAGE_TEXT_TO_TEXT = "image_text_to_text"
31
42
  CODE_SYNTHESIS = "code_synthesis"
32
- EMBEDDING = "text_embedding"
43
+ QUESTION_ANSWERING = "question_answering"
44
+ AUDIO_CLASSIFICATION = "audio_classification"
45
+ AUDIO_TO_AUDIO = "audio_to_audio"
46
+ IMAGE_CLASSIFICATION = "image_classification"
47
+ IMAGE_TO_TEXT = "image_to_text"
48
+ IMAGE_TO_IMAGE = "image_to_image"
49
+ VIDEO_CLASSIFICATION = "video_classification"
50
+ TIME_SERIES_FORECASTING = "time_series_forecasting"
ads/aqua/model/model.py CHANGED
@@ -83,18 +83,10 @@ from ads.aqua.model.entities import (
83
83
  ModelValidationResult,
84
84
  )
85
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
- )
86
+ from ads.aqua.model.utils import extract_fine_tune_artifacts_path
90
87
  from ads.common.auth import default_signer
91
88
  from ads.common.oci_resource import SEARCH_TYPE, OCIResource
92
- from ads.common.utils import (
93
- UNKNOWN,
94
- get_console_link,
95
- is_path_exists,
96
- read_file,
97
- )
89
+ from ads.common.utils import UNKNOWN, get_console_link, is_path_exists, read_file
98
90
  from ads.config import (
99
91
  AQUA_DEPLOYMENT_CONTAINER_CMD_VAR_METADATA_NAME,
100
92
  AQUA_DEPLOYMENT_CONTAINER_METADATA_NAME,
@@ -247,6 +239,7 @@ class AquaModelApp(AquaApp):
247
239
  compartment_id: Optional[str] = None,
248
240
  freeform_tags: Optional[Dict] = None,
249
241
  defined_tags: Optional[Dict] = None,
242
+ source_models: Optional[Dict[str, DataScienceModel]] = None,
250
243
  **kwargs, # noqa: ARG002
251
244
  ) -> DataScienceModel:
252
245
  """
@@ -264,6 +257,10 @@ class AquaModelApp(AquaApp):
264
257
  Freeform tags for the model.
265
258
  defined_tags : Optional[Dict]
266
259
  Defined tags for the model.
260
+ source_models: Optional[Dict[str, DataScienceModel]]
261
+ A mapping of model OCIDs to their corresponding `DataScienceModel` objects.
262
+ This dictionary contains metadata for all models involved in the multi-model deployment,
263
+ including both base models and fine-tuned weights.
267
264
 
268
265
  Returns
269
266
  -------
@@ -300,60 +297,127 @@ class AquaModelApp(AquaApp):
300
297
 
301
298
  selected_models_deployment_containers = set()
302
299
 
303
- # Process each model
300
+ if not source_models:
301
+ # Collect all unique model IDs (including fine-tuned models)
302
+ source_model_ids = list(
303
+ {model_id for model in models for model_id in model.all_model_ids()}
304
+ )
305
+ logger.debug(
306
+ "Fetching source model metadata for model IDs: %s", source_model_ids
307
+ )
308
+
309
+ # Fetch source model metadata
310
+ source_models = self.get_multi_source(source_model_ids) or {}
311
+
312
+ # Process each model in the input list
304
313
  for model in models:
305
- source_model = DataScienceModel.from_id(model.model_id)
306
- display_name = source_model.display_name
314
+ # Retrieve base model metadata
315
+ source_model: DataScienceModel = source_models.get(model.model_id)
316
+ if not source_model:
317
+ logger.error(
318
+ "Failed to fetch metadata for base model ID: %s", model.model_id
319
+ )
320
+ raise AquaValueError(
321
+ f"Unable to retrieve metadata for base model ID: {model.model_id}."
322
+ )
323
+
324
+ # Use display name as fallback if model name not provided
325
+ model.model_name = model.model_name or source_model.display_name
326
+
327
+ # Validate model file description
307
328
  model_file_description = source_model.model_file_description
308
- # Update model name in user's input model
309
- model.model_name = model.model_name or display_name
310
-
311
- # TODO Uncomment the section below, if only service models should be allowed for multi-model deployment
312
- # if not source_model.freeform_tags.get(Tags.AQUA_SERVICE_MODEL_TAG, UNKNOWN):
313
- # raise AquaValueError(
314
- # f"Invalid selected model {display_name}. "
315
- # "Currently only service models are supported for multi model deployment."
316
- # )
317
-
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
329
+ if not model_file_description:
330
+ logger.error(
331
+ "Model '%s' (%s) has no file description.",
332
+ source_model.display_name,
333
+ model.model_id,
334
+ )
335
+ raise AquaValueError(
336
+ f"Model '{source_model.display_name}' (ID: {model.model_id}) has no file description. "
337
+ "Please register the model with a file description."
338
+ )
339
+
340
+ # Track model file description in a validated structure
341
+ model_file_description_list.append(
342
+ ModelFileDescription(**model_file_description)
321
343
  )
322
344
 
323
- if is_fine_tuned_model:
324
- model.model_id, model.model_name = extract_base_model_from_ft(
325
- source_model
345
+ # Ensure base model has a valid artifact
346
+ if not source_model.artifact:
347
+ logger.error(
348
+ "Base model '%s' (%s) has no artifact.",
349
+ model.model_name,
350
+ model.model_id,
326
351
  )
327
- model_artifact_path, model.fine_tune_weights_location = (
328
- extract_fine_tune_artifacts_path(source_model)
352
+ raise AquaValueError(
353
+ f"Model '{model.model_name}' (ID: {model.model_id}) has no registered artifacts. "
354
+ "Please register the model before deployment."
329
355
  )
330
356
 
331
- else:
332
- # Retrieve model artifact for base models
333
- model_artifact_path = source_model.artifact
357
+ # Set base model artifact path
358
+ model.artifact_location = source_model.artifact
359
+ logger.debug(
360
+ "Model '%s' artifact path set to: %s",
361
+ model.model_name,
362
+ model.artifact_location,
363
+ )
334
364
 
335
- display_name_list.append(display_name)
365
+ display_name_list.append(model.model_name)
336
366
 
367
+ # Extract model task metadata from source model
337
368
  self._extract_model_task(model, source_model)
338
369
 
339
- if not model_artifact_path:
340
- raise AquaValueError(
341
- f"Model '{display_name}' (ID: {model.model_id}) has no artifacts. "
342
- "Please register the model first."
370
+ # Process fine-tuned weights if provided
371
+ for ft_model in model.fine_tune_weights or []:
372
+ fine_tune_source_model: DataScienceModel = source_models.get(
373
+ ft_model.model_id
343
374
  )
375
+ if not fine_tune_source_model:
376
+ logger.error(
377
+ "Failed to fetch metadata for fine-tuned model ID: %s",
378
+ ft_model.model_id,
379
+ )
380
+ raise AquaValueError(
381
+ f"Unable to retrieve metadata for fine-tuned model ID: {ft_model.model_id}."
382
+ )
344
383
 
345
- # Update model artifact location in user's input model
346
- model.artifact_location = model_artifact_path
384
+ # Validate model file description
385
+ ft_model_file_description = (
386
+ fine_tune_source_model.model_file_description
387
+ )
388
+ if not ft_model_file_description:
389
+ logger.error(
390
+ "Model '%s' (%s) has no file description.",
391
+ fine_tune_source_model.display_name,
392
+ ft_model.model_id,
393
+ )
394
+ raise AquaValueError(
395
+ f"Model '{fine_tune_source_model.display_name}' (ID: {ft_model.model_id}) has no file description. "
396
+ "Please register the model with a file description."
397
+ )
347
398
 
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."
399
+ # Track model file description in a validated structure
400
+ model_file_description_list.append(
401
+ ModelFileDescription(**ft_model_file_description)
352
402
  )
353
403
 
354
- model_file_description_list.append(
355
- ModelFileDescription(**model_file_description)
356
- )
404
+ # Extract fine-tuned model path
405
+ _, fine_tune_path = extract_fine_tune_artifacts_path(
406
+ fine_tune_source_model
407
+ )
408
+ logger.debug(
409
+ "Resolved fine-tuned model path for '%s': %s",
410
+ ft_model.model_id,
411
+ fine_tune_path,
412
+ )
413
+ ft_model.model_path = fine_tune_path
414
+
415
+ # Use fallback name if needed
416
+ ft_model.model_name = (
417
+ ft_model.model_name or fine_tune_source_model.display_name
418
+ )
419
+
420
+ display_name_list.append(ft_model.model_name)
357
421
 
358
422
  # Validate deployment container consistency
359
423
  deployment_container = source_model.custom_metadata_list.get(
@@ -364,9 +428,15 @@ class AquaModelApp(AquaApp):
364
428
  ).value
365
429
 
366
430
  if deployment_container not in supported_container_families:
431
+ logger.error(
432
+ "Unsupported deployment container '%s' for model '%s'. Supported: %s",
433
+ deployment_container,
434
+ source_model.id,
435
+ supported_container_families,
436
+ )
367
437
  raise AquaValueError(
368
438
  f"Unsupported deployment container '{deployment_container}' for model '{source_model.id}'. "
369
- f"Only '{supported_container_families}' are supported for multi-model deployments."
439
+ f"Only {supported_container_families} are supported for multi-model deployments."
370
440
  )
371
441
 
372
442
  selected_models_deployment_containers.add(deployment_container)
@@ -480,8 +550,6 @@ class AquaModelApp(AquaApp):
480
550
  ----------
481
551
  model_id: str
482
552
  The model OCID.
483
- load_model_card: (bool, optional). Defaults to `True`.
484
- Whether to load model card from artifacts or not.
485
553
 
486
554
  Returns
487
555
  -------
ads/aqua/model/utils.py CHANGED
@@ -3,9 +3,8 @@
3
3
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
4
  """AQUA model utils"""
5
5
 
6
- from typing import Dict, Optional, Tuple
6
+ from typing import Tuple
7
7
 
8
- from ads.aqua.common.entities import AquaMultiModelRef
9
8
  from ads.aqua.common.errors import AquaValueError
10
9
  from ads.aqua.common.utils import get_model_by_reference_paths
11
10
  from ads.aqua.finetuning.constants import FineTuneCustomMetadata