Skip to content

Commit

Permalink
Merge pull request #595 from gsunner/add-json-data-value
Browse files Browse the repository at this point in the history
Update SeldonMessage with jsonData
  • Loading branch information
ukclivecox authored May 31, 2019
2 parents 0c0e4c7 + f027b59 commit 80ed2b7
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 61 deletions.
3 changes: 2 additions & 1 deletion proto/prediction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ message SeldonMessage {
DefaultData data = 3;
bytes binData = 4;
string strData = 5;
google.protobuf.Value jsonData = 6;
}
}

Expand Down Expand Up @@ -126,4 +127,4 @@ service Seldon {
rpc SendFeedback(Feedback) returns (SeldonMessage) {};
}

// [END Services]
// [END Services]
3 changes: 2 additions & 1 deletion python/seldon_core/proto/prediction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ message SeldonMessage {
DefaultData data = 3;
bytes binData = 4;
string strData = 5;
google.protobuf.Value jsonData = 6;
}
}

Expand Down Expand Up @@ -126,4 +127,4 @@ service Seldon {
rpc SendFeedback(Feedback) returns (SeldonMessage) {};
}

// [END Services]
// [END Services]
100 changes: 56 additions & 44 deletions python/seldon_core/proto/prediction_pb2.py

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions python/seldon_core/proto/prediction_pb2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,28 @@ class SeldonMessage(google___protobuf___message___Message):
@property
def data(self) -> DefaultData: ...

@property
def jsonData(self) -> google___protobuf___struct_pb2___Value: ...

def __init__(self,
status : typing___Optional[Status] = None,
meta : typing___Optional[Meta] = None,
data : typing___Optional[DefaultData] = None,
binData : typing___Optional[bytes] = None,
strData : typing___Optional[typing___Text] = None,
jsonData : typing___Optional[google___protobuf___struct_pb2___Value] = None,
) -> None: ...
@classmethod
def FromString(cls, s: bytes) -> SeldonMessage: ...
def MergeFrom(self, other_msg: google___protobuf___message___Message) -> None: ...
def CopyFrom(self, other_msg: google___protobuf___message___Message) -> None: ...
if sys.version_info >= (3,):
def HasField(self, field_name: typing_extensions___Literal[u"binData",u"data",u"data_oneof",u"meta",u"status",u"strData"]) -> bool: ...
def ClearField(self, field_name: typing_extensions___Literal[u"binData",u"data",u"data_oneof",u"meta",u"status",u"strData"]) -> None: ...
def HasField(self, field_name: typing_extensions___Literal[u"binData",u"data",u"data_oneof",u"jsonData",u"meta",u"status",u"strData"]) -> bool: ...
def ClearField(self, field_name: typing_extensions___Literal[u"binData",u"data",u"data_oneof",u"jsonData",u"meta",u"status",u"strData"]) -> None: ...
else:
def HasField(self, field_name: typing_extensions___Literal[u"binData",b"binData",u"data",b"data",u"data_oneof",b"data_oneof",u"meta",b"meta",u"status",b"status",u"strData",b"strData"]) -> bool: ...
def ClearField(self, field_name: typing_extensions___Literal[b"binData",b"data",b"data_oneof",b"meta",b"status",b"strData"]) -> None: ...
def WhichOneof(self, oneof_group: typing_extensions___Literal[u"data_oneof",b"data_oneof"]) -> typing_extensions___Literal["data","binData","strData"]: ...
def HasField(self, field_name: typing_extensions___Literal[u"binData",b"binData",u"data",b"data",u"data_oneof",b"data_oneof",u"jsonData",b"jsonData",u"meta",b"meta",u"status",b"status",u"strData",b"strData"]) -> bool: ...
def ClearField(self, field_name: typing_extensions___Literal[b"binData",b"data",b"data_oneof",b"jsonData",b"meta",b"status",b"strData"]) -> None: ...
def WhichOneof(self, oneof_group: typing_extensions___Literal[u"data_oneof",b"data_oneof"]) -> typing_extensions___Literal["data","binData","strData","jsonData"]: ...

class DefaultData(google___protobuf___message___Message):
names = ... # type: google___protobuf___internal___containers___RepeatedScalarFieldContainer[typing___Text]
Expand Down
12 changes: 9 additions & 3 deletions python/seldon_core/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from google.protobuf import json_format
from google.protobuf.json_format import MessageToDict, ParseDict
from seldon_core.proto import prediction_pb2
from seldon_core.flask_utils import SeldonMicroserviceException
import numpy as np
Expand Down Expand Up @@ -114,7 +115,7 @@ def feedback_to_json(message_proto: prediction_pb2.Feedback) -> Dict:
return message_dict


def get_data_from_proto(request: prediction_pb2.SeldonMessage) -> Union[np.ndarray, str, bytes]:
def get_data_from_proto(request: prediction_pb2.SeldonMessage) -> Union[np.ndarray, str, bytes, dict]:
"""
Extract the data payload from the SeldonMessage
Expand All @@ -136,6 +137,8 @@ def get_data_from_proto(request: prediction_pb2.SeldonMessage) -> Union[np.ndarr
return request.binData
elif data_type == "strData":
return request.strData
elif data_type == "jsonData":
return MessageToDict(request.jsonData)
else:
raise SeldonMicroserviceException("Unknown data in SeldonMessage")

Expand Down Expand Up @@ -300,7 +303,7 @@ def array_to_list_value(array: np.ndarray, lv: Optional[ListValue] = None) -> Li


def construct_response(user_model: SeldonComponent, is_request: bool, client_request: prediction_pb2.SeldonMessage,
client_raw_response: Union[np.ndarray, str, bytes]) -> prediction_pb2.SeldonMessage:
client_raw_response: Union[np.ndarray, str, bytes, dict]) -> prediction_pb2.SeldonMessage:
"""
Parameters
Expand Down Expand Up @@ -349,14 +352,17 @@ def construct_response(user_model: SeldonComponent, is_request: bool, client_req
return prediction_pb2.SeldonMessage(data=data, meta=meta)
elif isinstance(client_raw_response, str):
return prediction_pb2.SeldonMessage(strData=client_raw_response, meta=meta)
elif isinstance(client_raw_response, dict):
jsonDataResponse = ParseDict(client_raw_response, prediction_pb2.SeldonMessage().jsonData)
return prediction_pb2.SeldonMessage(jsonData=jsonDataResponse, meta=meta)
elif isinstance(client_raw_response, (bytes, bytearray)):
return prediction_pb2.SeldonMessage(binData=client_raw_response, meta=meta)
else:
raise SeldonMicroserviceException("Unknown data type returned as payload:" + client_raw_response)


def extract_request_parts(request: prediction_pb2.SeldonMessage) -> Tuple[
Union[np.ndarray, str, bytes], Dict, prediction_pb2.DefaultData, str]:
Union[np.ndarray, str, bytes, dict], Dict, prediction_pb2.DefaultData, str]:
"""
Parameters
Expand Down
6 changes: 0 additions & 6 deletions python/tests/test_combiner_microservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ def test_aggreate_invalid_message():
j = json.loads(rv.data)
print(j)
assert j["status"]["reason"] == "MICROSERVICE_BAD_DATA"
assert j["status"][
"info"] == 'Invalid JSON: Message type "seldon.protos.SeldonMessageList" has no field named "wrong".\n Available Fields(except extensions): <MessageFields sequence>'


def test_aggreate_no_list():
Expand All @@ -124,8 +122,6 @@ def test_aggreate_no_list():
j = json.loads(rv.data)
print(j)
assert j["status"]["reason"] == "MICROSERVICE_BAD_DATA"
assert j["status"][
"info"] == "Invalid JSON: Failed to parse seldonMessages field: repeated field seldonMessages must be in [] which is {'data': {'ndarray': [1]}}."


def test_aggreate_bad_messages():
Expand All @@ -137,8 +133,6 @@ def test_aggreate_bad_messages():
j = json.loads(rv.data)
print(j)
assert j["status"]["reason"] == "MICROSERVICE_BAD_DATA"
assert j["status"][
"info"] == 'Invalid JSON: Failed to parse seldonMessages field: Message type "seldon.protos.SeldonMessage" has no field named "data2".\n Available Fields(except extensions): <MessageFields sequence>'


def test_aggreate_ok_2messages():
Expand Down
23 changes: 23 additions & 0 deletions python/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from seldon_core.proto import prediction_pb2
from seldon_core.flask_utils import SeldonMicroserviceException
import seldon_core.utils as scu
from google.protobuf.struct_pb2 import Value


class UserObject(object):
Expand Down Expand Up @@ -90,6 +91,18 @@ def test_create_response_strdata():
assert len(sm.strData) > 0


def test_create_response_jsondata():
user_model = UserObject()
request_data = np.array([[5, 6, 7]])
datadef = scu.array_to_grpc_datadef("ndarray", request_data)
request = prediction_pb2.SeldonMessage(data=datadef)
raw_response = {"output": "data"}
sm = scu.construct_response(user_model, True, request, raw_response)
assert sm.data.WhichOneof("data_oneof") == None
emptyValue = Value()
assert sm.jsonData != emptyValue


def test_create_reponse_list():
user_model = UserObject()
request_data = np.array([[5, 6, 7]])
Expand Down Expand Up @@ -161,6 +174,16 @@ def test_json_to_seldon_message_str_data():
assert arr == "my string data"


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 test_json_to_seldon_message_bad_data():
with pytest.raises(SeldonMicroserviceException):
data = {"foo": "bar"}
Expand Down
7 changes: 6 additions & 1 deletion util/loadtester/scripts/proto/prediction.proto
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
syntax = "proto3";

import "google/protobuf/struct.proto";
import "tensorflow/core/framework/tensor.proto";

package seldon.protos;

option java_package = "io.seldon.protos";
option java_outer_classname = "PredictionProtos";
option go_package = "github.com/seldonio/seldon-core/examples/wrappers/go/pkg/api";

// [START Messages]

Expand All @@ -17,6 +19,7 @@ message SeldonMessage {
DefaultData data = 3;
bytes binData = 4;
string strData = 5;
google.protobuf.Value jsonData = 6;
}
}

Expand All @@ -25,6 +28,7 @@ message DefaultData {
oneof data_oneof {
Tensor tensor = 2;
google.protobuf.ListValue ndarray = 3;
tensorflow.TensorProto tftensor = 4;
}
}

Expand All @@ -50,6 +54,7 @@ message Metric {
string key = 1;
MetricType type = 2;
float value = 3;
map<string,string> tags = 4;
}

message SeldonMessageList {
Expand Down Expand Up @@ -122,4 +127,4 @@ service Seldon {
rpc SendFeedback(Feedback) returns (SeldonMessage) {};
}

// [END Services]
// [END Services]

0 comments on commit 80ed2b7

Please sign in to comment.