oracle-ads 2.13.18rc0__py3-none-any.whl → 2.13.19__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 (30) hide show
  1. ads/aqua/cli.py +7 -5
  2. ads/aqua/common/entities.py +195 -48
  3. ads/aqua/common/enums.py +6 -0
  4. ads/aqua/common/errors.py +5 -0
  5. ads/aqua/common/utils.py +157 -66
  6. ads/aqua/constants.py +3 -0
  7. ads/aqua/extension/deployment_handler.py +36 -0
  8. ads/aqua/modeldeployment/constants.py +1 -0
  9. ads/aqua/modeldeployment/deployment.py +95 -14
  10. ads/aqua/modeldeployment/entities.py +3 -0
  11. ads/aqua/modeldeployment/model_group_config.py +3 -3
  12. ads/aqua/resources/gpu_shapes_index.json +315 -26
  13. ads/aqua/shaperecommend/__init__.py +6 -0
  14. ads/aqua/shaperecommend/constants.py +116 -0
  15. ads/aqua/shaperecommend/estimator.py +384 -0
  16. ads/aqua/shaperecommend/llm_config.py +283 -0
  17. ads/aqua/shaperecommend/recommend.py +493 -0
  18. ads/aqua/shaperecommend/shape_report.py +233 -0
  19. ads/aqua/version.json +1 -1
  20. ads/cli.py +9 -1
  21. ads/jobs/builders/infrastructure/dsc_job.py +1 -0
  22. ads/jobs/builders/infrastructure/dsc_job_runtime.py +9 -1
  23. ads/model/service/oci_datascience_model_deployment.py +46 -19
  24. ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +4 -3
  25. ads/pipeline/ads_pipeline.py +13 -9
  26. {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/METADATA +1 -1
  27. {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/RECORD +30 -24
  28. {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/WHEEL +0 -0
  29. {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/entry_points.txt +0 -0
  30. {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/licenses/LICENSE.txt +0 -0
ads/aqua/cli.py CHANGED
@@ -96,18 +96,20 @@ class AquaCommand:
96
96
  "If you intend to chain a function call to the result, please separate the "
97
97
  "flag and the subsequent function call with separator `-`."
98
98
  )
99
-
99
+
100
100
  @staticmethod
101
101
  def install():
102
102
  """Install ADS Aqua Extension from wheel file. Set enviroment variable `AQUA_EXTENSTION_PATH` to change the wheel file path.
103
103
 
104
- Return
104
+ Return
105
105
  ------
106
106
  int:
107
107
  Installatation status.
108
108
  """
109
109
  import subprocess
110
110
 
111
- wheel_file_path = os.environ.get("AQUA_EXTENSTION_PATH", "/ads/extension/adsjupyterlab_aqua_extension*.whl")
112
- status = subprocess.run(f"pip install {wheel_file_path}",shell=True)
113
- return status.check_returncode
111
+ wheel_file_path = os.environ.get(
112
+ "AQUA_EXTENSTION_PATH", "/ads/extension/adsjupyterlab_aqua_extension*.whl"
113
+ )
114
+ status = subprocess.run(f"pip install {wheel_file_path}", shell=True, check=False)
115
+ return status.check_returncode
@@ -3,7 +3,7 @@
3
3
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
4
4
 
5
5
  import re
6
- from typing import Any, Dict, List, Optional
6
+ from typing import Any, Dict, List, Optional, Union
7
7
 
8
8
  from oci.data_science.models import Model
9
9
  from pydantic import BaseModel, ConfigDict, Field, model_validator
@@ -47,19 +47,76 @@ class ModelConfigResult(BaseModel):
47
47
  protected_namespaces = ()
48
48
 
49
49
 
50
- class GPUSpecs(Serializable):
50
+ class ComputeRank(Serializable):
51
51
  """
52
- Represents the GPU specifications for a compute instance.
52
+ Represents the cost and performance rankings for a specific compute shape.
53
+ These rankings help compare different shapes based on their relative pricing
54
+ and computational capabilities.
53
55
  """
54
56
 
55
- gpu_memory_in_gbs: Optional[int] = Field(
56
- default=None, description="The amount of GPU memory available (in GB)."
57
+ cost: Optional[int] = Field(
58
+ None,
59
+ description=(
60
+ "Relative cost ranking of the compute shape. "
61
+ "Value ranges from 10 (most cost-effective) to 100 (most expensive). "
62
+ "Lower values indicate cheaper compute options."
63
+ ),
64
+ )
65
+
66
+ performance: Optional[int] = Field(
67
+ None,
68
+ description=(
69
+ "Relative performance ranking of the compute shape. "
70
+ "Value ranges from 10 (lowest performance) to 110 (highest performance). "
71
+ "Higher values indicate better compute performance."
72
+ ),
57
73
  )
74
+
75
+
76
+ class GPUSpecs(Serializable):
77
+ """
78
+ Represents the specifications and capabilities of a GPU-enabled compute shape.
79
+ Includes details about GPU and CPU resources, supported quantization formats, and
80
+ relative rankings for cost and performance.
81
+ """
82
+
58
83
  gpu_count: Optional[int] = Field(
59
- default=None, description="The number of GPUs available."
84
+ default=None,
85
+ description="Number of physical GPUs available on the compute shape.",
86
+ )
87
+
88
+ gpu_memory_in_gbs: Optional[int] = Field(
89
+ default=None, description="Total GPU memory available in gigabytes (GB)."
60
90
  )
91
+
61
92
  gpu_type: Optional[str] = Field(
62
- default=None, description="The type of GPU (e.g., 'V100, A100, H100')."
93
+ default=None,
94
+ description="Type of GPU and architecture. Example: 'H100', 'GB200'.",
95
+ )
96
+
97
+ quantization: Optional[List[str]] = Field(
98
+ default_factory=list,
99
+ description=(
100
+ "List of supported quantization formats for the GPU. "
101
+ "Examples: 'fp16', 'int8', 'bitsandbytes', 'bf16', 'fp4', etc."
102
+ ),
103
+ )
104
+
105
+ cpu_count: Optional[int] = Field(
106
+ default=None, description="Number of CPU cores available on the shape."
107
+ )
108
+
109
+ cpu_memory_in_gbs: Optional[int] = Field(
110
+ default=None, description="Total CPU memory available in gigabytes (GB)."
111
+ )
112
+
113
+ ranking: Optional[ComputeRank] = Field(
114
+ default=None,
115
+ description=(
116
+ "Relative cost and performance rankings of this shape. "
117
+ "Cost is ranked from 10 (least expensive) to 100+ (most expensive), "
118
+ "and performance from 10 (lowest) to 100+ (highest)."
119
+ ),
63
120
  )
64
121
 
65
122
 
@@ -80,46 +137,49 @@ class GPUShapesIndex(Serializable):
80
137
 
81
138
  class ComputeShapeSummary(Serializable):
82
139
  """
83
- Represents the specifications of a compute instance shape,
84
- including CPU, memory, and optional GPU characteristics.
140
+ Represents a compute shape's specification including CPU, memory, and (if applicable) GPU configuration.
85
141
  """
86
142
 
143
+ available: Optional[bool] = Field(
144
+ default=False,
145
+ description="True if the shape is available in the user's tenancy/region.",
146
+ )
147
+
87
148
  core_count: Optional[int] = Field(
88
- default=None,
89
- description="Total number of CPU cores available for the compute shape.",
149
+ default=None, description="Number of vCPUs available for the compute shape."
90
150
  )
151
+
91
152
  memory_in_gbs: Optional[int] = Field(
92
- default=None,
93
- description="Amount of memory (in GB) available for the compute shape.",
153
+ default=None, description="Total CPU memory available for the shape (in GB)."
94
154
  )
155
+
95
156
  name: Optional[str] = Field(
96
- default=None,
97
- description="Full name of the compute shape, e.g., 'VM.GPU.A10.2'.",
157
+ default=None, description="Name of the compute shape, e.g., 'VM.GPU.A10.2'."
98
158
  )
159
+
99
160
  shape_series: Optional[str] = Field(
100
161
  default=None,
101
- description="Shape family or series, e.g., 'GPU', 'Standard', etc.",
162
+ description="Series or family of the shape, e.g., 'GPU', 'Standard'.",
102
163
  )
164
+
103
165
  gpu_specs: Optional[GPUSpecs] = Field(
104
- default=None,
105
- description="Optional GPU specifications associated with the shape.",
166
+ default=None, description="GPU configuration for the shape, if applicable."
106
167
  )
107
168
 
108
169
  @model_validator(mode="after")
109
170
  @classmethod
110
- def set_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
171
+ def populate_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
111
172
  """
112
- Validates and populates GPU specifications if the shape_series indicates a GPU-based shape.
113
-
114
- - If the shape_series contains "GPU", the validator first checks if the shape name exists
115
- in the GPU_SPECS dictionary. If found, it creates a GPUSpecs instance with the corresponding data.
116
- - If the shape is not found in the GPU_SPECS, it attempts to extract the GPU count from the shape name
117
- using a regex pattern (looking for a number following a dot at the end of the name).
173
+ Attempts to populate GPU specs if the shape is GPU-based and no GPU specs are explicitly set.
118
174
 
119
- The information about shapes is taken from: https://docs.oracle.com/en-us/iaas/data-science/using/supported-shapes.htm
175
+ Logic:
176
+ - If `shape_series` includes 'GPU' and `gpu_specs` is None:
177
+ - Tries to parse the shape name to extract GPU count (e.g., from 'VM.GPU.A10.2').
178
+ - Fallback is based on suffix numeric group (e.g., '.2' → gpu_count=2).
179
+ - If extraction fails, logs debug-level error but does not raise.
120
180
 
121
181
  Returns:
122
- ComputeShapeSummary: The updated instance with gpu_specs populated if applicable.
182
+ ComputeShapeSummary: The updated model instance.
123
183
  """
124
184
  try:
125
185
  if (
@@ -128,16 +188,15 @@ class ComputeShapeSummary(Serializable):
128
188
  and model.name
129
189
  and not model.gpu_specs
130
190
  ):
131
- # Try to extract gpu_count from the shape name using a regex (e.g., "VM.GPU3.2" -> gpu_count=2)
132
191
  match = re.search(r"\.(\d+)$", model.name)
133
192
  if match:
134
193
  gpu_count = int(match.group(1))
135
194
  model.gpu_specs = GPUSpecs(gpu_count=gpu_count)
136
195
  except Exception as err:
137
196
  logger.debug(
138
- f"Error occurred in attempt to extract GPU specification for the f{model.name}. "
139
- f"Details: {err}"
197
+ f"[populate_gpu_specs] Failed to auto-populate GPU specs for shape '{model.name}': {err}"
140
198
  )
199
+
141
200
  return model
142
201
 
143
202
 
@@ -186,55 +245,71 @@ class AquaMultiModelRef(Serializable):
186
245
  """
187
246
  Lightweight model descriptor used for multi-model deployment.
188
247
 
189
- This class only contains essential details
190
- required to fetch complete model metadata and deploy models.
248
+ This class holds essential details required to fetch model metadata and deploy
249
+ individual models as part of a multi-model deployment group.
191
250
 
192
251
  Attributes
193
252
  ----------
194
253
  model_id : str
195
- The unique identifier of the model.
254
+ The unique identifier (OCID) of the base model.
196
255
  model_name : Optional[str]
197
- The name of the model.
256
+ Optional name for the model.
198
257
  gpu_count : Optional[int]
199
- Number of GPUs required for deployment.
258
+ Number of GPUs required to allocate for this model during deployment.
200
259
  model_task : Optional[str]
201
- The task that model operates on. Supported tasks are in MultiModelSupportedTaskType
260
+ The machine learning task this model performs (e.g., text-generation, summarization).
261
+ Supported values are listed in `MultiModelSupportedTaskType`.
202
262
  env_var : Optional[Dict[str, Any]]
203
- Optional environment variables to override during deployment.
263
+ Optional dictionary of environment variables to inject into the runtime environment
264
+ of the model container.
265
+ params : Optional[Dict[str, Any]]
266
+ Optional dictionary of container-specific inference parameters to override.
267
+ These are typically framework-level flags required by the runtime backend.
268
+ For example, in vLLM containers, valid params may include:
269
+ `--tensor-parallel-size`, `--enforce-eager`, `--max-model-len`, etc.
204
270
  artifact_location : Optional[str]
205
- Artifact path of model in the multimodel group.
271
+ Relative path or URI of the model artifact inside the multi-model group folder.
206
272
  fine_tune_weights : Optional[List[LoraModuleSpec]]
207
- For fine tuned models, the artifact path of the modified model weights
273
+ List of fine-tuned weight artifacts (e.g., LoRA modules) associated with this model.
208
274
  """
209
275
 
210
276
  model_id: str = Field(..., description="The model OCID to deploy.")
211
- model_name: Optional[str] = Field(None, description="The name of model.")
277
+ model_name: Optional[str] = Field(None, description="The name of the model.")
212
278
  gpu_count: Optional[int] = Field(
213
- None, description="The gpu count allocation for the model."
279
+ None, description="The number of GPUs allocated for the model."
214
280
  )
215
281
  model_task: Optional[str] = Field(
216
282
  None,
217
- description="The task that model operates on. Supported tasks are in MultiModelSupportedTaskType",
283
+ description="The task this model performs. See `MultiModelSupportedTaskType` for supported values.",
218
284
  )
219
285
  env_var: Optional[dict] = Field(
220
- default_factory=dict, description="The environment variables of the model."
286
+ default_factory=dict,
287
+ description="Environment variables to override during container startup.",
288
+ )
289
+ params: Optional[dict] = Field(
290
+ default_factory=dict,
291
+ description=(
292
+ "Framework-specific startup parameters required by the container runtime. "
293
+ "For example, vLLM models may use flags like `--tensor-parallel-size`, `--enforce-eager`, etc."
294
+ ),
221
295
  )
222
296
  artifact_location: Optional[str] = Field(
223
- None, description="Artifact path of model in the multimodel group."
297
+ None,
298
+ description="Path to the model artifact relative to the multi-model base folder.",
224
299
  )
225
300
  fine_tune_weights: Optional[List[LoraModuleSpec]] = Field(
226
301
  None,
227
- description="For fine tuned models, the artifact path of the modified model weights",
302
+ description="List of fine-tuned weight modules (e.g., LoRA) associated with this base model.",
228
303
  )
229
304
 
230
305
  def all_model_ids(self) -> List[str]:
231
306
  """
232
- Returns all associated model OCIDs, including the base model and any fine-tuned models.
307
+ Returns all model OCIDs associated with this reference, including fine-tuned weights.
233
308
 
234
309
  Returns
235
310
  -------
236
311
  List[str]
237
- A list of all model OCIDs associated with this multi-model reference.
312
+ A list containing the base model OCID and any fine-tuned module OCIDs.
238
313
  """
239
314
  ids = {self.model_id}
240
315
  if self.fine_tune_weights:
@@ -243,8 +318,80 @@ class AquaMultiModelRef(Serializable):
243
318
  )
244
319
  return list(ids)
245
320
 
321
+ @model_validator(mode="before")
322
+ @classmethod
323
+ def extract_params_from_env_var(cls, values: Dict[str, Any]) -> Dict[str, Any]:
324
+ """
325
+ A model-level validator that extracts `PARAMS` from the `env_var` dictionary
326
+ and injects them into the `params` field as a dictionary.
327
+
328
+ This is useful for backward compatibility where users pass CLI-style
329
+ parameters via environment variables, e.g.:
330
+ env_var = { "PARAMS": "--max-model-len 65536 --enable-streaming" }
331
+
332
+ If `params` is already set, values from `PARAMS` in `env_var` are added
333
+ only if they do not override existing keys.
334
+ """
335
+ env = values.get("env_var", {})
336
+ param_string = env.pop("PARAMS", None)
337
+
338
+ if param_string:
339
+ parsed_params = cls._parse_params(params=param_string)
340
+ existing_params = values.get("params", {}) or {}
341
+ # Avoid overriding existing keys
342
+ for k, v in parsed_params.items():
343
+ if k not in existing_params:
344
+ existing_params[k] = v
345
+ values["params"] = existing_params
346
+ values["env_var"] = env # cleaned up version without PARAMS
347
+
348
+ return values
349
+
350
+ @staticmethod
351
+ def _parse_params(params: Union[str, List[str]]) -> Dict[str, str]:
352
+ """
353
+ Parses CLI-style parameters into a dictionary format.
354
+
355
+ This method accepts either:
356
+ - A single string of parameters (e.g., "--key1 val1 --key2 val2")
357
+ - A list of strings (e.g., ["--key1", "val1", "--key2", "val2"])
358
+
359
+ Returns a dictionary of the form { "key1": "val1", "key2": "val2" }.
360
+
361
+ Parameters
362
+ ----------
363
+ params : Union[str, List[str]]
364
+ The parameters to parse. Can be a single string or a list of strings.
365
+
366
+ Returns
367
+ -------
368
+ Dict[str, str]
369
+ Dictionary with parameter names as keys and their corresponding values as strings.
370
+ """
371
+ if not params or not isinstance(params, (str, list)):
372
+ return {}
373
+
374
+ # Normalize string to list of "--key value" strings
375
+ if isinstance(params, str):
376
+ params_list = [
377
+ f"--{param.strip()}" for param in params.split("--") if param.strip()
378
+ ]
379
+ else:
380
+ params_list = params
381
+
382
+ parsed = {}
383
+ for item in params_list:
384
+ parts = item.strip().split()
385
+ if not parts:
386
+ continue
387
+ key = parts[0]
388
+ value = " ".join(parts[1:]) if len(parts) > 1 else ""
389
+ parsed[key] = value
390
+
391
+ return parsed
392
+
246
393
  class Config:
247
- extra = "ignore"
394
+ extra = "allow"
248
395
  protected_namespaces = ()
249
396
 
250
397
 
ads/aqua/common/enums.py CHANGED
@@ -123,6 +123,12 @@ class Platform(ExtendedEnum):
123
123
  # - Key: The preferred container family to use when multiple compatible families are selected.
124
124
  # - Value: A list of all compatible families (including the preferred one).
125
125
  CONTAINER_FAMILY_COMPATIBILITY: Dict[str, List[str]] = {
126
+ InferenceContainerTypeFamily.AQUA_VLLM_OPENAI_CONTAINER_FAMILY: [
127
+ InferenceContainerTypeFamily.AQUA_VLLM_OPENAI_CONTAINER_FAMILY,
128
+ InferenceContainerTypeFamily.AQUA_VLLM_LLAMA4_CONTAINER_FAMILY,
129
+ InferenceContainerTypeFamily.AQUA_VLLM_V1_CONTAINER_FAMILY,
130
+ InferenceContainerTypeFamily.AQUA_VLLM_CONTAINER_FAMILY,
131
+ ],
126
132
  InferenceContainerTypeFamily.AQUA_VLLM_V1_CONTAINER_FAMILY: [
127
133
  InferenceContainerTypeFamily.AQUA_VLLM_V1_CONTAINER_FAMILY,
128
134
  InferenceContainerTypeFamily.AQUA_VLLM_CONTAINER_FAMILY,
ads/aqua/common/errors.py CHANGED
@@ -55,6 +55,11 @@ class AquaValueError(AquaError, ValueError):
55
55
  def __init__(self, reason, status=403, service_payload=None):
56
56
  super().__init__(reason, status, service_payload)
57
57
 
58
+ class AquaRecommendationError(AquaError):
59
+ """Exception raised for models incompatible with shape recommendation tool."""
60
+
61
+ def __init__(self, reason, status=400, service_payload=None):
62
+ super().__init__(reason, status, service_payload)
58
63
 
59
64
  class AquaFileNotFoundError(AquaError, FileNotFoundError):
60
65
  """Exception raised for missing target file."""