From db485b93b5f7981dff3f4449b52ce6ddd332c59a Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Fri, 25 Nov 2022 02:37:38 -0800 Subject: [PATCH] feat(grpc): adding service metadata Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com> --- src/bentoml/_internal/io_descriptors/json.py | 42 +-- src/bentoml/_internal/server/grpc/servicer.py | 231 ++++++++++----- .../_internal/service/inference_api.py | 2 +- src/bentoml/grpc/utils/__init__.py | 2 + src/bentoml/grpc/utils/_import_hook.py | 4 +- src/bentoml/grpc/v1/service.proto | 37 +++ src/bentoml/grpc/v1/service_pb2.py | 271 ++++++++++++++++-- src/bentoml/grpc/v1/service_pb2.pyi | 110 +++++++ src/bentoml/grpc/v1/service_pb2_grpc.py | 34 +++ src/bentoml/grpc/v1/service_pb2_grpc.pyi | 12 + 10 files changed, 633 insertions(+), 112 deletions(-) diff --git a/src/bentoml/_internal/io_descriptors/json.py b/src/bentoml/_internal/io_descriptors/json.py index da6ed9bc502..08c1dc18b59 100644 --- a/src/bentoml/_internal/io_descriptors/json.py +++ b/src/bentoml/_internal/io_descriptors/json.py @@ -30,6 +30,7 @@ import pydantic import pydantic.schema as schema + from google.protobuf import message as _message from google.protobuf import struct_pb2 from typing_extensions import Self @@ -392,19 +393,28 @@ async def to_proto(self, obj: JSONType) -> struct_pb2.Value: if LazyType["pydantic.BaseModel"]("pydantic.BaseModel").isinstance(obj): obj = obj.dict() msg = struct_pb2.Value() - # To handle None cases. - if obj is not None: - from google.protobuf.json_format import ParseDict - - if isinstance(obj, (dict, str, list, float, int, bool)): - # ParseDict handles google.protobuf.Struct type - # directly if given object has a supported type - ParseDict(obj, msg) - else: - # If given object doesn't have a supported type, we will - # use given JSON encoder to convert it to dictionary - # and then parse it to google.protobuf.Struct. - # Note that if a custom JSON encoder is used, it mustn't - # take any arguments. - ParseDict(self._json_encoder().default(obj), msg) - return msg + return parse_dict_to_proto(obj, msg, json_encoder=self._json_encoder) + + +def parse_dict_to_proto( + obj: JSONType, + msg: _message.Message, + json_encoder: type[json.JSONEncoder] = DefaultJsonEncoder, +) -> t.Any: + # To handle None cases. + if obj is not None: + from google.protobuf.json_format import ParseDict + + if isinstance(obj, (dict, str, list, float, int, bool)): + # ParseDict handles google.protobuf.Struct type + # directly if given object has a supported type + ParseDict(obj, msg) + else: + # If given object doesn't have a supported type, we will + # use given JSON encoder to convert it to dictionary + # and then parse it to google.protobuf.Struct. + # Note that if a custom JSON encoder is used, it mustn't + # take any arguments. + ParseDict(json_encoder().default(obj), msg) + # otherwise this function is an identity op for the msg if obj is None. + return msg diff --git a/src/bentoml/_internal/server/grpc/servicer.py b/src/bentoml/_internal/server/grpc/servicer.py index b96ca7b8ea4..f3dd28a2ebb 100644 --- a/src/bentoml/_internal/server/grpc/servicer.py +++ b/src/bentoml/_internal/server/grpc/servicer.py @@ -9,14 +9,14 @@ import anyio -from bentoml.grpc.utils import import_grpc -from bentoml.grpc.utils import grpc_status_code -from bentoml.grpc.utils import validate_proto_fields -from bentoml.grpc.utils import import_generated_stubs - from ...utils import LazyLoader from ....exceptions import InvalidArgument from ....exceptions import BentoMLException +from ....grpc.utils import import_grpc +from ....grpc.utils import grpc_status_code +from ....grpc.utils import validate_proto_fields +from ....grpc.utils import import_generated_stubs +from ....grpc.utils import LATEST_PROTOCOL_VERSION logger = logging.getLogger(__name__) @@ -26,19 +26,24 @@ import grpc from grpc import aio from grpc_health.v1 import health + from google.protobuf import struct_pb2 from typing_extensions import Self - from bentoml.grpc.v1 import service_pb2 as pb - from bentoml.grpc.v1 import service_pb2_grpc as services from bentoml.grpc.types import Interceptors from bentoml.grpc.types import AddServicerFn from bentoml.grpc.types import ServicerClass from bentoml.grpc.types import BentoServicerContext from ...service.service import Service - + from ...io_descriptors.base import IODescriptor + + if LATEST_PROTOCOL_VERSION == "v1": + from bentoml.grpc.v1 import service_pb2 as pb + from bentoml.grpc.v1 import service_pb2_grpc as services + else: + from bentoml.grpc.v1alpha1 import service_pb2 as pb + from bentoml.grpc.v1alpha1 import service_pb2_grpc as services else: - pb, services = import_generated_stubs() grpc, aio = import_grpc() health = LazyLoader( "health", @@ -46,9 +51,7 @@ "grpc_health.v1.health", exc_msg="'grpcio-health-checking' is required for using health checking endpoints. Install with 'pip install grpcio-health-checking'.", ) - containers = LazyLoader( - "containers", globals(), "google.protobuf.internal.containers" - ) + struct_pb2 = LazyLoader("struct_pb2", globals(), "google.protobuf.struct_pb2") def log_exception(request: pb.Request, exc_info: ExcInfoType) -> None: @@ -67,6 +70,7 @@ def __init__( mount_servicers: t.Sequence[tuple[ServicerClass, AddServicerFn, list[str]]] | None = None, interceptors: Interceptors | None = None, + protocol_version: str = LATEST_PROTOCOL_VERSION, ) -> None: self.bento_service = service @@ -75,13 +79,17 @@ def __init__( self.mount_servicers = [] if not mount_servicers else list(mount_servicers) self.interceptors = [] if not interceptors else list(interceptors) self.loaded = False + self.protocol_version = protocol_version def load(self): + pb, _ = import_generated_stubs(self.protocol_version) assert not self.loaded self.interceptors_stack = self.build_interceptors_stack() - self.bento_servicer = create_bento_servicer(self.bento_service) + self.bento_servicer = create_bento_servicer( + self.bento_service, protocol_version=self.protocol_version + ) # Create a health check servicer. We use the non-blocking implementation # to avoid thread starvation. @@ -111,66 +119,147 @@ def __bool__(self): return self.loaded -def create_bento_servicer(service: Service) -> services.BentoServiceServicer: +def create_bento_servicer( + service: Service, protocol_version: str = LATEST_PROTOCOL_VERSION +) -> services.BentoServiceServicer: """ This is the actual implementation of BentoServicer. Main inference entrypoint will be invoked via /bentoml.grpc..BentoService/Call """ - - class BentoServiceImpl(services.BentoServiceServicer): - """An asyncio implementation of BentoService servicer.""" - - async def Call( # type: ignore (no async types) # pylint: disable=invalid-overridden-method - self, - request: pb.Request, - context: BentoServicerContext, - ) -> pb.Response | None: - if request.api_name not in service.apis: - raise InvalidArgument( - f"given 'api_name' is not defined in {service.name}", - ) from None - - api = service.apis[request.api_name] - response = pb.Response() - - # NOTE: since IODescriptor._proto_fields is a tuple, the order is preserved. - # This is important so that we know the order of fields to process. - # We will use fields descriptor to determine how to process that request. - try: - # we will check if the given fields list contains a pb.Multipart. - input_proto = getattr( - request, - validate_proto_fields(request.WhichOneof("content"), api.input), - ) - input_data = await api.input.from_proto(input_proto) - if asyncio.iscoroutinefunction(api.func): - if api.multi_input: - output = await api.func(**input_data) - else: - output = await api.func(input_data) + if protocol_version == "v1": + from bentoml.grpc.v1 import service_pb2 as pb + from bentoml.grpc.v1.service_pb2_grpc import BentoServiceServicer + else: + from bentoml.grpc.v1alpha1 import service_pb2 as pb + from bentoml.grpc.v1alpha1.service_pb2_grpc import BentoServiceServicer + + attrs: dict[str, t.Any] = { + "__doc__": "An asyncio implementation of BentoService servicer." + } + + async def Call( + _: BentoServiceServicer, + request: pb.Request, + context: BentoServicerContext, + ) -> pb.Response | None: + if request.api_name not in service.apis: + raise InvalidArgument( + f"given 'api_name' is not defined in {service.name}", + ) from None + + api = service.apis[request.api_name] + response = pb.Response() + + # NOTE: since IODescriptor._proto_fields is a tuple, the order is preserved. + # This is important so that we know the order of fields to process. + # We will use fields descriptor to determine how to process that request. + try: + # we will check if the given fields list contains a pb.Multipart. + input_proto = getattr( + request, + validate_proto_fields(request.WhichOneof("content"), api.input), + ) + input_data = await api.input.from_proto(input_proto) + if asyncio.iscoroutinefunction(api.func): + if api.multi_input: + output = await api.func(**input_data) + else: + output = await api.func(input_data) + else: + if api.multi_input: + output = await anyio.to_thread.run_sync(api.func, **input_data) else: - if api.multi_input: - output = await anyio.to_thread.run_sync(api.func, **input_data) - else: - output = await anyio.to_thread.run_sync(api.func, input_data) - res = await api.output.to_proto(output) - # TODO(aarnphm): support multiple proto fields - response = pb.Response(**{api.output._proto_fields[0]: res}) - except BentoMLException as e: - log_exception(request, sys.exc_info()) - await context.abort(code=grpc_status_code(e), details=e.message) - except (RuntimeError, TypeError, NotImplementedError): - log_exception(request, sys.exc_info()) - await context.abort( - code=grpc.StatusCode.INTERNAL, - details="A runtime error has occurred, see stacktrace from logs.", - ) - except Exception: # pylint: disable=broad-except - log_exception(request, sys.exc_info()) - await context.abort( - code=grpc.StatusCode.INTERNAL, - details="An error has occurred in BentoML user code when handling this request, find the error details in server logs.", - ) - return response - - return BentoServiceImpl() + output = await anyio.to_thread.run_sync(api.func, input_data) + res = await api.output.to_proto(output) + # TODO(aarnphm): support multiple proto fields + response = pb.Response(**{api.output._proto_fields[0]: res}) + except BentoMLException as e: + log_exception(request, sys.exc_info()) + await context.abort(code=grpc_status_code(e), details=e.message) + except (RuntimeError, TypeError, NotImplementedError): + log_exception(request, sys.exc_info()) + await context.abort( + code=grpc.StatusCode.INTERNAL, + details="A runtime error has occurred, see stacktrace from logs.", + ) + except Exception: # pylint: disable=broad-except + log_exception(request, sys.exc_info()) + await context.abort( + code=grpc.StatusCode.INTERNAL, + details="An error has occurred in BentoML user code when handling this request, find the error details in server logs.", + ) + return response + + attrs.setdefault("Call", Call) + + if protocol_version == "v1": + # "v1" introduces ServiceMetadata to send in bentoml.Service information. + from bentoml.grpc.v1.service_pb2 import ServiceMetadataRequest + from bentoml.grpc.v1.service_pb2 import ServiceMetadataResponse + + async def ServiceMetadata( + _: BentoServiceServicer, + request: ServiceMetadataRequest, # pylint: disable=unused-argument + context: BentoServicerContext, # pylint: disable=unused-argument + ) -> ServiceMetadataResponse: + return ServiceMetadataResponse( + name=service.name, + docs=service.doc, + apis=[ + ServiceMetadataResponse.InferenceAPI( + name=api.name, + docs=api.doc, + input=ServiceMetadataResponse.DescriptorMetadata( + descriptor_id=api.input.descriptor_id, + attributes=make_attributes_struct(api.input), + ), + output=ServiceMetadataResponse.DescriptorMetadata( + descriptor_id=api.output.descriptor_id, + attributes=make_attributes_struct(api.output), + ), + ) + for api in service.apis.values() + ], + ) + + attrs.setdefault("ServiceMetadata", ServiceMetadata) + + if TYPE_CHECKING: + # NOTE: typeshed only accept type expression for type() class creation. + # Hence, pyright will raise an error if we only pass in BentoServiceServicer, as it won't + # acknowledge BentoServiceServicer as a type expression. + BentoServiceServicerT = type(BentoServiceServicer) + else: + BentoServiceServicerT = BentoServiceServicer + + return type("BentoServiceImpl", (BentoServiceServicerT,), attrs)() + + +if TYPE_CHECKING: + NestedDictStrAny = dict[str, dict[str, t.Any] | t.Any] + TupleAny = tuple[t.Any, ...] + + +def _tuple_converter(d: NestedDictStrAny | None) -> NestedDictStrAny | None: + # handles case for struct_pb2.Value where nested items are tuple. + # if that is the case, then convert to list. + # This dict is only one level deep, as we don't allow nested Multipart. + if d is not None: + for key, value in d.items(): + if isinstance(value, tuple): + d[key] = list(t.cast("TupleAny", value)) + elif isinstance(value, dict): + d[key] = _tuple_converter(t.cast("NestedDictStrAny", value)) + return d + + +def make_attributes_struct(io: IODescriptor[t.Any]) -> struct_pb2.Struct: + from ...io_descriptors.json import parse_dict_to_proto + + return struct_pb2.Struct( + fields={ + "args": parse_dict_to_proto( + _tuple_converter(io.to_spec().get("args", None)), struct_pb2.Value() + ) + } + ) diff --git a/src/bentoml/_internal/service/inference_api.py b/src/bentoml/_internal/service/inference_api.py index 9fb9d98b609..bffd6126965 100644 --- a/src/bentoml/_internal/service/inference_api.py +++ b/src/bentoml/_internal/service/inference_api.py @@ -26,7 +26,7 @@ class InferenceAPI: def __init__( self, - user_defined_callback: t.Callable[..., t.Any] | None, + user_defined_callback: t.Callable[..., t.Any], input_descriptor: IODescriptor[t.Any], output_descriptor: IODescriptor[t.Any], name: Optional[str], diff --git a/src/bentoml/grpc/utils/__init__.py b/src/bentoml/grpc/utils/__init__.py index a0a998334f6..f5ba32f5284 100644 --- a/src/bentoml/grpc/utils/__init__.py +++ b/src/bentoml/grpc/utils/__init__.py @@ -10,6 +10,7 @@ from bentoml.exceptions import InvalidArgument from bentoml.grpc.utils._import_hook import import_grpc from bentoml.grpc.utils._import_hook import import_generated_stubs +from bentoml.grpc.utils._import_hook import LATEST_PROTOCOL_VERSION if TYPE_CHECKING: from enum import Enum @@ -36,6 +37,7 @@ "import_generated_stubs", "import_grpc", "validate_proto_fields", + "LATEST_PROTOCOL_VERSION", ] logger = logging.getLogger(__name__) diff --git a/src/bentoml/grpc/utils/_import_hook.py b/src/bentoml/grpc/utils/_import_hook.py index 29b33eac705..147f0c2921d 100644 --- a/src/bentoml/grpc/utils/_import_hook.py +++ b/src/bentoml/grpc/utils/_import_hook.py @@ -5,9 +5,11 @@ if TYPE_CHECKING: import types +LATEST_PROTOCOL_VERSION = "v1" + def import_generated_stubs( - version: str = "v1", + version: str = LATEST_PROTOCOL_VERSION, file: str = "service.proto", ) -> tuple[types.ModuleType, types.ModuleType]: """ diff --git a/src/bentoml/grpc/v1/service.proto b/src/bentoml/grpc/v1/service.proto index 7e63f5f4472..f4919adf540 100644 --- a/src/bentoml/grpc/v1/service.proto +++ b/src/bentoml/grpc/v1/service.proto @@ -18,6 +18,43 @@ option py_generic_services = true; service BentoService { // Call handles methodcaller of given API entrypoint. rpc Call(Request) returns (Response) {} + // ServiceMetadata returns metadata of bentoml.Service. + rpc ServiceMetadata(ServiceMetadataRequest) returns (ServiceMetadataResponse) {} +} + +// ServiceMetadataRequest message doesn't take any arguments. +message ServiceMetadataRequest {} + +// ServiceMetadataResponse returns metadata of bentoml.Service. +// Currently it includes name, version, apis, and docs. +message ServiceMetadataResponse { + // DescriptorMetadata is a metadata of any given IODescriptor. + message DescriptorMetadata { + // descriptor_id describes the given ID of the descriptor, which matches with our OpenAPI definition. + optional string descriptor_id = 1; + + // attributes is the kwargs of the given descriptor. + google.protobuf.Struct attributes = 2; + } + // InferenceAPI is bentoml._internal.service.inferece_api.InferenceAPI + // that is exposed to gRPC client. + // There is no way for reflection to get information of given @svc.api. + message InferenceAPI { + // name is the name of the API. + string name = 1; + // input is the input descriptor of the API. + DescriptorMetadata input = 2; + // output is the output descriptor of the API. + DescriptorMetadata output = 3; + // docs is the optional documentation of the API. + optional string docs = 4; + } + // name is the service name. + string name = 1; + // apis holds a list of InferenceAPI of the service. + repeated InferenceAPI apis = 2; + // docs is the documentation of the service. + string docs = 3; } // Request message for incoming Call. diff --git a/src/bentoml/grpc/v1/service_pb2.py b/src/bentoml/grpc/v1/service_pb2.py index 024c9bdb10c..887c9203667 100644 --- a/src/bentoml/grpc/v1/service_pb2.py +++ b/src/bentoml/grpc/v1/service_pb2.py @@ -23,7 +23,7 @@ syntax='proto3', serialized_options=b'\n\023com.bentoml.grpc.v1B\014ServiceProtoP\001Z*github.com/bentoml/bentoml/grpc/v1;service\220\001\001\370\001\001\242\002\003SVC', create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x1d\x62\x65ntoml/grpc/v1/service.proto\x12\x0f\x62\x65ntoml.grpc.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\x85\x03\n\x07Request\x12\x10\n\x08\x61pi_name\x18\x01 \x01(\t\x12+\n\x07ndarray\x18\x03 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x05 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x06 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x07 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\t \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12/\n\tmultipart\x18\n \x01(\x0b\x32\x1a.bentoml.grpc.v1.MultipartH\x00\x12\x1a\n\x10serialized_bytes\x18\x02 \x01(\x0cH\x00\x42\t\n\x07\x63ontentJ\x04\x08\x04\x10\x05J\x04\x08\x0b\x10\x0e\"\xf4\x02\n\x08Response\x12+\n\x07ndarray\x18\x01 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x03 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x05 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\x08 \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12/\n\tmultipart\x18\t \x01(\x0b\x32\x1a.bentoml.grpc.v1.MultipartH\x00\x12\x1a\n\x10serialized_bytes\x18\x02 \x01(\x0cH\x00\x42\t\n\x07\x63ontentJ\x04\x08\x04\x10\x05J\x04\x08\n\x10\x0e\"\xc6\x02\n\x04Part\x12+\n\x07ndarray\x18\x01 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x03 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x05 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\x08 \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12\x1a\n\x10serialized_bytes\x18\x04 \x01(\x0cH\x00\x42\x10\n\x0erepresentationJ\x04\x08\x02\x10\x03J\x04\x08\t\x10\x0e\"\x89\x01\n\tMultipart\x12\x36\n\x06\x66ields\x18\x01 \x03(\x0b\x32&.bentoml.grpc.v1.Multipart.FieldsEntry\x1a\x44\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.bentoml.grpc.v1.Part:\x02\x38\x01\"3\n\x04\x46ile\x12\x11\n\x04kind\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x42\x07\n\x05_kind\"K\n\tDataFrame\x12\x14\n\x0c\x63olumn_names\x18\x01 \x03(\t\x12(\n\x07\x63olumns\x18\x02 \x03(\x0b\x32\x17.bentoml.grpc.v1.Series\"\xa1\x01\n\x06Series\x12\x17\n\x0b\x62ool_values\x18\x01 \x03(\x08\x42\x02\x10\x01\x12\x18\n\x0c\x66loat_values\x18\x02 \x03(\x02\x42\x02\x10\x01\x12\x18\n\x0cint32_values\x18\x03 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x0cint64_values\x18\x06 \x03(\x03\x42\x02\x10\x01\x12\x15\n\rstring_values\x18\x05 \x03(\t\x12\x19\n\rdouble_values\x18\x04 \x03(\x01\x42\x02\x10\x01\"\xc2\x03\n\x07NDArray\x12-\n\x05\x64type\x18\x01 \x01(\x0e\x32\x1e.bentoml.grpc.v1.NDArray.DType\x12\r\n\x05shape\x18\x02 \x03(\x05\x12\x15\n\rstring_values\x18\x05 \x03(\t\x12\x18\n\x0c\x66loat_values\x18\x03 \x03(\x02\x42\x02\x10\x01\x12\x19\n\rdouble_values\x18\x04 \x03(\x01\x42\x02\x10\x01\x12\x17\n\x0b\x62ool_values\x18\x06 \x03(\x08\x42\x02\x10\x01\x12\x18\n\x0cint32_values\x18\x07 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x0cint64_values\x18\x08 \x03(\x03\x42\x02\x10\x01\x12\x19\n\ruint32_values\x18\t \x03(\rB\x02\x10\x01\x12\x19\n\ruint64_values\x18\n \x03(\x04\x42\x02\x10\x01\"\xa9\x01\n\x05\x44Type\x12\x15\n\x11\x44TYPE_UNSPECIFIED\x10\x00\x12\x0f\n\x0b\x44TYPE_FLOAT\x10\x01\x12\x10\n\x0c\x44TYPE_DOUBLE\x10\x02\x12\x0e\n\nDTYPE_BOOL\x10\x03\x12\x0f\n\x0b\x44TYPE_INT32\x10\x04\x12\x0f\n\x0b\x44TYPE_INT64\x10\x05\x12\x10\n\x0c\x44TYPE_UINT32\x10\x06\x12\x10\n\x0c\x44TYPE_UINT64\x10\x07\x12\x10\n\x0c\x44TYPE_STRING\x10\x08\x32M\n\x0c\x42\x65ntoService\x12=\n\x04\x43\x61ll\x12\x18.bentoml.grpc.v1.Request\x1a\x19.bentoml.grpc.v1.Response\"\x00\x42]\n\x13\x63om.bentoml.grpc.v1B\x0cServiceProtoP\x01Z*github.com/bentoml/bentoml/grpc/v1;service\x90\x01\x01\xf8\x01\x01\xa2\x02\x03SVCb\x06proto3' + serialized_pb=b'\n\x1d\x62\x65ntoml/grpc/v1/service.proto\x12\x0f\x62\x65ntoml.grpc.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\x18\n\x16ServiceMetadataRequest\"\xbf\x03\n\x17ServiceMetadataResponse\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x43\n\x04\x61pis\x18\x02 \x03(\x0b\x32\x35.bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI\x12\x0c\n\x04\x64ocs\x18\x03 \x01(\t\x1ao\n\x12\x44\x65scriptorMetadata\x12\x1a\n\rdescriptor_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12+\n\nattributes\x18\x02 \x01(\x0b\x32\x17.google.protobuf.StructB\x10\n\x0e_descriptor_id\x1a\xd1\x01\n\x0cInferenceAPI\x12\x0c\n\x04name\x18\x01 \x01(\t\x12J\n\x05input\x18\x02 \x01(\x0b\x32;.bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata\x12K\n\x06output\x18\x03 \x01(\x0b\x32;.bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata\x12\x11\n\x04\x64ocs\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_docs\"\x85\x03\n\x07Request\x12\x10\n\x08\x61pi_name\x18\x01 \x01(\t\x12+\n\x07ndarray\x18\x03 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x05 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x06 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x07 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\t \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12/\n\tmultipart\x18\n \x01(\x0b\x32\x1a.bentoml.grpc.v1.MultipartH\x00\x12\x1a\n\x10serialized_bytes\x18\x02 \x01(\x0cH\x00\x42\t\n\x07\x63ontentJ\x04\x08\x04\x10\x05J\x04\x08\x0b\x10\x0e\"\xf4\x02\n\x08Response\x12+\n\x07ndarray\x18\x01 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x03 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x05 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\x08 \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12/\n\tmultipart\x18\t \x01(\x0b\x32\x1a.bentoml.grpc.v1.MultipartH\x00\x12\x1a\n\x10serialized_bytes\x18\x02 \x01(\x0cH\x00\x42\t\n\x07\x63ontentJ\x04\x08\x04\x10\x05J\x04\x08\n\x10\x0e\"\xc6\x02\n\x04Part\x12+\n\x07ndarray\x18\x01 \x01(\x0b\x32\x18.bentoml.grpc.v1.NDArrayH\x00\x12/\n\tdataframe\x18\x03 \x01(\x0b\x32\x1a.bentoml.grpc.v1.DataFrameH\x00\x12)\n\x06series\x18\x05 \x01(\x0b\x32\x17.bentoml.grpc.v1.SeriesH\x00\x12%\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x15.bentoml.grpc.v1.FileH\x00\x12,\n\x04text\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.StringValueH\x00\x12&\n\x04json\x18\x08 \x01(\x0b\x32\x16.google.protobuf.ValueH\x00\x12\x1a\n\x10serialized_bytes\x18\x04 \x01(\x0cH\x00\x42\x10\n\x0erepresentationJ\x04\x08\x02\x10\x03J\x04\x08\t\x10\x0e\"\x89\x01\n\tMultipart\x12\x36\n\x06\x66ields\x18\x01 \x03(\x0b\x32&.bentoml.grpc.v1.Multipart.FieldsEntry\x1a\x44\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.bentoml.grpc.v1.Part:\x02\x38\x01\"3\n\x04\x46ile\x12\x11\n\x04kind\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\x42\x07\n\x05_kind\"K\n\tDataFrame\x12\x14\n\x0c\x63olumn_names\x18\x01 \x03(\t\x12(\n\x07\x63olumns\x18\x02 \x03(\x0b\x32\x17.bentoml.grpc.v1.Series\"\xa1\x01\n\x06Series\x12\x17\n\x0b\x62ool_values\x18\x01 \x03(\x08\x42\x02\x10\x01\x12\x18\n\x0c\x66loat_values\x18\x02 \x03(\x02\x42\x02\x10\x01\x12\x18\n\x0cint32_values\x18\x03 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x0cint64_values\x18\x06 \x03(\x03\x42\x02\x10\x01\x12\x15\n\rstring_values\x18\x05 \x03(\t\x12\x19\n\rdouble_values\x18\x04 \x03(\x01\x42\x02\x10\x01\"\xc2\x03\n\x07NDArray\x12-\n\x05\x64type\x18\x01 \x01(\x0e\x32\x1e.bentoml.grpc.v1.NDArray.DType\x12\r\n\x05shape\x18\x02 \x03(\x05\x12\x15\n\rstring_values\x18\x05 \x03(\t\x12\x18\n\x0c\x66loat_values\x18\x03 \x03(\x02\x42\x02\x10\x01\x12\x19\n\rdouble_values\x18\x04 \x03(\x01\x42\x02\x10\x01\x12\x17\n\x0b\x62ool_values\x18\x06 \x03(\x08\x42\x02\x10\x01\x12\x18\n\x0cint32_values\x18\x07 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x0cint64_values\x18\x08 \x03(\x03\x42\x02\x10\x01\x12\x19\n\ruint32_values\x18\t \x03(\rB\x02\x10\x01\x12\x19\n\ruint64_values\x18\n \x03(\x04\x42\x02\x10\x01\"\xa9\x01\n\x05\x44Type\x12\x15\n\x11\x44TYPE_UNSPECIFIED\x10\x00\x12\x0f\n\x0b\x44TYPE_FLOAT\x10\x01\x12\x10\n\x0c\x44TYPE_DOUBLE\x10\x02\x12\x0e\n\nDTYPE_BOOL\x10\x03\x12\x0f\n\x0b\x44TYPE_INT32\x10\x04\x12\x0f\n\x0b\x44TYPE_INT64\x10\x05\x12\x10\n\x0c\x44TYPE_UINT32\x10\x06\x12\x10\n\x0c\x44TYPE_UINT64\x10\x07\x12\x10\n\x0c\x44TYPE_STRING\x10\x08\x32\xb5\x01\n\x0c\x42\x65ntoService\x12=\n\x04\x43\x61ll\x12\x18.bentoml.grpc.v1.Request\x1a\x19.bentoml.grpc.v1.Response\"\x00\x12\x66\n\x0fServiceMetadata\x12\'.bentoml.grpc.v1.ServiceMetadataRequest\x1a(.bentoml.grpc.v1.ServiceMetadataResponse\"\x00\x42]\n\x13\x63om.bentoml.grpc.v1B\x0cServiceProtoP\x01Z*github.com/bentoml/bentoml/grpc/v1;service\x90\x01\x01\xf8\x01\x01\xa2\x02\x03SVCb\x06proto3' , dependencies=[google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,google_dot_protobuf_dot_wrappers__pb2.DESCRIPTOR,]) @@ -84,12 +84,183 @@ ], containing_type=None, serialized_options=None, - serialized_start=1924, - serialized_end=2093, + serialized_start=2400, + serialized_end=2569, ) _sym_db.RegisterEnumDescriptor(_NDARRAY_DTYPE) +_SERVICEMETADATAREQUEST = _descriptor.Descriptor( + name='ServiceMetadataRequest', + full_name='bentoml.grpc.v1.ServiceMetadataRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=112, + serialized_end=136, +) + + +_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA = _descriptor.Descriptor( + name='DescriptorMetadata', + full_name='bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='descriptor_id', full_name='bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata.descriptor_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='attributes', full_name='bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata.attributes', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='_descriptor_id', full_name='bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata._descriptor_id', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=263, + serialized_end=374, +) + +_SERVICEMETADATARESPONSE_INFERENCEAPI = _descriptor.Descriptor( + name='InferenceAPI', + full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='input', full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI.input', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='output', full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI.output', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='docs', full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI.docs', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='_docs', full_name='bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI._docs', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=377, + serialized_end=586, +) + +_SERVICEMETADATARESPONSE = _descriptor.Descriptor( + name='ServiceMetadataResponse', + full_name='bentoml.grpc.v1.ServiceMetadataResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='bentoml.grpc.v1.ServiceMetadataResponse.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='apis', full_name='bentoml.grpc.v1.ServiceMetadataResponse.apis', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='docs', full_name='bentoml.grpc.v1.ServiceMetadataResponse.docs', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA, _SERVICEMETADATARESPONSE_INFERENCEAPI, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=139, + serialized_end=586, +) + + _REQUEST = _descriptor.Descriptor( name='Request', full_name='bentoml.grpc.v1.Request', @@ -178,8 +349,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=113, - serialized_end=502, + serialized_start=589, + serialized_end=978, ) @@ -264,8 +435,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=505, - serialized_end=877, + serialized_start=981, + serialized_end=1353, ) @@ -343,8 +514,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=880, - serialized_end=1206, + serialized_start=1356, + serialized_end=1682, ) @@ -382,8 +553,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1278, - serialized_end=1346, + serialized_start=1754, + serialized_end=1822, ) _MULTIPART = _descriptor.Descriptor( @@ -413,8 +584,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1209, - serialized_end=1346, + serialized_start=1685, + serialized_end=1822, ) @@ -457,8 +628,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1348, - serialized_end=1399, + serialized_start=1824, + serialized_end=1875, ) @@ -496,8 +667,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1401, - serialized_end=1476, + serialized_start=1877, + serialized_end=1952, ) @@ -563,8 +734,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1479, - serialized_end=1640, + serialized_start=1955, + serialized_end=2116, ) @@ -659,10 +830,22 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1643, - serialized_end=2093, + serialized_start=2119, + serialized_end=2569, ) +_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.fields_by_name['attributes'].message_type = google_dot_protobuf_dot_struct__pb2._STRUCT +_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.containing_type = _SERVICEMETADATARESPONSE +_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.oneofs_by_name['_descriptor_id'].fields.append( + _SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.fields_by_name['descriptor_id']) +_SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.fields_by_name['descriptor_id'].containing_oneof = _SERVICEMETADATARESPONSE_DESCRIPTORMETADATA.oneofs_by_name['_descriptor_id'] +_SERVICEMETADATARESPONSE_INFERENCEAPI.fields_by_name['input'].message_type = _SERVICEMETADATARESPONSE_DESCRIPTORMETADATA +_SERVICEMETADATARESPONSE_INFERENCEAPI.fields_by_name['output'].message_type = _SERVICEMETADATARESPONSE_DESCRIPTORMETADATA +_SERVICEMETADATARESPONSE_INFERENCEAPI.containing_type = _SERVICEMETADATARESPONSE +_SERVICEMETADATARESPONSE_INFERENCEAPI.oneofs_by_name['_docs'].fields.append( + _SERVICEMETADATARESPONSE_INFERENCEAPI.fields_by_name['docs']) +_SERVICEMETADATARESPONSE_INFERENCEAPI.fields_by_name['docs'].containing_oneof = _SERVICEMETADATARESPONSE_INFERENCEAPI.oneofs_by_name['_docs'] +_SERVICEMETADATARESPONSE.fields_by_name['apis'].message_type = _SERVICEMETADATARESPONSE_INFERENCEAPI _REQUEST.fields_by_name['ndarray'].message_type = _NDARRAY _REQUEST.fields_by_name['dataframe'].message_type = _DATAFRAME _REQUEST.fields_by_name['series'].message_type = _SERIES @@ -761,6 +944,8 @@ _DATAFRAME.fields_by_name['columns'].message_type = _SERIES _NDARRAY.fields_by_name['dtype'].enum_type = _NDARRAY_DTYPE _NDARRAY_DTYPE.containing_type = _NDARRAY +DESCRIPTOR.message_types_by_name['ServiceMetadataRequest'] = _SERVICEMETADATAREQUEST +DESCRIPTOR.message_types_by_name['ServiceMetadataResponse'] = _SERVICEMETADATARESPONSE DESCRIPTOR.message_types_by_name['Request'] = _REQUEST DESCRIPTOR.message_types_by_name['Response'] = _RESPONSE DESCRIPTOR.message_types_by_name['Part'] = _PART @@ -771,6 +956,36 @@ DESCRIPTOR.message_types_by_name['NDArray'] = _NDARRAY _sym_db.RegisterFileDescriptor(DESCRIPTOR) +ServiceMetadataRequest = _reflection.GeneratedProtocolMessageType('ServiceMetadataRequest', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEMETADATAREQUEST, + '__module__' : 'bentoml.grpc.v1.service_pb2' + # @@protoc_insertion_point(class_scope:bentoml.grpc.v1.ServiceMetadataRequest) + }) +_sym_db.RegisterMessage(ServiceMetadataRequest) + +ServiceMetadataResponse = _reflection.GeneratedProtocolMessageType('ServiceMetadataResponse', (_message.Message,), { + + 'DescriptorMetadata' : _reflection.GeneratedProtocolMessageType('DescriptorMetadata', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEMETADATARESPONSE_DESCRIPTORMETADATA, + '__module__' : 'bentoml.grpc.v1.service_pb2' + # @@protoc_insertion_point(class_scope:bentoml.grpc.v1.ServiceMetadataResponse.DescriptorMetadata) + }) + , + + 'InferenceAPI' : _reflection.GeneratedProtocolMessageType('InferenceAPI', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEMETADATARESPONSE_INFERENCEAPI, + '__module__' : 'bentoml.grpc.v1.service_pb2' + # @@protoc_insertion_point(class_scope:bentoml.grpc.v1.ServiceMetadataResponse.InferenceAPI) + }) + , + 'DESCRIPTOR' : _SERVICEMETADATARESPONSE, + '__module__' : 'bentoml.grpc.v1.service_pb2' + # @@protoc_insertion_point(class_scope:bentoml.grpc.v1.ServiceMetadataResponse) + }) +_sym_db.RegisterMessage(ServiceMetadataResponse) +_sym_db.RegisterMessage(ServiceMetadataResponse.DescriptorMetadata) +_sym_db.RegisterMessage(ServiceMetadataResponse.InferenceAPI) + Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), { 'DESCRIPTOR' : _REQUEST, '__module__' : 'bentoml.grpc.v1.service_pb2' @@ -858,8 +1073,8 @@ index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=2095, - serialized_end=2172, + serialized_start=2572, + serialized_end=2753, methods=[ _descriptor.MethodDescriptor( name='Call', @@ -871,6 +1086,16 @@ serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ServiceMetadata', + full_name='bentoml.grpc.v1.BentoService.ServiceMetadata', + index=1, + containing_service=None, + input_type=_SERVICEMETADATAREQUEST, + output_type=_SERVICEMETADATARESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), ]) _sym_db.RegisterServiceDescriptor(_BENTOSERVICE) diff --git a/src/bentoml/grpc/v1/service_pb2.pyi b/src/bentoml/grpc/v1/service_pb2.pyi index d7996c9cd86..c1c6e2a4065 100644 --- a/src/bentoml/grpc/v1/service_pb2.pyi +++ b/src/bentoml/grpc/v1/service_pb2.pyi @@ -23,6 +23,101 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class ServiceMetadataRequest(google.protobuf.message.Message): + """ServiceMetadataRequest message doesn't take any arguments.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___ServiceMetadataRequest = ServiceMetadataRequest + +class ServiceMetadataResponse(google.protobuf.message.Message): + """ServiceMetadataResponse returns metadata of bentoml.Service. + Currently it includes name, version, apis, and docs. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class DescriptorMetadata(google.protobuf.message.Message): + """DescriptorMetadata is a metadata of any given IODescriptor.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DESCRIPTOR_ID_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + descriptor_id: builtins.str + """descriptor_id describes the given ID of the descriptor, which matches with our OpenAPI definition.""" + @property + def attributes(self) -> google.protobuf.struct_pb2.Struct: + """attributes is the kwargs of the given descriptor.""" + def __init__( + self, + *, + descriptor_id: builtins.str | None = ..., + attributes: google.protobuf.struct_pb2.Struct | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["_descriptor_id", b"_descriptor_id", "attributes", b"attributes", "descriptor_id", b"descriptor_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["_descriptor_id", b"_descriptor_id", "attributes", b"attributes", "descriptor_id", b"descriptor_id"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["_descriptor_id", b"_descriptor_id"]) -> typing_extensions.Literal["descriptor_id"] | None: ... + + class InferenceAPI(google.protobuf.message.Message): + """InferenceAPI is bentoml._internal.service.inferece_api.InferenceAPI + that is exposed to gRPC client. + There is no way for reflection to get information of given @svc.api. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + INPUT_FIELD_NUMBER: builtins.int + OUTPUT_FIELD_NUMBER: builtins.int + DOCS_FIELD_NUMBER: builtins.int + name: builtins.str + """name is the name of the API.""" + @property + def input(self) -> global___ServiceMetadataResponse.DescriptorMetadata: + """input is the input descriptor of the API.""" + @property + def output(self) -> global___ServiceMetadataResponse.DescriptorMetadata: + """output is the output descriptor of the API.""" + docs: builtins.str + """docs is the optional documentation of the API.""" + def __init__( + self, + *, + name: builtins.str = ..., + input: global___ServiceMetadataResponse.DescriptorMetadata | None = ..., + output: global___ServiceMetadataResponse.DescriptorMetadata | None = ..., + docs: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["_docs", b"_docs", "docs", b"docs", "input", b"input", "output", b"output"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["_docs", b"_docs", "docs", b"docs", "input", b"input", "name", b"name", "output", b"output"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["_docs", b"_docs"]) -> typing_extensions.Literal["docs"] | None: ... + + NAME_FIELD_NUMBER: builtins.int + APIS_FIELD_NUMBER: builtins.int + DOCS_FIELD_NUMBER: builtins.int + name: builtins.str + """name is the service name.""" + @property + def apis(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ServiceMetadataResponse.InferenceAPI]: + """apis holds a list of InferenceAPI of the service.""" + docs: builtins.str + """docs is the documentation of the service.""" + def __init__( + self, + *, + name: builtins.str = ..., + apis: collections.abc.Iterable[global___ServiceMetadataResponse.InferenceAPI] | None = ..., + docs: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["apis", b"apis", "docs", b"docs", "name", b"name"]) -> None: ... + +global___ServiceMetadataResponse = ServiceMetadataResponse + class Request(google.protobuf.message.Message): """Request message for incoming Call.""" @@ -481,6 +576,14 @@ class BentoService(google.protobuf.service.Service, metaclass=abc.ABCMeta): callback: collections.abc.Callable[[global___Response], None] | None, ) -> concurrent.futures.Future[global___Response]: """Call handles methodcaller of given API entrypoint.""" + @abc.abstractmethod + def ServiceMetadata( + inst: BentoService, + rpc_controller: google.protobuf.service.RpcController, + request: global___ServiceMetadataRequest, + callback: collections.abc.Callable[[global___ServiceMetadataResponse], None] | None, + ) -> concurrent.futures.Future[global___ServiceMetadataResponse]: + """ServiceMetadata returns metadata of bentoml.Service.""" class BentoService_Stub(BentoService): """a gRPC BentoServer.""" @@ -494,3 +597,10 @@ class BentoService_Stub(BentoService): callback: collections.abc.Callable[[global___Response], None] | None = ..., ) -> concurrent.futures.Future[global___Response]: """Call handles methodcaller of given API entrypoint.""" + def ServiceMetadata( + inst: BentoService_Stub, + rpc_controller: google.protobuf.service.RpcController, + request: global___ServiceMetadataRequest, + callback: collections.abc.Callable[[global___ServiceMetadataResponse], None] | None = ..., + ) -> concurrent.futures.Future[global___ServiceMetadataResponse]: + """ServiceMetadata returns metadata of bentoml.Service.""" diff --git a/src/bentoml/grpc/v1/service_pb2_grpc.py b/src/bentoml/grpc/v1/service_pb2_grpc.py index b2c23fee0fe..58de0699de3 100644 --- a/src/bentoml/grpc/v1/service_pb2_grpc.py +++ b/src/bentoml/grpc/v1/service_pb2_grpc.py @@ -20,6 +20,11 @@ def __init__(self, channel): request_serializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.Request.SerializeToString, response_deserializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.Response.FromString, ) + self.ServiceMetadata = channel.unary_unary( + '/bentoml.grpc.v1.BentoService/ServiceMetadata', + request_serializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataRequest.SerializeToString, + response_deserializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataResponse.FromString, + ) class BentoServiceServicer(object): @@ -33,6 +38,13 @@ def Call(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ServiceMetadata(self, request, context): + """ServiceMetadata returns metadata of bentoml.Service. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_BentoServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -41,6 +53,11 @@ def add_BentoServiceServicer_to_server(servicer, server): request_deserializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.Request.FromString, response_serializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.Response.SerializeToString, ), + 'ServiceMetadata': grpc.unary_unary_rpc_method_handler( + servicer.ServiceMetadata, + request_deserializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataRequest.FromString, + response_serializer=bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'bentoml.grpc.v1.BentoService', rpc_method_handlers) @@ -68,3 +85,20 @@ def Call(request, bentoml_dot_grpc_dot_v1_dot_service__pb2.Response.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ServiceMetadata(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/bentoml.grpc.v1.BentoService/ServiceMetadata', + bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataRequest.SerializeToString, + bentoml_dot_grpc_dot_v1_dot_service__pb2.ServiceMetadataResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/bentoml/grpc/v1/service_pb2_grpc.pyi b/src/bentoml/grpc/v1/service_pb2_grpc.pyi index 041e385e371..78eaa75a2ce 100644 --- a/src/bentoml/grpc/v1/service_pb2_grpc.pyi +++ b/src/bentoml/grpc/v1/service_pb2_grpc.pyi @@ -15,6 +15,11 @@ class BentoServiceStub: bentoml.grpc.v1.service_pb2.Response, ] """Call handles methodcaller of given API entrypoint.""" + ServiceMetadata: grpc.UnaryUnaryMultiCallable[ + bentoml.grpc.v1.service_pb2.ServiceMetadataRequest, + bentoml.grpc.v1.service_pb2.ServiceMetadataResponse, + ] + """ServiceMetadata returns metadata of bentoml.Service.""" class BentoServiceServicer(metaclass=abc.ABCMeta): """a gRPC BentoServer.""" @@ -26,5 +31,12 @@ class BentoServiceServicer(metaclass=abc.ABCMeta): context: grpc.ServicerContext, ) -> bentoml.grpc.v1.service_pb2.Response: """Call handles methodcaller of given API entrypoint.""" + @abc.abstractmethod + def ServiceMetadata( + self, + request: bentoml.grpc.v1.service_pb2.ServiceMetadataRequest, + context: grpc.ServicerContext, + ) -> bentoml.grpc.v1.service_pb2.ServiceMetadataResponse: + """ServiceMetadata returns metadata of bentoml.Service.""" def add_BentoServiceServicer_to_server(servicer: BentoServiceServicer, server: grpc.Server) -> None: ...