Skip to content

Commit 2bc9469

Browse files
authored
Feat/deploy arm (#46)
* initial prototype for deploying arm64 models * dev: modified deploy method for ARM-based models and added model metadata function * More explicitly documented architecture types and created constant variables for hardware types * Modified docstring to accurately reflect �rchitecture parameter
1 parent 59b3b17 commit 2bc9469

File tree

1 file changed

+134
-41
lines changed

1 file changed

+134
-41
lines changed

modzy/models.py

Lines changed: 134 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
from time import sleep
1313
from ._util import load_model, upload_input_example, run_model, deploy_model
1414

15+
# define constants used for model deployment method
16+
MODEL_HARDWARE_GPU_ID = -6
17+
MODEL_HARDWARE_ARM_ID = -99
18+
MODEL_HARDWARE_OTHER_ID = 1
1519
class Models:
1620
"""The `Models` object.
1721
@@ -382,8 +386,58 @@ def get_models(self, model_id=None, author=None, created_by_email=None, name=Non
382386
json_list = self._api_client.http.get('{}?{}'.format(self._base_route, urlencode(body)))
383387
return list(Model(json_obj, self._api_client) for json_obj in json_list)
384388

389+
def edit_model_metadata(self, model_id, model_version, long_description=None, technical_details=None,
390+
performance_summary=None, performance_metrics=None, input_details=None, output_details=None):
391+
392+
'''
393+
Edit a model's metadata after it is deployed
394+
395+
Args:
396+
model_id (str): Model identifier of model to edit
397+
model_version (str): Model version of model to edit
398+
long_description (str): Description to appear on model biography page
399+
technical_details (str): Technical details to appear on model biography page. Markdown is accepted
400+
performance_summary (str): Description providing model performance to appear on model biography page
401+
performance_metrics (List): List of arrays describing model performance statistics
402+
input_details (List): List of dictionaries describing details of model inputs
403+
output_details (List): List of dictionaries describing details of model outputs
404+
405+
Returns:
406+
dict: Metadata of newly edited model information including formatted URL to newly deployed model page.
407+
Raises:
408+
ApiError: A subclass of ApiError will be raised if the API returns an error status,
409+
or the client is unable to connect.
410+
'''
411+
412+
# validate model version exists
413+
try:
414+
json_obj = self._api_client.http.get('{}/{}/versions/{}'.format(self._base_route, model_id, model_version))
415+
_ = ModelVersion(json_obj, self._api_client)
416+
except NotFoundError as e:
417+
raise e
418+
419+
# update model metadata
420+
model_metadata_patch = {
421+
"inputs": input_details,
422+
"outputs": output_details,
423+
"statistics": performance_metrics,
424+
"longDescription": long_description,
425+
"technicalDetails": technical_details,
426+
"performanceSummary": performance_summary
427+
}
428+
model_data_patch = self._api_client.http.patch(f"{self._base_route}/{model_id}/versions/{model_version}", model_metadata_patch)
429+
self.logger.info(f"Patched Model Data: {json.dumps(model_data_patch)}")
430+
431+
# get edited model URL and return model data
432+
base_url = self._api_client.base_url.split("api")[0][:-1]
433+
container_data = {
434+
'model_data': json.dumps(model_data_patch),
435+
'container_url': f"{base_url}{self._base_route}/{model_id}/{model_version}"
436+
}
437+
return container_data
438+
385439
def deploy(
386-
self, container_image, model_name, model_version, sample_input_file, credentials=None,
440+
self, container_image, model_name, model_version, sample_input_file=None, architecture="amd64", credentials=None,
387441
model_id=None, run_timeout=None, status_timeout=None, short_description=None, tags=[],
388442
gpu=False, long_description=None, technical_details=None, performance_summary=None,
389443
performance_metrics=None, input_details=None, output_details=None
@@ -394,6 +448,7 @@ def deploy(
394448
container_image (str): Docker container image to be deployed. This string should represent what follows a `docker pull` command
395449
model_name (str): Name of model to be deployed
396450
model_version (str): Version of model to be deployed
451+
architecture (str): `{'amd64', 'arm64', 'arm'}` If set to `arm64` or `arm`, deploy method will expedite the deployment process and bypass some Modzy tests that are only available for models compiled for amd64 chips.
397452
sample_input_file (str): Path to local file to be used for sample inference
398453
credentials (dict): Dictionary containing credentials if the container image is private. The keys in this dictionary must be `["user", "pass"]`
399454
model_id (str): Model identifier if deploying a new version to a model that already exists
@@ -454,47 +509,85 @@ def deploy(
454509
run_timeout_body = int(run_timeout)*1000 if run_timeout else 60000
455510
status_timeout_body = int(status_timeout)*1000 if status_timeout else 60000
456511

457-
model_metadata = {
458-
"requirement": {"requirementId": -6 if gpu else 1},
459-
"timeout": {
460-
"run": run_timeout_body,
461-
"status": status_timeout_body
462-
},
463-
"inputs": input_details or self.default_inputs,
464-
"outputs": output_details or self.default_outputs,
465-
"statistics": performance_metrics or [],
466-
"processing": {
467-
"minimumParallelCapacity": 0,
468-
"maximumParallelCapacity": 1
469-
},
470-
"longDescription": long_description or "",
471-
"technicalDetails": technical_details or "",
472-
"performanceSummary": performance_summary or ""
473-
}
474-
model_data = self._api_client.http.patch(f"{self._base_route}/{identifier}/versions/{version}", model_metadata)
475-
self.logger.info(f"Model Data: {json.dumps(model_data)}")
512+
if architecture in ["arm64", "arm"]:
513+
# assign "ARM" hardware requirement to bypass validation tests
514+
model_metadata = {
515+
"requirement": {"requirementId": MODEL_HARDWARE_ARM_ID},
516+
}
517+
model_data = self._api_client.http.patch(f"{self._base_route}/{identifier}/versions/{version}", model_metadata)
518+
self.logger.info(f"Model Data: {json.dumps(model_data)}")
476519

477-
# load model container
478-
try:
479-
load_model(self._api_client, self.logger, identifier, version)
480-
except Exception as e:
481-
raise ValueError("Loading model container failed. Make sure you passed through a valid Docker registry container image. \n\nSee full error below:\n{}".format(e))
482-
# upload sample data for inference test
483-
try:
484-
upload_input_example(self._api_client, self.logger, identifier, version, model_data, sample_input_file)
485-
except Exception as e:
486-
raise ValueError("Uploading sample input failed. \n\nSee full error below:\n{}".format(e))
487-
# run sample inference
488-
try:
489-
run_model(self._api_client, self.logger, identifier, version)
490-
except Exception as e:
491-
raise ValueError("Inference test failed. Make sure the provided input sample is valid and your model can process it for inference. \n\nSee full error below:\n{}".format(e))
492-
# deploy model pending all tests have passed
493-
try:
494-
deploy_model(self._api_client, self.logger, identifier, version)
495-
except Exception as e:
496-
raise ValueError("Deployment failed. Check to make sure all of your parameters and assets are valid and try again. \n\nSee full error below:\n{}".format(e))
497-
520+
# load model container
521+
try:
522+
load_model(self._api_client, self.logger, identifier, version)
523+
except Exception as e:
524+
raise ValueError("Loading model container failed. Make sure you passed through a valid Docker registry container image. \n\nSee full error below:\n{}".format(e))
525+
526+
# update model metadata
527+
model_metadata_patch = {
528+
"inputs": input_details,
529+
"outputs": output_details,
530+
"statistics": performance_metrics or [],
531+
"processing": {
532+
"minimumParallelCapacity": 0,
533+
"maximumParallelCapacity": 1
534+
},
535+
"longDescription": long_description or "",
536+
"technicalDetails": technical_details or "",
537+
"performanceSummary": performance_summary or ""
538+
}
539+
model_data = self._api_client.http.patch(f"{self._base_route}/{identifier}/versions/{version}", model_metadata_patch)
540+
self.logger.info(f"Patched Model Data: {json.dumps(model_data)}")
541+
542+
# deploy model and skip tests (because model is compiled for arm64)
543+
try:
544+
deploy_model(self._api_client, self.logger, identifier, version)
545+
except Exception as e:
546+
raise ValueError("Deployment failed. Check to make sure all of your parameters and assets are valid and try again. \n\nSee full error below:\n{}".format(e))
547+
elif architecture=="amd64":
548+
model_metadata = {
549+
"requirement": {"requirementId": MODEL_HARDWARE_GPU_ID if gpu else MODEL_HARDWARE_OTHER_ID},
550+
"timeout": {
551+
"run": run_timeout_body,
552+
"status": status_timeout_body
553+
},
554+
"inputs": input_details or self.default_inputs,
555+
"outputs": output_details or self.default_outputs,
556+
"statistics": performance_metrics or [],
557+
"processing": {
558+
"minimumParallelCapacity": 0,
559+
"maximumParallelCapacity": 1
560+
},
561+
"longDescription": long_description or "",
562+
"technicalDetails": technical_details or "",
563+
"performanceSummary": performance_summary or ""
564+
}
565+
model_data = self._api_client.http.patch(f"{self._base_route}/{identifier}/versions/{version}", model_metadata)
566+
self.logger.info(f"Model Data: {json.dumps(model_data)}")
567+
568+
# load model container
569+
try:
570+
load_model(self._api_client, self.logger, identifier, version)
571+
except Exception as e:
572+
raise ValueError("Loading model container failed. Make sure you passed through a valid Docker registry container image. \n\nSee full error below:\n{}".format(e))
573+
# upload sample data for inference test
574+
try:
575+
upload_input_example(self._api_client, self.logger, identifier, version, model_data, sample_input_file)
576+
except Exception as e:
577+
raise ValueError("Uploading sample input failed. \n\nSee full error below:\n{}".format(e))
578+
# run sample inference
579+
try:
580+
run_model(self._api_client, self.logger, identifier, version)
581+
except Exception as e:
582+
raise ValueError("Inference test failed. Make sure the provided input sample is valid and your model can process it for inference. \n\nSee full error below:\n{}".format(e))
583+
# deploy model pending all tests have passed
584+
try:
585+
deploy_model(self._api_client, self.logger, identifier, version)
586+
except Exception as e:
587+
raise ValueError("Deployment failed. Check to make sure all of your parameters and assets are valid and try again. \n\nSee full error below:\n{}".format(e))
588+
else:
589+
raise ValueError("Invalid value for `architecture` parameter. Choose option from array: {'amd', 'arm'}")
590+
498591
# get new model URL and return model data
499592
base_url = self._api_client.base_url.split("api")[0][:-1]
500593
container_data = {

0 commit comments

Comments
 (0)