Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 180 additions & 1 deletion ads/model/datascience_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
ModelCustomMetadataItem,
ModelProvenanceMetadata,
ModelTaxonomyMetadata,
ModelBackupSetting,
ModelRetentionSetting,
ModelRetentionOperationDetails,
ModelBackupOperationDetails,
)
from ads.model.service.oci_datascience_model import (
ModelProvenanceNotFoundError,
Expand Down Expand Up @@ -120,6 +124,19 @@ class DataScienceModel(Builder):
Model version id
model_file_description: dict
Contains object path details for models created by reference.
backup_setting: ModelBackupSetting
The value to assign to the backup_setting property of this CreateModelDetails.
retention_setting: ModelRetentionSetting
The value to assign to the retention_setting property of this CreateModelDetails.
retention_operation_details: ModelRetentionOperationDetails
The value to assign to the retention_operation_details property for the Model.
backup_operation_details: ModelBackupOperationDetails
The value to assign to the backup_operation_details property for the Model.






Methods
-------
Expand Down Expand Up @@ -178,7 +195,6 @@ class DataScienceModel(Builder):
Sets path details for models created by reference. Input can be either a dict, string or json file and
the schema is dictated by model_file_description_schema.json


Examples
--------
>>> ds_model = (DataScienceModel()
Expand Down Expand Up @@ -217,7 +233,12 @@ class DataScienceModel(Builder):
CONST_MODEL_VERSION_ID = "versionId"
CONST_TIME_CREATED = "timeCreated"
CONST_LIFECYCLE_STATE = "lifecycleState"
CONST_LIFECYCLE_DETAILS = "lifecycleDetails"
CONST_MODEL_FILE_DESCRIPTION = "modelDescription"
CONST_BACKUP_SETTING = "backupSetting"
CONST_RETENTION_SETTING = "retentionSetting"
CONST_BACKUP_OPERATION_DETAILS = "backupOperationDetails"
CONST_RETENTION_OPERATION_DETAILS = "retentionOperationDetails"

attribute_map = {
CONST_ID: "id",
Expand All @@ -239,7 +260,12 @@ class DataScienceModel(Builder):
CONST_MODEL_VERSION_ID: "version_id",
CONST_TIME_CREATED: "time_created",
CONST_LIFECYCLE_STATE: "lifecycle_state",
CONST_LIFECYCLE_DETAILS: "lifecycle_details",
CONST_MODEL_FILE_DESCRIPTION: "model_description",
CONST_BACKUP_SETTING: "backup_setting",
CONST_RETENTION_SETTING: "retention_setting",
CONST_BACKUP_OPERATION_DETAILS: "backup_operation_details",
CONST_RETENTION_OPERATION_DETAILS: "retention_operation_details",
}

def __init__(self, spec: Dict = None, **kwargs) -> None:
Expand Down Expand Up @@ -308,6 +334,28 @@ def lifecycle_state(self) -> Union[str, None]:
return self.dsc_model.status
return None

@property
def lifecycle_details(self) -> str:
"""
Gets the lifecycle_details of this DataScienceModel.
Details about the lifecycle state of the model.

:return: The lifecycle_details of this DataScienceModel.
:rtype: str
"""
return self.get_spec(self.CONST_LIFECYCLE_DETAILS)

@lifecycle_details.setter
def lifecycle_details(self, lifecycle_details: str) -> "DataScienceModel":
"""
Sets the lifecycle_details of this DataScienceModel.
Details about the lifecycle state of the model.

:param lifecycle_details: The lifecycle_details of this DataScienceModel.
:type: str
"""
return self.set_spec(self.CONST_LIFECYCLE_DETAILS, lifecycle_details)

@property
def kind(self) -> str:
"""The kind of the object as showing in a YAML."""
Expand Down Expand Up @@ -685,6 +733,85 @@ def with_model_file_description(

return self.set_spec(self.CONST_MODEL_FILE_DESCRIPTION, json_data)

@property
def retention_setting(self) -> ModelRetentionSetting:
"""
Gets the retention_setting of this model.

:return: The retention_setting of this model.
:rtype: RetentionSetting
"""
return self.get_spec(self.CONST_RETENTION_SETTING)

def with_retention_setting(
self, retention_setting: Union[Dict, ModelRetentionSetting]
) -> "DataScienceModel":
"""
Sets the retention setting details for the model.

Parameters
----------
retention_setting : Union[Dict, RetentionSetting]
The retention setting details for the model. Can be provided as either a dictionary or
an instance of the `RetentionSetting` class.

Returns
-------
DataScienceModel
The `DataScienceModel` instance (self) for method chaining.
"""
return self.set_spec(self.CONST_RETENTION_SETTING, retention_setting)

@property
def backup_setting(self) -> ModelBackupSetting:
"""
Gets the backup_setting of this model.

:return: The backup_setting of this model.
:rtype: BackupSetting
"""
return self.get_spec(self.CONST_BACKUP_SETTING)

def with_backup_setting(
self, backup_setting: Union[Dict, ModelBackupSetting]
) -> "DataScienceModel":
"""
Sets the model's backup setting details.

Parameters
----------
backup_setting : Union[Dict, BackupSetting]
The backup setting details for the model. This can be passed as either a dictionary or
an instance of the `BackupSetting` class.

Returns
-------
DataScienceModel
The `DataScienceModel` instance (self) for method chaining.
"""

return self.set_spec(self.CONST_BACKUP_SETTING, backup_setting)

@property
def retention_operation_details(self) -> ModelRetentionOperationDetails:
"""
Gets the retention_operation_details of this Model using the spec constant.

:return: The retention_operation_details of this Model.
:rtype: ModelRetentionOperationDetails
"""
return self.get_spec(self.CONST_RETENTION_OPERATION_DETAILS)

@property
def backup_operation_details(self) -> "ModelBackupOperationDetails":
"""
Gets the backup_operation_details of this Model using the spec constant.

:return: The backup_operation_details of this Model.
:rtype: ModelBackupOperationDetails
"""
return self.get_spec(self.CONST_BACKUP_OPERATION_DETAILS)

def create(self, **kwargs) -> "DataScienceModel":
"""Creates datascience model.

Expand Down Expand Up @@ -907,6 +1034,49 @@ def _remove_file_description_artifact(self):
if self.local_copy_dir:
shutil.rmtree(self.local_copy_dir, ignore_errors=True)

def restore_model(
self,
model_id: str,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This either should be a class method or model_id parameter is not required. See the update nethod.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this update the case shared below will not work. I would rather prefer to remove the model_id attribute from the function.

This case will not work:

DataScienceModel.from_id("OCID"). restore_model()

It will expect from e to provide OCID one more time,
like this

DataScienceModel.from_id("OCID"). restore_model("OCID")

But why should i do this, if I already provided the OCID before?

The better solution would be

    def restore_model(
            cls,
            restore_model_for_hours_specified: Optional[int] = HOURS_24,
    ):

    if not self.id:
          raise ValueError("Model needs to be saved to the model catalog before it can be updated.")

Or if we really want to support both cases below

DataScienceModel.from_id("OCID"). restore_model()
DataScienceModel.restore_model("OCID")

Then the solution could be

@class_or_instance_method
def restore_model(
            cls,
            model_id: Optional[str] = None,
            restore_model_for_hours_specified: Optional[int] = HOURS_24,
    ):

   if not inspect.isclass(cls): # if the instance used
        pass
   
   # otherwise the class is used
   
   # see GenericModel.update_deployment as example.    

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have updated the method to use self and model_id isn't a required parameter anymore now.
just passing it to restore_archived_model_artifact method here

self.dsc_model.restore_archived_model_artifact(
            model_id=self.id,
            restore_model_for_hours_specified=restore_model_for_hours_specified,
        )

restore_model_for_hours_specified: Optional[int] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHouldn't we have some default value? FOr example 24 hours?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is already handled in backend, if the value passed is null, it defaults it to 24 hours

):
"""
Restore archived model artifact.

Parameters
----------
model_id : str
The `OCID` of the model to be restored.
restore_model_for_hours_specified : Optional[int]
Duration in hours for which the archived model is available for access.

Returns
-------
None

Raises
------
ValueError
If the model ID is invalid or if any parameters are incorrect.
"""
# Validate model_id
if not model_id or not isinstance(model_id, str):
raise ValueError("model_id must be a non-empty string.")

# Optional: Validate restore_model_for_hours_specified
if restore_model_for_hours_specified is not None:
if (
not isinstance(restore_model_for_hours_specified, int)
or restore_model_for_hours_specified <= 0
):
raise ValueError(
"restore_model_for_hours_specified must be a positive integer."
)

self.dsc_model.restore_archived_model_artifact(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be something like this:

self.dsc_model.restore_archived_model_artifact(
            model_id=self.id,
            restore_model_for_hours_specified=restore_model_for_hours_specified,
        )

return self.sync()

check the update method.

Copy link
Member Author

@iamishaan iamishaan Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't return the model here in response. Return type is None. return self.sync() is not needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return self.sync() would get the updated model, in this case user would have the object containing the fresh status and details. Otherwise, after the calling this method user will have an old object.

model_id=model_id,
restore_model_for_hours_specified=restore_model_for_hours_specified,
)

def download_artifact(
self,
target_dir: str,
Expand Down Expand Up @@ -1162,6 +1332,8 @@ def _init_complex_attributes(self):
self.with_provenance_metadata(self.provenance_metadata)
self.with_input_schema(self.input_schema)
self.with_output_schema(self.output_schema)
self.with_backup_setting(self.backup_setting)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably we don't need to add it here. The complex attributes mean that some extra logic is required to initialize these attributes. For example if to check the input schema it has some try/catch mechanism to validate the schema.

self.with_retention_setting(self.retention_setting)

def _to_oci_dsc_model(self, **kwargs):
"""Creates an `OCIDataScienceModel` instance from the `DataScienceModel`.
Expand Down Expand Up @@ -1197,6 +1369,8 @@ def _to_oci_dsc_model(self, **kwargs):
dsc_spec[dsc_attr] = value

dsc_spec.update(**kwargs)
print("Model Dsc spec")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

print(dsc_spec)
return OCIDataScienceModel(**dsc_spec)

def _update_from_oci_dsc_model(
Expand All @@ -1219,10 +1393,15 @@ def _update_from_oci_dsc_model(
self.CONST_OUTPUT_SCHEMA: [Schema.from_json, json.loads],
self.CONST_CUSTOM_METADATA: ModelCustomMetadata._from_oci_metadata,
self.CONST_DEFINED_METADATA: ModelTaxonomyMetadata._from_oci_metadata,
self.CONST_BACKUP_SETTING: ModelBackupSetting.to_dict,
self.CONST_RETENTION_SETTING: ModelRetentionSetting.to_dict,
}

# Update the main properties

self.dsc_model = dsc_model
print("Model Details from here")
print(dsc_model)
for infra_attr, dsc_attr in self.attribute_map.items():
value = utils.get_value(dsc_model, dsc_attr)
if value:
Expand Down
14 changes: 8 additions & 6 deletions ads/model/generic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1422,9 +1422,9 @@ def from_model_artifact(
)
model.update_summary_status(
detail=PREPARE_STATUS_POPULATE_METADATA_DETAIL,
status=ModelState.AVAILABLE.value
if reload
else ModelState.NOTAPPLICABLE.value,
status=(
ModelState.AVAILABLE.value if reload else ModelState.NOTAPPLICABLE.value
),
)

return model
Expand Down Expand Up @@ -1706,9 +1706,11 @@ def from_model_catalog(
)
result_model.update_summary_status(
detail=SAVE_STATUS_INTROSPECT_TEST_DETAIL,
status=ModelState.AVAILABLE.value
if not result_model.ignore_conda_error
else ModelState.NOTAVAILABLE.value,
status=(
ModelState.AVAILABLE.value
if not result_model.ignore_conda_error
else ModelState.NOTAVAILABLE.value
),
)
return result_model

Expand Down
Loading