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.
- ads/aqua/cli.py +7 -5
- ads/aqua/common/entities.py +195 -48
- ads/aqua/common/enums.py +6 -0
- ads/aqua/common/errors.py +5 -0
- ads/aqua/common/utils.py +157 -66
- ads/aqua/constants.py +3 -0
- ads/aqua/extension/deployment_handler.py +36 -0
- ads/aqua/modeldeployment/constants.py +1 -0
- ads/aqua/modeldeployment/deployment.py +95 -14
- ads/aqua/modeldeployment/entities.py +3 -0
- ads/aqua/modeldeployment/model_group_config.py +3 -3
- ads/aqua/resources/gpu_shapes_index.json +315 -26
- ads/aqua/shaperecommend/__init__.py +6 -0
- ads/aqua/shaperecommend/constants.py +116 -0
- ads/aqua/shaperecommend/estimator.py +384 -0
- ads/aqua/shaperecommend/llm_config.py +283 -0
- ads/aqua/shaperecommend/recommend.py +493 -0
- ads/aqua/shaperecommend/shape_report.py +233 -0
- ads/aqua/version.json +1 -1
- ads/cli.py +9 -1
- ads/jobs/builders/infrastructure/dsc_job.py +1 -0
- ads/jobs/builders/infrastructure/dsc_job_runtime.py +9 -1
- ads/model/service/oci_datascience_model_deployment.py +46 -19
- ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +4 -3
- ads/pipeline/ads_pipeline.py +13 -9
- {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/METADATA +1 -1
- {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/RECORD +30 -24
- {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/WHEEL +0 -0
- {oracle_ads-2.13.18rc0.dist-info → oracle_ads-2.13.19.dist-info}/entry_points.txt +0 -0
- {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(
|
112
|
-
|
113
|
-
|
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
|
ads/aqua/common/entities.py
CHANGED
@@ -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
|
50
|
+
class ComputeRank(Serializable):
|
51
51
|
"""
|
52
|
-
Represents the
|
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
|
-
|
56
|
-
|
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,
|
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,
|
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
|
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="
|
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
|
171
|
+
def populate_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
|
111
172
|
"""
|
112
|
-
|
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
|
-
|
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
|
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"
|
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
|
190
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
271
|
+
Relative path or URI of the model artifact inside the multi-model group folder.
|
206
272
|
fine_tune_weights : Optional[List[LoraModuleSpec]]
|
207
|
-
|
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
|
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
|
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,
|
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,
|
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="
|
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
|
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
|
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 = "
|
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."""
|