Beispiel #1
0
def index():
    #try:
    content = request.get_json(force=True)

    requestPart = dict_digger.dig(content, 'request')
    req_elements = None
    if not requestPart is None:
        requestCopy = requestPart.copy()
        if "date" in requestCopy:
            del requestCopy["date"]
        requestMsg = json_to_seldon_message(requestCopy)
        (req_features, _, req_datadef,
         req_datatype) = extract_request_parts(requestMsg)
        req_elements = createElelmentsArray(req_features,
                                            list(req_datadef.names))

    responsePart = dict_digger.dig(content, 'response')
    res_elements = None
    if not responsePart is None:
        responseCopy = responsePart.copy()
        if "date" in responseCopy:
            del responseCopy["date"]
        responseMsg = json_to_seldon_message(responseCopy)
        (res_features, _, res_datadef,
         res_datatype) = extract_request_parts(responseMsg)
        res_elements = createElelmentsArray(res_features,
                                            list(res_datadef.names))

    if not req_elements is None and not res_elements is None:
        for i, (a, b) in enumerate(zip(req_elements, res_elements)):
            merged = {**a, **b}
            content["elements"] = merged
            reqJson = extractRow(i, requestMsg, req_datatype, req_features,
                                 req_datadef)
            resJson = extractRow(i, responseMsg, res_datatype, res_features,
                                 res_datadef)
            content["request"] = reqJson
            content["response"] = resJson
            #log formatted json to stdout for fluentd collection
            print(str(json.dumps(content)))
    elif not req_elements is None:
        for i, e in enumerate(req_elements):
            content["elements"] = e
            reqJson = extractRow(i, requestMsg, req_datatype, req_features,
                                 req_datadef)
            content["request"] = reqJson
            print(str(json.dumps(content)))
    elif not res_elements is None:
        for i, e in enumerate(res_elements):
            content["elements"] = e
            resJson = extractRow(i, responseMsg, res_datatype, res_features,
                                 res_datadef)
            content["response"] = resJson
            print(str(json.dumps(content)))
    else:
        print(str(json.dumps(content)))

    sys.stdout.flush()

    return str(content)
Beispiel #2
0
def process_content(content):

    if content is None:
        return content

    #no transformation using strData
    if "strData" in content:
        content["dataType"] = "text"
        return content

    #if we have dataType then have already parsed before
    if "dataType" in content:
        print('dataType already in content')
        sys.stdout.flush()
        return content

    requestCopy = content.copy()
    if "date" in requestCopy:
        del requestCopy["date"]
    requestMsg = json_to_seldon_message(requestCopy)
    (req_features, _, req_datadef, req_datatype) = extract_request_parts(requestMsg)
    elements = createElelmentsArray(req_features, list(req_datadef.names))
    for i, e in enumerate(elements):
        reqJson = extractRow(i, requestMsg, req_datatype, req_features, req_datadef)
        reqJson["elements"] = e
        content = reqJson

    return content
def route(
    user_model: Any, request: Union[prediction_pb2.SeldonMessage, List, Dict]
) -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """

    Parameters
    ----------
    user_model
       A Seldon user model
    request
       A SelodonMessage proto
    Returns
    -------

    """
    is_proto = isinstance(request, prediction_pb2.SeldonMessage)

    if hasattr(user_model, "route_rest"):
        logger.warning("route_rest is deprecated. Please use route_raw")
        return user_model.route_rest(request)
    elif hasattr(user_model, "route_grpc"):
        logger.warning("route_grpc is deprecated. Please use route_raw")
        return user_model.route_grpc(request)
    else:
        if hasattr(user_model, "route_raw"):
            try:
                return user_model.route_raw(request)
            except SeldonNotImplementedError:
                pass

        if is_proto:
            (features, meta, datadef,
             data_type) = extract_request_parts(request)
            client_response = client_route(user_model,
                                           features,
                                           datadef.names,
                                           meta=meta)
            if not isinstance(client_response, int):
                raise SeldonMicroserviceException(
                    "Routing response must be int but got " +
                    str(client_response))
            client_response_arr = np.array([[client_response]])
            return construct_response(user_model, False, request,
                                      client_response_arr)
        else:
            (features, meta, datadef,
             data_type) = extract_request_parts_json(request)
            class_names = datadef[
                "names"] if datadef and "names" in datadef else []
            client_response = client_route(user_model,
                                           features,
                                           class_names,
                                           meta=meta)
            if not isinstance(client_response, int):
                raise SeldonMicroserviceException(
                    "Routing response must be int but got " +
                    str(client_response))
            client_response_arr = np.array([[client_response]])
            return construct_response_json(user_model, False, request,
                                           client_response_arr)
Beispiel #4
0
def test_json_to_seldon_message_str_data():
    data = {"strData": "my string data"}
    requestProto = scu.json_to_seldon_message(data)
    assert len(requestProto.data.tensor.values) == 0
    assert requestProto.WhichOneof("data_oneof") == "strData"
    (arr, meta, datadef, _) = scu.extract_request_parts(requestProto)
    assert not isinstance(arr, np.ndarray)
    assert arr == "my string data"
Beispiel #5
0
def test_json_to_seldon_message_json_data():
    data = {"jsonData": {"some": "value"}}
    requestProto = scu.json_to_seldon_message(data)
    assert len(requestProto.data.tensor.values) == 0
    assert requestProto.WhichOneof("data_oneof") == "jsonData"
    (json_data, meta, datadef, _) = scu.extract_request_parts(requestProto)
    assert not isinstance(json_data, np.ndarray)
    assert json_data == {"some": "value"}
def transform_output(
        user_model: Any,
        request: Union[prediction_pb2.SeldonMessage, List, Dict]) \
        -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """

    Parameters
    ----------
    user_model
       User defined class to handle transform input
    request
       The incoming request

    Returns
    -------
       The transformed request

    """
    is_proto = isinstance(request, prediction_pb2.SeldonMessage)

    if hasattr(user_model, "transform_output_rest"):
        logger.warning(
            "transform_input_rest is deprecated. Please use transform_input_raw"
        )
        return user_model.transform_output_rest(request)
    elif hasattr(user_model, "transform_output_grpc"):
        logger.warning(
            "transform_input_grpc is deprecated. Please use transform_input_raw"
        )
        return user_model.transform_output_grpc(request)
    else:
        if hasattr(user_model, "transform_output_raw"):
            try:
                return user_model.transform_output_raw(request)
            except SeldonNotImplementedError:
                pass

        if is_proto:
            (features, meta, datadef,
             data_type) = extract_request_parts(request)
            client_response = client_transform_output(user_model,
                                                      features,
                                                      datadef.names,
                                                      meta=meta)
            return construct_response(user_model, False, request,
                                      client_response)
        else:
            (features, meta, datadef,
             data_type) = extract_request_parts_json(request)
            class_names = datadef[
                "names"] if datadef and "names" in datadef else []
            client_response = client_transform_output(user_model,
                                                      features,
                                                      class_names,
                                                      meta=meta)
            return construct_response_json(user_model, False, request,
                                           client_response)
Beispiel #7
0
def test_json_to_seldon_message_ndarray():
    data = {"data": {"ndarray": [[1]]}}
    requestProto = scu.json_to_seldon_message(data)
    assert requestProto.data.ndarray[0][0] == 1
    (arr, meta, datadef, _) = scu.extract_request_parts(requestProto)
    assert isinstance(arr, np.ndarray)
    assert arr.shape[0] == 1
    assert arr.shape[1] == 1
    assert arr[0][0] == 1
Beispiel #8
0
def test_json_to_seldon_message_bin_data():
    a = np.array([1, 2, 3])
    serialized = pickle.dumps(a)
    bdata_base64 = base64.b64encode(serialized).decode('utf-8')
    data = {"binData": bdata_base64}
    requestProto = scu.json_to_seldon_message(data)
    assert len(requestProto.data.tensor.values) == 0
    assert requestProto.WhichOneof("data_oneof") == "binData"
    assert len(requestProto.binData) > 0
    (arr, meta, datadef, _) = scu.extract_request_parts(requestProto)
    assert not isinstance(arr, np.ndarray)
    assert arr == serialized
Beispiel #9
0
def test_json_to_seldon_message_normal_data():
    data = {"data": {"tensor": {"shape": [1, 1], "values": [1]}}}
    requestProto = scu.json_to_seldon_message(data)
    assert requestProto.data.tensor.values == [1]
    assert requestProto.data.tensor.shape[0] == 1
    assert requestProto.data.tensor.shape[1] == 1
    assert len(requestProto.data.tensor.shape) == 2
    (arr, meta, datadef, _) = scu.extract_request_parts(requestProto)
    assert isinstance(arr, np.ndarray)
    assert arr.shape[0] == 1
    assert arr.shape[1] == 1
    assert arr[0][0] == 1
def predict(
    user_model: Any, request: Union[prediction_pb2.SeldonMessage, List, Dict]
) -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """
    Call the user model to get a prediction and package the response

    Parameters
    ----------
    user_model
       User defined class instance
    request
       The incoming request
    Returns
    -------
      The prediction
    """
    is_proto = isinstance(request, prediction_pb2.SeldonMessage)

    if hasattr(user_model, "predict_rest") and not is_proto:
        logger.warning("predict_rest is deprecated. Please use predict_raw")
        return user_model.predict_rest(request)
    elif hasattr(user_model, "predict_grpc") and is_proto:
        logger.warning("predict_grpc is deprecated. Please use predict_raw")
        return user_model.predict_grpc(request)
    else:
        if hasattr(user_model, "predict_raw"):
            try:
                return user_model.predict_raw(request)
            except SeldonNotImplementedError:
                pass

        if is_proto:
            (features, meta, datadef,
             data_type) = extract_request_parts(request)
            client_response = client_predict(user_model,
                                             features,
                                             datadef.names,
                                             meta=meta)
            return construct_response(user_model, False, request,
                                      client_response)
        else:
            (features, meta, datadef,
             data_type) = extract_request_parts_json(request)
            class_names = datadef[
                "names"] if datadef and "names" in datadef else []
            client_response = client_predict(user_model,
                                             features,
                                             class_names,
                                             meta=meta)
            return construct_response_json(user_model, False, request,
                                           client_response)
Beispiel #11
0
def test_create_grpc_reponse_text_ndarray():
    user_model = UserObject()
    request_data = np.array([["hello", "world"], ["hello", "another", "world"]])
    datadef = scu.array_to_grpc_datadef("ndarray", request_data)
    request = prediction_pb2.SeldonMessage(data=datadef)
    (features, meta, datadef, data_type) = scu.extract_request_parts(request)
    raw_response = np.array([["hello", "world"], ["here", "another"]])
    sm = scu.construct_response(user_model, True, request, raw_response)
    assert sm.data.WhichOneof("data_oneof") == "ndarray"
    assert type(features[0]) == list
    assert np.array_equal(sm.data.ndarray, raw_response)
    assert datadef == request.data
    assert np.array_equal(features, request_data)
    assert data_type == "data"
Beispiel #12
0
def extract_data_part(content):
    copy = content.copy()

    # if 'instances' in body then tensorflow request protocol
    # if 'predictions' then tensorflow response
    # otherwise can use seldon logic for parsing and inferring type (won't be in here if outlier)

    if "instances" in copy:

        copy["instance"] = copy["instances"]
        content_np = np.array(copy["instance"])

        copy["dataType"] = "tabular"
        first_element = content_np.item(0)

        set_datatype_from_numpy(content_np, copy, first_element)
        del copy["instances"]
    elif "predictions" in copy:
        copy["instance"] = copy["predictions"]
        content_np = np.array(copy["predictions"])

        copy["dataType"] = "tabular"
        first_element = content_np.item(0)

        set_datatype_from_numpy(content_np, copy, first_element)
        del copy["predictions"]
    else:
        requestMsg = json_to_seldon_message(copy)

        (req_features, _, req_datadef,
         req_datatype) = extract_request_parts(requestMsg)

        #set sensible defaults for non-tabular dataTypes
        #tabular should be iterable and get inferred through later block
        if req_datatype == "strData":
            copy["dataType"] = "text"
        if req_datatype == "binData":
            copy["dataType"] = "image"

        if isinstance(req_features, Iterable):

            elements = createElelmentsArray(req_features,
                                            list(req_datadef.names))

            if isinstance(elements, Iterable):

                for i, e in enumerate(elements):
                    reqJson = extractRow(i, requestMsg, req_datatype,
                                         req_features, req_datadef)
                    reqJson["elements"] = e
                    copy = reqJson

        copy["instance"] = json.loads(
            json.dumps(req_features, cls=log_helper.NumpyEncoder))

        if isinstance(req_features, np.ndarray):
            set_datatype_from_numpy(req_features, copy, req_features.item(0))

    #copy names into its own section of request
    if "data" in content:
        if "names" in content["data"]:
            copy["names"] = content["data"]["names"]

    #should now have processed content of seldon message so don't want its various constructs on top-level anymore
    if "data" in copy:
        del copy["data"]
    if "strData" in copy:
        del copy["strData"]
    if "jsonData" in copy:
        del copy["jsonData"]
    if "binData" in copy:
        del copy["binData"]

    copy['payload'] = content

    return copy
def aggregate(
    user_model: Any, request: Union[prediction_pb2.SeldonMessageList, List,
                                    Dict]
) -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """
    Aggregate a list of payloads

    Parameters
    ----------
    user_model
       A Seldon user model
    request
       SeldonMessage proto

    Returns
    -------
       Aggregated SeldonMessage proto

    """
    is_proto = isinstance(request, prediction_pb2.SeldonMessageList)

    if hasattr(user_model, "aggregate_rest"):
        logger.warning(
            "aggregate_rest is deprecated. Please use aggregate_raw")
        return user_model.aggregate_rest(request)
    elif hasattr(user_model, "aggregate_grpc"):
        logger.warning(
            "aggregate_grpc is deprecated. Please use aggregate_raw")
        return user_model.aggregate_grpc(request)
    else:
        if hasattr(user_model, "aggregate_raw"):
            try:
                return user_model.aggregate_raw(request)
            except SeldonNotImplementedError:
                pass

        if is_proto:
            features_list = []
            names_list = []

            for msg in request.seldonMessages:
                (features, meta, datadef,
                 data_type) = extract_request_parts(msg)
                features_list.append(features)
                names_list.append(datadef.names)

            client_response = client_aggregate(user_model, features_list,
                                               names_list)
            return construct_response(user_model, False,
                                      request.seldonMessages[0],
                                      client_response)
        else:
            features_list = []
            names_list = []

            if isinstance(request, list):
                msgs = request
            elif "seldonMessages" in request and isinstance(
                    request["seldonMessages"], list):
                msgs = request["seldonMessages"]
            else:
                raise SeldonMicroserviceException(
                    f"Invalid request data type: {request}")

            for msg in msgs:
                (features, meta, datadef,
                 data_type) = extract_request_parts_json(msg)
                class_names = datadef[
                    "names"] if datadef and "names" in datadef else []
                features_list.append(features)
                names_list.append(class_names)

            client_response = client_aggregate(user_model, features_list,
                                               names_list)
            return construct_response_json(user_model, False, msgs[0],
                                           client_response)
Beispiel #14
0
def transform_input(
    user_model: Any,
    request: Union[prediction_pb2.SeldonMessage, List, Dict],
    seldon_metrics: SeldonMetrics,
) -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """

    Parameters
    ----------
    user_model
       User defined class to handle transform input
    request
       The incoming request

    Returns
    -------
       The transformed request

    """
    is_proto = isinstance(request, prediction_pb2.SeldonMessage)

    if hasattr(user_model, "transform_input_rest"):
        logger.warning(
            "transform_input_rest is deprecated. Please use transform_input_raw"
        )
        return user_model.transform_input_rest(request)
    elif hasattr(user_model, "transform_input_grpc"):
        logger.warning(
            "transform_input_grpc is deprecated. Please use transform_input_raw"
        )
        return user_model.transform_input_grpc(request)
    else:
        if hasattr(user_model, "transform_input_raw"):
            try:
                response = user_model.transform_input_raw(request)
                if is_proto:
                    metrics = seldon_message_to_json(response.meta).get(
                        "metrics", [])
                else:
                    metrics = response.get("meta", {}).get("metrics", [])
                seldon_metrics.update(metrics)
                return response
            except SeldonNotImplementedError:
                pass

        if is_proto:
            (features, meta, datadef,
             data_type) = extract_request_parts(request)
            client_response = client_transform_input(user_model,
                                                     features,
                                                     datadef.names,
                                                     meta=meta)

            metrics = client_custom_metrics(user_model)
            if seldon_metrics is not None:
                seldon_metrics.update(metrics)

            return construct_response(user_model, False, request,
                                      client_response, meta, metrics)
        else:
            (features, meta, datadef,
             data_type) = extract_request_parts_json(request)
            class_names = datadef[
                "names"] if datadef and "names" in datadef else []
            client_response = client_transform_input(user_model,
                                                     features,
                                                     class_names,
                                                     meta=meta)

            metrics = client_custom_metrics(user_model)
            if seldon_metrics is not None:
                seldon_metrics.update(metrics)

            return construct_response_json(user_model, False, request,
                                           client_response, meta, metrics)
Beispiel #15
0
def extract_data_part(content, headers, message_type):
    copy = content.copy()

    namespace = log_helper.get_header(log_helper.NAMESPACE_HEADER_NAME,
                                      headers)
    inferenceservice_name = log_helper.get_header(
        log_helper.INFERENCESERVICE_HEADER_NAME, headers)
    endpoint_name = log_helper.get_header(log_helper.ENDPOINT_HEADER_NAME,
                                          headers)
    serving_engine = log_helper.serving_engine(headers)

    # if 'instances' in body then tensorflow request protocol
    # if 'predictions' then tensorflow response
    # if 'model_name' and 'outputs' then v2 dataplane response
    # if 'inputs' then v2 data plane request
    # otherwise can use seldon logic for parsing and inferring type (won't be in here if outlier)

    # V2 Data Plane Response
    if "model_name" in copy and "outputs" in copy:
        # assumes single output
        output = copy["outputs"][0]
        data_type = output["datatype"]
        shape = output["shape"]
        data = output["data"]

        if data_type == "BYTES":
            copy["dataType"] = "text"
            copy["instance"] = array.array('B', data).tostring()
        else:
            arr = create_np_from_v2(data, data_type, shape)
            copy["dataType"] = "tabular"
            first_element = arr.item(0)
            set_datatype_from_numpy(arr, copy, first_element)
            copy["instance"] = arr.tolist()

        del copy["outputs"]
        del copy["model_name"]
        del copy["model_version"]
    elif "inputs" in copy:
        # assumes single input
        inputs = copy["inputs"][0]
        data_type = inputs["datatype"]
        shape = inputs["shape"]
        data = inputs["data"]

        if data_type == "BYTES":
            copy["dataType"] = "text"
            copy["instance"] = array.array('B', data).tostring()
        else:
            arr = create_np_from_v2(data, data_type, shape)
            copy["dataType"] = "tabular"
            first_element = arr.item(0)
            set_datatype_from_numpy(arr, copy, first_element)
            copy["instance"] = arr.tolist()

        del copy["inputs"]
    elif "instances" in copy:

        copy["instance"] = copy["instances"]
        content_np = np.array(copy["instance"])

        copy["dataType"] = "tabular"
        first_element = content_np.item(0)

        set_datatype_from_numpy(content_np, copy, first_element)
        elements = createElelmentsArray(content_np, None, namespace,
                                        serving_engine, inferenceservice_name,
                                        endpoint_name, message_type)
        copy["elements"] = elements

        del copy["instances"]
    elif "predictions" in copy:
        copy["instance"] = copy["predictions"]
        content_np = np.array(copy["predictions"])

        copy["dataType"] = "tabular"
        first_element = content_np.item(0)
        set_datatype_from_numpy(content_np, copy, first_element)
        elements = createElelmentsArray(content_np, None, namespace,
                                        serving_engine, inferenceservice_name,
                                        endpoint_name, message_type)
        copy["elements"] = elements

        del copy["predictions"]
    else:
        requestMsg = json_to_seldon_message(copy)

        (req_features, _, req_datadef,
         req_datatype) = extract_request_parts(requestMsg)

        # set sensible defaults for non-tabular dataTypes
        # tabular should be iterable and get inferred through later block
        if req_datatype == "strData":
            copy["dataType"] = "text"
        if req_datatype == "jsonData":
            copy["dataType"] = "json"
        if req_datatype == "binData":
            copy["dataType"] = "image"

        if isinstance(req_features, Iterable):

            elements = createElelmentsArray(req_features,
                                            list(req_datadef.names), namespace,
                                            serving_engine,
                                            inferenceservice_name,
                                            endpoint_name, message_type)
            copy["elements"] = elements

        copy["instance"] = json.loads(
            json.dumps(req_features, cls=log_helper.NumpyEncoder))

        if isinstance(req_features, np.ndarray):
            set_datatype_from_numpy(req_features, copy, req_features.item(0))

    # copy names into its own section of request
    if "data" in content:
        if "names" in content["data"]:
            copy["names"] = content["data"]["names"]

    # should now have processed content of seldon message so don't want its various constructs on top-level anymore
    if "data" in copy:
        del copy["data"]
    if "strData" in copy:
        del copy["strData"]
    if "jsonData" in copy:
        del copy["jsonData"]
    if "binData" in copy:
        del copy["binData"]

    copy["payload"] = content

    return copy
def aggregate(
    user_model: Any,
    request: Union[prediction_pb2.SeldonMessageList, List, Dict],
    seldon_metrics: SeldonMetrics,
) -> Union[prediction_pb2.SeldonMessage, List, Dict]:
    """
    Aggregate a list of payloads

    Parameters
    ----------
    user_model
       A Seldon user model
    request
       SeldonMessage proto
    seldon_metrics
        A SeldonMetrics instance

    Returns
    -------
       Aggregated SeldonMessage proto

    """
    def merge_meta(meta_list):
        tags = {}
        for meta in meta_list:
            if meta:
                tags.update(meta.get("tags", {}))
        return {"tags": tags}

    def merge_metrics(meta_list, custom_metrics):
        metrics = []
        for meta in meta_list:
            if meta:
                metrics.extend(meta.get("metrics", []))
        metrics.extend(custom_metrics)
        return metrics

    is_proto = isinstance(request, prediction_pb2.SeldonMessageList)

    if hasattr(user_model, "aggregate_rest"):
        logger.warning(
            "aggregate_rest is deprecated. Please use aggregate_raw")
        return user_model.aggregate_rest(request)
    elif hasattr(user_model, "aggregate_grpc"):
        logger.warning(
            "aggregate_grpc is deprecated. Please use aggregate_raw")
        return user_model.aggregate_grpc(request)
    else:
        if hasattr(user_model, "aggregate_raw"):
            try:
                response = user_model.aggregate_raw(request)
                handle_raw_custom_metrics(response, seldon_metrics, is_proto,
                                          AGGREGATE_METRIC_METHOD_TAG)
                return response
            except SeldonNotImplementedError:
                pass

        if is_proto:
            features_list = []
            names_list = []
            meta_list = []

            for msg in request.seldonMessages:
                (features, meta, datadef,
                 data_type) = extract_request_parts(msg)
                features_list.append(features)
                names_list.append(datadef.names)
                meta_list.append(meta)

            client_response = client_aggregate(user_model, features_list,
                                               names_list)

            metrics = client_custom_metrics(
                user_model,
                seldon_metrics,
                AGGREGATE_METRIC_METHOD_TAG,
                client_response.metrics,
            )

            return construct_response(
                user_model,
                False,
                request.seldonMessages[0],
                client_response.data,
                merge_meta(meta_list),
                merge_metrics(meta_list, metrics),
                client_response.tags,
            )
        else:
            features_list = []
            names_list = []

            if isinstance(request, list):
                msgs = request
            elif "seldonMessages" in request and isinstance(
                    request["seldonMessages"], list):
                msgs = request["seldonMessages"]
            else:
                raise SeldonMicroserviceException(
                    f"Invalid request data type: {request}")

            meta_list = []
            for msg in msgs:
                (features, meta, datadef,
                 data_type) = extract_request_parts_json(msg)
                class_names = datadef[
                    "names"] if datadef and "names" in datadef else []
                features_list.append(features)
                names_list.append(class_names)
                meta_list.append(meta)

            client_response = client_aggregate(user_model, features_list,
                                               names_list)

            metrics = client_custom_metrics(
                user_model,
                seldon_metrics,
                AGGREGATE_METRIC_METHOD_TAG,
                client_response.metrics,
            )

            return construct_response_json(
                user_model,
                False,
                msgs[0],
                client_response.data,
                merge_meta(meta_list),
                merge_metrics(meta_list, metrics),
                client_response.tags,
            )
def predict(
    user_model: Any,
    request: Union[prediction_pb2.SeldonMessage, List, Dict, bytes],
    seldon_metrics: SeldonMetrics,
) -> Union[prediction_pb2.SeldonMessage, List, Dict, bytes]:
    """
    Call the user model to get a prediction and package the response

    Parameters
    ----------
    user_model
       User defined class instance
    request
       The incoming request

    Returns
    -------
      The prediction
    """
    # TODO: Find a way to choose predict_rest or predict_grpc when payload is
    # not decoded
    is_proto = isinstance(request, prediction_pb2.SeldonMessage)

    if hasattr(user_model, "predict_rest") and not is_proto:
        logger.warning("predict_rest is deprecated. Please use predict_raw")
        return user_model.predict_rest(request)
    elif hasattr(user_model, "predict_grpc") and is_proto:
        logger.warning("predict_grpc is deprecated. Please use predict_raw")
        return user_model.predict_grpc(request)
    else:
        if hasattr(user_model, "predict_raw"):
            try:
                response = user_model.predict_raw(request)
                handle_raw_custom_metrics(response, seldon_metrics, is_proto,
                                          PREDICT_METRIC_METHOD_TAG)
                return response
            except SeldonNotImplementedError:
                pass

        if is_proto:
            (features, meta, datadef,
             data_type) = extract_request_parts(request)

            client_response = client_predict(user_model,
                                             features,
                                             datadef.names,
                                             meta=meta)

            metrics = client_custom_metrics(
                user_model,
                seldon_metrics,
                PREDICT_METRIC_METHOD_TAG,
                client_response.metrics,
            )

            return construct_response(
                user_model,
                False,
                request,
                client_response.data,
                meta,
                metrics,
                client_response.tags,
            )
        else:
            (features, meta, datadef,
             data_type) = extract_request_parts_json(request)
            class_names = datadef[
                "names"] if datadef and "names" in datadef else []

            client_response = client_predict(user_model,
                                             features,
                                             class_names,
                                             meta=meta)

            metrics = client_custom_metrics(
                user_model,
                seldon_metrics,
                PREDICT_METRIC_METHOD_TAG,
                client_response.metrics,
            )

            return construct_response_json(
                user_model,
                False,
                request,
                client_response.data,
                meta,
                metrics,
                client_response.tags,
            )