Automatic Swagger generation for Azure ML endpoints with AML SDK V2

72 Views Asked by At

Referring to example: https://learn.microsoft.com/en-us/azure/machine-learning/how-to-deploy-advanced-entry-script?view=azureml-api-1#automatically-generate-a-swagger-schema

I am looking to enhance my Swagger documentation by including details such as optional inputs for model and example enums. Is this achievable with inference-schema? I haven't come across any examples demonstrating this. When attempting to provide the json_schema with enums and required fields in my request format using the input_schema decorator, it seems that Swagger considers everything as required.

example:

 {
  "ServiceInput": {
    "type": "object",
    "properties": {
      "request_payload": {
        "type": "object",
        "required": [
          "input1",
          "requestTimestamp",
          "requestTimezone"
        ],
        "properties": {
          "input1": {
            "type": "integer",
            "format": "int64"
          },
          "input2": {
            "type": "integer",
            "format": "int64"
          },
          "requestTimestamp": {
            "type": "string"
          },
          "requestTimezone": {
            "type": "string"
          }
        }
      }
    },
    "example": {
      "request_payload": {
        "input1": 200,
        "requestTimestamp": "2023-05-05T12:12:12.000Z",
        "requestTimezone": "Europe/Berlin",
        "input2": 10
      }
    }
  }
}

In above example input2 is optional in the request inputPayload, and i would like to show this on swagger doc (auto generated one).

I am aware that I can pass my own custom swagger specs to the deployment but i was wondering if this can be done automatically with inference-schema while cerating deployment.

That would essentially eliminate the need of manually editing swagger docs.

Any guidance on how to achieve this would be greatly appreciated.

1

There are 1 best solutions below

3
JayashankarGS On

To create an optional parameter, you need to give GlobalParameters of type StandardPythonParameterType to the input_schema decorator.

Below is the sample score.py I am using.

Create a parameter of type StandardPythonParameterType:

method_sample  = StandardPythonParameterType("predict")
sample_global_params  = StandardPythonParameterType({"method": method_sample})

Then, in the decorator, provide it as below:

@input_schema('GlobalParameters', sample_global_params, convert_to_provided_type=False)

Here, GlobalParameters is case-sensitive.

Code:

import json
import logging
import os
import pickle
import numpy as np
import pandas as pd
import joblib

import azureml.automl.core
from azureml.automl.core.shared import logging_utilities, log_server
from azureml.telemetry import INSTRUMENTATION_KEY

from inference_schema.schema_decorators import input_schema, output_schema
from inference_schema.parameter_types.numpy_parameter_type import NumpyParameterType
from inference_schema.parameter_types.pandas_parameter_type import PandasParameterType
from inference_schema.parameter_types.standard_py_parameter_type import StandardPythonParameterType

data_sample = StandardPythonParameterType([1,2])
input_sample = StandardPythonParameterType({'data': data_sample})
method_sample = StandardPythonParameterType("predict")
sample_global_params = StandardPythonParameterType({"method": method_sample})

result_sample = NumpyParameterType(np.array(["example_value"]))
output_sample = StandardPythonParameterType({'Results':result_sample})

try:
    log_server.enable_telemetry(INSTRUMENTATION_KEY)
    log_server.set_verbosity('INFO')
    logger = logging.getLogger('azureml.automl.core.scoring_script_v2')
except:
    pass

def init():
    global model
    # This name is model.id of the model that we want to deploy; deserialize the model file back
    # into a sklearn model
    model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), 'model.pkl')
    path = os.path.normpath(model_path)
    path_split = path.split(os.sep)
    log_server.update_custom_dimensions({'model_name': path_split[-3], 'model_version': path_split[-2]})
    try:
        logger.info("Loading model from path.")
        model = joblib.load(model_path)
        logger.info("Loading successful.")
    except Exception as e:
        logging_utilities.log_traceback(e, logger)
        raise

@input_schema('GlobalParameters', sample_global_params, convert_to_provided_type=False)
@input_schema('Inputs', input_sample)
@output_schema(output_sample)
def run(Inputs, GlobalParameters={"method": "predict"}):
    data = Inputs['data']
    if GlobalParameters.get("method", None) == "predict_proba":
        result = ["Method proba executed",sum(data)]
    elif GlobalParameters.get("method", None) == "predict":
        result = ["Method predict executed",sum(data)]
    else:
        raise Exception(f"Invalid predict method argument received. GlobalParameters: {GlobalParameters}")
    if isinstance(result, pd.DataFrame):
        result = result.values
    return {'Results':result.tolist()}

You can refer to this Stack solution