Skip to content

Commit 4776bd8

Browse files
committed
Merge branch 'main' into ODSC-72395/SMC-config-updates
2 parents 72fb3c0 + 60b0693 commit 4776bd8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+5572
-459
lines changed

ads/aqua/cli.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,20 @@ def _validate_value(flag, value):
9696
"If you intend to chain a function call to the result, please separate the "
9797
"flag and the subsequent function call with separator `-`."
9898
)
99-
99+
100100
@staticmethod
101101
def install():
102102
"""Install ADS Aqua Extension from wheel file. Set enviroment variable `AQUA_EXTENSTION_PATH` to change the wheel file path.
103103
104-
Return
104+
Return
105105
------
106106
int:
107107
Installatation status.
108108
"""
109109
import subprocess
110110

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

ads/aqua/client/client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,19 @@ def embeddings(
582582
payload = {**(payload or {}), "input": input}
583583
return self._request(payload=payload, headers=headers)
584584

585+
def fetch_data(self) -> Union[Dict[str, Any], Iterator[Mapping[str, Any]]]:
586+
"""Fetch Data in json format by sending a request to the endpoint.
587+
588+
Args:
589+
590+
Returns:
591+
Union[Dict[str, Any], Iterator[Mapping[str, Any]]]: The server's response, typically including the data in JSON format.
592+
"""
593+
# headers = {"Content-Type", "application/json"}
594+
response = self._client.get(self.endpoint)
595+
json_response = response.json()
596+
return json_response
597+
585598

586599
class AsyncClient(BaseClient):
587600
"""

ads/aqua/common/entities.py

Lines changed: 195 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
44

55
import re
6-
from typing import Any, Dict, List, Optional
6+
from typing import Any, Dict, List, Optional, Union
77

88
from oci.data_science.models import Model
99
from pydantic import BaseModel, ConfigDict, Field, model_validator
@@ -47,19 +47,76 @@ class Config:
4747
protected_namespaces = ()
4848

4949

50-
class GPUSpecs(Serializable):
50+
class ComputeRank(Serializable):
5151
"""
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.
5355
"""
5456

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+
),
5773
)
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+
5883
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)."
6090
)
91+
6192
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+
),
63120
)
64121

65122

@@ -80,46 +137,49 @@ class GPUShapesIndex(Serializable):
80137

81138
class ComputeShapeSummary(Serializable):
82139
"""
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.
85141
"""
86142

143+
available: Optional[bool] = Field(
144+
default=False,
145+
description="True if the shape is available in the user's tenancy/region.",
146+
)
147+
87148
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."
90150
)
151+
91152
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)."
94154
)
155+
95156
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'."
98158
)
159+
99160
shape_series: Optional[str] = Field(
100161
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'.",
102163
)
164+
103165
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."
106167
)
107168

108169
@model_validator(mode="after")
109170
@classmethod
110-
def set_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
171+
def populate_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
111172
"""
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.
118174
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.
120180
121181
Returns:
122-
ComputeShapeSummary: The updated instance with gpu_specs populated if applicable.
182+
ComputeShapeSummary: The updated model instance.
123183
"""
124184
try:
125185
if (
@@ -128,16 +188,15 @@ def set_gpu_specs(cls, model: "ComputeShapeSummary") -> "ComputeShapeSummary":
128188
and model.name
129189
and not model.gpu_specs
130190
):
131-
# Try to extract gpu_count from the shape name using a regex (e.g., "VM.GPU3.2" -> gpu_count=2)
132191
match = re.search(r"\.(\d+)$", model.name)
133192
if match:
134193
gpu_count = int(match.group(1))
135194
model.gpu_specs = GPUSpecs(gpu_count=gpu_count)
136195
except Exception as err:
137196
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}"
140198
)
199+
141200
return model
142201

143202

@@ -186,55 +245,71 @@ class AquaMultiModelRef(Serializable):
186245
"""
187246
Lightweight model descriptor used for multi-model deployment.
188247
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.
191250
192251
Attributes
193252
----------
194253
model_id : str
195-
The unique identifier of the model.
254+
The unique identifier (OCID) of the base model.
196255
model_name : Optional[str]
197-
The name of the model.
256+
Optional name for the model.
198257
gpu_count : Optional[int]
199-
Number of GPUs required for deployment.
258+
Number of GPUs required to allocate for this model during deployment.
200259
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`.
202262
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.
204270
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.
206272
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.
208274
"""
209275

210276
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.")
212278
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."
214280
)
215281
model_task: Optional[str] = Field(
216282
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.",
218284
)
219285
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+
),
221295
)
222296
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.",
224299
)
225300
fine_tune_weights: Optional[List[LoraModuleSpec]] = Field(
226301
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.",
228303
)
229304

230305
def all_model_ids(self) -> List[str]:
231306
"""
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.
233308
234309
Returns
235310
-------
236311
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.
238313
"""
239314
ids = {self.model_id}
240315
if self.fine_tune_weights:
@@ -243,8 +318,80 @@ def all_model_ids(self) -> List[str]:
243318
)
244319
return list(ids)
245320

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+
246393
class Config:
247-
extra = "ignore"
394+
extra = "allow"
248395
protected_namespaces = ()
249396

250397

0 commit comments

Comments
 (0)