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
@@ -245,55 +245,71 @@ class AquaMultiModelRef(Serializable):
245
245
"""
246
246
Lightweight model descriptor used for multi-model deployment.
247
247
248
- This class only contains essential details
249
- 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 .
250
250
251
251
Attributes
252
252
----------
253
253
model_id : str
254
- The unique identifier of the model.
254
+ The unique identifier (OCID) of the base model.
255
255
model_name : Optional[str]
256
- The name of the model.
256
+ Optional name for the model.
257
257
gpu_count : Optional[int]
258
- Number of GPUs required for deployment.
258
+ Number of GPUs required to allocate for this model during deployment.
259
259
model_task : Optional[str]
260
- 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`.
261
262
env_var : Optional[Dict[str, Any]]
262
- 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.
263
270
artifact_location : Optional[str]
264
- Artifact path of model in the multimodel group.
271
+ Relative path or URI of the model artifact inside the multi-model group folder .
265
272
fine_tune_weights : Optional[List[LoraModuleSpec]]
266
- 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.
267
274
"""
268
275
269
276
model_id : str = Field (..., description = "The model OCID to deploy." )
270
- model_name : Optional [str ] = Field (None , description = "The name of model." )
277
+ model_name : Optional [str ] = Field (None , description = "The name of the model." )
271
278
gpu_count : Optional [int ] = Field (
272
- None , description = "The gpu count allocation for the model."
279
+ None , description = "The number of GPUs allocated for the model."
273
280
)
274
281
model_task : Optional [str ] = Field (
275
282
None ,
276
- description = "The task that model operates on. Supported tasks are in MultiModelSupportedTaskType " ,
283
+ description = "The task this model performs. See `MultiModelSupportedTaskType` for supported values. " ,
277
284
)
278
285
env_var : Optional [dict ] = Field (
279
- 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
+ ),
280
295
)
281
296
artifact_location : Optional [str ] = Field (
282
- 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." ,
283
299
)
284
300
fine_tune_weights : Optional [List [LoraModuleSpec ]] = Field (
285
301
None ,
286
- 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. " ,
287
303
)
288
304
289
305
def all_model_ids (self ) -> List [str ]:
290
306
"""
291
- 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 .
292
308
293
309
Returns
294
310
-------
295
311
List[str]
296
- 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 .
297
313
"""
298
314
ids = {self .model_id }
299
315
if self .fine_tune_weights :
@@ -302,8 +318,80 @@ def all_model_ids(self) -> List[str]:
302
318
)
303
319
return list (ids )
304
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
+
305
393
class Config :
306
- extra = "ignore "
394
+ extra = "allow "
307
395
protected_namespaces = ()
308
396
309
397
0 commit comments