From 970c5b2966abdc4c08a11fef9a0f6406162682d3 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Wed, 7 Dec 2022 00:18:39 -0800 Subject: [PATCH] feat(grpc): adding service metadata (#3278) --- .../server/grpc/servicer/v1/__init__.py | 64 ++++- 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 + 6 files changed, 503 insertions(+), 25 deletions(-) diff --git a/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py b/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py index 7886d828ee..224f2b9878 100644 --- a/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py +++ b/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py @@ -1,12 +1,13 @@ from __future__ import annotations import sys +import typing as t import asyncio import logging from typing import TYPE_CHECKING import anyio - +from .....utils import LazyLoader from ......exceptions import InvalidArgument from ......exceptions import BentoMLException from ......grpc.utils import import_grpc @@ -20,7 +21,7 @@ from logging import _ExcInfoType as ExcInfoType # type: ignore (private warning) import grpc - + from google.protobuf import struct_pb2 from ......grpc.v1 import service_pb2 as pb from ......grpc.v1 import service_pb2_grpc as services from ......grpc.types import BentoServicerContext @@ -28,6 +29,7 @@ else: grpc, _ = import_grpc() pb, services = import_generated_stubs(version="v1") + struct_pb2 = LazyLoader("struct_pb2", globals(), "google.protobuf.struct_pb2") def log_exception(request: pb.Request, exc_info: ExcInfoType) -> None: @@ -97,4 +99,62 @@ async def Call( # type: ignore (no async types) # pylint: disable=invalid-overr ) return response + async def ServiceMetadata( # type: ignore (no async types) # pylint: disable=invalid-overridden-method + self, + request: pb.ServiceMetadataRequest, # pylint: disable=unused-argument + context: BentoServicerContext, # pylint: disable=unused-argument + ) -> pb.ServiceMetadataResponse: + return pb.ServiceMetadataResponse( + name=service.name, + docs=service.doc, + apis=[ + pb.ServiceMetadataResponse.InferenceAPI( + name=api.name, + docs=api.doc, + input=make_descriptor_spec( + api.input.to_spec(), pb.ServiceMetadataResponse + ), + output=make_descriptor_spec( + api.output.to_spec(), pb.ServiceMetadataResponse + ), + ) + for api in service.apis.values() + ], + ) + return BentoServiceImpl() + + +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_descriptor_spec( + spec: dict[str, t.Any], pb: type[pb.ServiceMetadataResponse] +) -> pb.ServiceMetadataResponse.DescriptorMetadata: + from .....io_descriptors.json import parse_dict_to_proto + + descriptor_id = spec.pop("id") + return pb.DescriptorMetadata( + descriptor_id=descriptor_id, + attributes=struct_pb2.Struct( + fields={ + key: parse_dict_to_proto(_tuple_converter(value), struct_pb2.Value()) + for key, value in spec.items() + } + ), + ) diff --git a/src/bentoml/grpc/v1/service.proto b/src/bentoml/grpc/v1/service.proto index 7e63f5f447..f4919adf54 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 024c9bdb10..887c920366 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 d7996c9cd8..c1c6e2a406 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 b2c23fee0f..58de0699de 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 041e385e37..78eaa75a2c 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: ...