oracle-ads 2.13.11__py3-none-any.whl → 2.13.12__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 +13 -7
- ads/aqua/cli.py +15 -0
- ads/aqua/common/entities.py +31 -5
- ads/aqua/common/utils.py +35 -0
- ads/aqua/evaluation/evaluation.py +5 -4
- ads/aqua/extension/model_handler.py +1 -1
- ads/aqua/model/enums.py +19 -1
- ads/aqua/model/model.py +45 -36
- ads/aqua/model/utils.py +1 -2
- ads/aqua/modeldeployment/config_loader.py +815 -0
- ads/aqua/modeldeployment/constants.py +4 -1
- ads/aqua/modeldeployment/deployment.py +100 -124
- ads/aqua/modeldeployment/entities.py +4 -178
- ads/aqua/modeldeployment/model_group_config.py +240 -0
- ads/aqua/modeldeployment/utils.py +0 -539
- ads/common/work_request.py +39 -38
- ads/jobs/builders/infrastructure/dsc_job.py +121 -24
- ads/jobs/builders/infrastructure/dsc_job_runtime.py +71 -24
- ads/jobs/builders/runtimes/base.py +7 -5
- ads/jobs/builders/runtimes/pytorch_runtime.py +6 -8
- ads/jobs/templates/driver_pytorch.py +486 -172
- ads/jobs/templates/driver_utils.py +27 -11
- ads/model/service/oci_datascience_model_deployment.py +6 -11
- ads/telemetry/client.py +4 -4
- {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.12.dist-info}/METADATA +1 -1
- {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.12.dist-info}/RECORD +29 -27
- {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.12.dist-info}/WHEEL +0 -0
- {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.12.dist-info}/entry_points.txt +0 -0
- {oracle_ads-2.13.11.dist-info → oracle_ads-2.13.12.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,240 @@
|
|
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
|
+
|
5
|
+
from typing import List, Optional, Tuple, Union
|
6
|
+
|
7
|
+
from pydantic import BaseModel, Field, field_validator
|
8
|
+
from typing_extensions import Self
|
9
|
+
|
10
|
+
from ads.aqua import logger
|
11
|
+
from ads.aqua.common.entities import AquaMultiModelRef
|
12
|
+
from ads.aqua.common.errors import AquaValueError
|
13
|
+
from ads.aqua.common.utils import (
|
14
|
+
build_params_string,
|
15
|
+
find_restricted_params,
|
16
|
+
get_combined_params,
|
17
|
+
get_container_params_type,
|
18
|
+
get_params_dict,
|
19
|
+
)
|
20
|
+
from ads.aqua.config.utils.serializer import Serializable
|
21
|
+
from ads.aqua.modeldeployment.config_loader import (
|
22
|
+
AquaDeploymentConfig,
|
23
|
+
ConfigurationItem,
|
24
|
+
ModelDeploymentConfigSummary,
|
25
|
+
)
|
26
|
+
from ads.aqua.modeldeployment.entities import CreateModelDeploymentDetails
|
27
|
+
from ads.common.object_storage_details import ObjectStorageDetails
|
28
|
+
from ads.common.utils import UNKNOWN
|
29
|
+
|
30
|
+
__all__ = ["ModelGroupConfig", "BaseModelSpec"]
|
31
|
+
|
32
|
+
from ads.aqua.common.entities import LoraModuleSpec
|
33
|
+
|
34
|
+
|
35
|
+
class BaseModelSpec(BaseModel):
|
36
|
+
"""
|
37
|
+
Defines configuration for a single base model in multi-model deployment.
|
38
|
+
|
39
|
+
Attributes
|
40
|
+
----------
|
41
|
+
model_id: str
|
42
|
+
The OCID of the base model.
|
43
|
+
model_path : str
|
44
|
+
Path to the model in OCI Object Storage.
|
45
|
+
params : str
|
46
|
+
Additional vLLM launch parameters for this model (e.g. parallelism, max context).
|
47
|
+
model_task : str, optional
|
48
|
+
Model task type (e.g., text-generation, image-to-text).
|
49
|
+
fine_tune_weights : List[List[LoraModuleSpec]], optional
|
50
|
+
List of associated LoRA modules for fine-tuned models.
|
51
|
+
"""
|
52
|
+
|
53
|
+
model_id: str = Field(..., description="The base model OCID.")
|
54
|
+
model_path: str = Field(..., description="Path to the base model.")
|
55
|
+
params: str = Field(..., description="Startup parameters passed to vLLM.")
|
56
|
+
model_task: Optional[str] = Field(
|
57
|
+
..., description="Task type the model is intended for."
|
58
|
+
)
|
59
|
+
fine_tune_weights: Optional[List[LoraModuleSpec]] = Field(
|
60
|
+
default_factory=list,
|
61
|
+
description="Optional list of fine-tuned model variants associated with this base model.",
|
62
|
+
)
|
63
|
+
|
64
|
+
@field_validator("model_path")
|
65
|
+
@classmethod
|
66
|
+
def clean_model_path(cls, artifact_path_prefix: str) -> str:
|
67
|
+
"""Validates and cleans the file path for model_path parameter."""
|
68
|
+
if ObjectStorageDetails.is_oci_path(artifact_path_prefix):
|
69
|
+
os_path = ObjectStorageDetails.from_path(artifact_path_prefix)
|
70
|
+
artifact_path_prefix = os_path.filepath.rstrip("/")
|
71
|
+
return artifact_path_prefix
|
72
|
+
|
73
|
+
raise AquaValueError(
|
74
|
+
"The base model path is not available in the model artifact."
|
75
|
+
)
|
76
|
+
|
77
|
+
@classmethod
|
78
|
+
def dedup_lora_modules(cls, fine_tune_weights: List[LoraModuleSpec]):
|
79
|
+
"""Removes duplicate LoRA Modules (duplicate model_names in fine_tune_weights)"""
|
80
|
+
seen = set()
|
81
|
+
unique_modules: List[LoraModuleSpec] = []
|
82
|
+
|
83
|
+
for module in fine_tune_weights or []:
|
84
|
+
name = getattr(module, "model_name", None)
|
85
|
+
if not name:
|
86
|
+
logger.warning("Fine-tuned model in AquaMultiModelRef is missing model_name.")
|
87
|
+
continue
|
88
|
+
if name in seen:
|
89
|
+
logger.warning(f"Duplicate LoRA Module detected: {name!r} (skipping duplicate).")
|
90
|
+
continue
|
91
|
+
seen.add(name)
|
92
|
+
unique_modules.append(module)
|
93
|
+
|
94
|
+
return unique_modules
|
95
|
+
|
96
|
+
@classmethod
|
97
|
+
def from_aqua_multi_model_ref(
|
98
|
+
cls, model: AquaMultiModelRef, model_params: str
|
99
|
+
) -> Self:
|
100
|
+
"""Converts AquaMultiModelRef to BaseModelSpec. Fields are validated using @field_validator methods above."""
|
101
|
+
|
102
|
+
return cls(
|
103
|
+
model_id=model.model_id,
|
104
|
+
model_path=model.artifact_location,
|
105
|
+
params=model_params,
|
106
|
+
model_task=model.model_task,
|
107
|
+
fine_tune_weights=cls.dedup_lora_modules(model.fine_tune_weights),
|
108
|
+
)
|
109
|
+
|
110
|
+
|
111
|
+
class ModelGroupConfig(Serializable):
|
112
|
+
"""
|
113
|
+
Schema representing the metadata passed via MULTI_MODEL_CONFIG for multi-model deployments.
|
114
|
+
|
115
|
+
Attributes
|
116
|
+
----------
|
117
|
+
models : List[BaseModelConfig]
|
118
|
+
List of base models (with optional fine-tune weights) to be served.
|
119
|
+
"""
|
120
|
+
|
121
|
+
models: List[BaseModelSpec] = Field(
|
122
|
+
..., description="List of models in the multi-model deployment."
|
123
|
+
)
|
124
|
+
|
125
|
+
@staticmethod
|
126
|
+
def _extract_model_params(
|
127
|
+
model: AquaMultiModelRef,
|
128
|
+
container_params: Union[str, List[str]],
|
129
|
+
container_type_key: str,
|
130
|
+
) -> Tuple[str, str]:
|
131
|
+
"""
|
132
|
+
Validates if user-provided parameters override pre-set parameters by AQUA.
|
133
|
+
Updates model name and TP size parameters to user-provided parameters.
|
134
|
+
"""
|
135
|
+
user_params = build_params_string(model.env_var)
|
136
|
+
if user_params:
|
137
|
+
restricted_params = find_restricted_params(
|
138
|
+
container_params, user_params, container_type_key
|
139
|
+
)
|
140
|
+
if restricted_params:
|
141
|
+
selected_model = model.model_name or model.model_id
|
142
|
+
raise AquaValueError(
|
143
|
+
f"Parameters {restricted_params} are set by Aqua "
|
144
|
+
f"and cannot be overridden or are invalid."
|
145
|
+
f"Select other parameters for model {selected_model}."
|
146
|
+
)
|
147
|
+
|
148
|
+
# replaces `--served-model-name`` with user's model name
|
149
|
+
container_params_dict = get_params_dict(container_params)
|
150
|
+
container_params_dict.update({"--served-model-name": model.model_name})
|
151
|
+
# replaces `--tensor-parallel-size` with model gpu count
|
152
|
+
container_params_dict.update({"--tensor-parallel-size": model.gpu_count})
|
153
|
+
params = build_params_string(container_params_dict)
|
154
|
+
|
155
|
+
return user_params, params
|
156
|
+
|
157
|
+
@staticmethod
|
158
|
+
def _merge_gpu_count_params(
|
159
|
+
model: AquaMultiModelRef,
|
160
|
+
model_config_summary: ModelDeploymentConfigSummary,
|
161
|
+
create_deployment_details: CreateModelDeploymentDetails,
|
162
|
+
container_type_key: str,
|
163
|
+
container_params,
|
164
|
+
):
|
165
|
+
"""Finds the corresponding deployment parameters based on the GPU count
|
166
|
+
and combines them with user's parameters. Existing deployment parameters
|
167
|
+
will be overriden by user's parameters."""
|
168
|
+
user_params, params = ModelGroupConfig._extract_model_params(
|
169
|
+
model, container_params, container_type_key
|
170
|
+
)
|
171
|
+
|
172
|
+
model_id = (
|
173
|
+
model.fine_tune_weights[0].model_id
|
174
|
+
if model.fine_tune_weights
|
175
|
+
else model.model_id
|
176
|
+
)
|
177
|
+
|
178
|
+
deployment_config = model_config_summary.deployment_config.get(
|
179
|
+
model_id, AquaDeploymentConfig()
|
180
|
+
).configuration.get(
|
181
|
+
create_deployment_details.instance_shape, ConfigurationItem()
|
182
|
+
)
|
183
|
+
params_found = False
|
184
|
+
for item in deployment_config.multi_model_deployment:
|
185
|
+
if model.gpu_count and item.gpu_count and item.gpu_count == model.gpu_count:
|
186
|
+
config_parameters = item.parameters.get(
|
187
|
+
get_container_params_type(container_type_key), UNKNOWN
|
188
|
+
)
|
189
|
+
params = f"{params} {get_combined_params(config_parameters, user_params)}".strip()
|
190
|
+
params_found = True
|
191
|
+
break
|
192
|
+
|
193
|
+
if not params_found and deployment_config.parameters:
|
194
|
+
config_parameters = deployment_config.parameters.get(
|
195
|
+
get_container_params_type(container_type_key), UNKNOWN
|
196
|
+
)
|
197
|
+
params = f"{params} {get_combined_params(config_parameters, user_params)}".strip()
|
198
|
+
params_found = True
|
199
|
+
|
200
|
+
# if no config parameters found, append user parameters directly.
|
201
|
+
if not params_found:
|
202
|
+
params = f"{params} {user_params}".strip()
|
203
|
+
|
204
|
+
return params
|
205
|
+
|
206
|
+
@classmethod
|
207
|
+
def from_create_model_deployment_details(
|
208
|
+
cls,
|
209
|
+
create_deployment_details: CreateModelDeploymentDetails,
|
210
|
+
model_config_summary: ModelDeploymentConfigSummary,
|
211
|
+
container_type_key,
|
212
|
+
container_params,
|
213
|
+
) -> Self:
|
214
|
+
"""
|
215
|
+
Converts CreateModelDeploymentDetail to ModelGroupConfig.
|
216
|
+
CreateModelDeploymentDetail represents user-provided parameters and models within a multi-model group after model artifact is created.
|
217
|
+
ModelGroupConfig is the Pydantic representation of MULTI_MODEL_CONFIG environment variable during model deployment.
|
218
|
+
"""
|
219
|
+
models = []
|
220
|
+
seen_models = set()
|
221
|
+
for model in create_deployment_details.models:
|
222
|
+
params = ModelGroupConfig._merge_gpu_count_params(
|
223
|
+
model,
|
224
|
+
model_config_summary,
|
225
|
+
create_deployment_details,
|
226
|
+
container_type_key,
|
227
|
+
container_params,
|
228
|
+
)
|
229
|
+
|
230
|
+
if model.model_name not in seen_models:
|
231
|
+
seen_models.add(model.model_name)
|
232
|
+
base_model_spec = BaseModelSpec.from_aqua_multi_model_ref(model, params)
|
233
|
+
models.append(base_model_spec)
|
234
|
+
else:
|
235
|
+
raise AquaValueError(
|
236
|
+
f"Duplicate model name ‘{model.model_name}’ detected in multi-model group. "
|
237
|
+
"Each base model must have a unique `model_name`. "
|
238
|
+
"Please remove or rename the duplicate model and register the model group again."
|
239
|
+
)
|
240
|
+
return cls(models=models)
|