Implementation:Bentoml BentoML IO Descriptor Multipart
| Knowledge Sources | |
|---|---|
| Domains | IO Descriptors, Multipart Encoding, API Specification |
| Last Updated | 2026-02-13 15:00 GMT |
Overview
The Multipart IO descriptor defines API specification for BentoML services that accept or return multipart form data, composing multiple child IO descriptors into a single request/response.
Description
The Multipart class is an IO descriptor that handles multipart/form-data inputs and outputs for BentoML services. It extends IODescriptor[Dict[str, Any]] and composes multiple child IO descriptors (such as NumpyNdarray, JSON, Image, Text, File) into named fields of a multipart request. Key features include:
- Nested descriptor composition: Accepts keyword arguments where each key is a field name and each value is an
IODescriptorinstance. Nested Multipart descriptors are explicitly disallowed with a validation check in__init__. - HTTP request parsing: Uses
populate_multipart_requeststo split multipart form data into individual sub-requests per field. If field names do not match, falls back to positional matching by iterating through form values and descriptors in order. - HTTP response generation: Concurrently converts all field values to individual responses via
asyncio.gather, then assembles them into a multipart response usingconcat_to_multipart_response. - gRPC protobuf serialization: Converts between
pb.Multipartprotobuf messages and dictionaries. Usesvalidate_proto_fieldsto determine which protobuf representation each child descriptor expects. Supports both v1 and v1alpha1 protocol versions through_to_proto_impl. - Input validation: The
validate_input_mappingmethod checks that provided field keys match the declared descriptor keys, raisingInvalidArgumentfor unknown fields. - OpenAPI schema generation: Produces an object schema with properties derived from each child descriptor's schema. Aggregates component schemas from all child descriptors.
- from_sample not supported: The
_from_samplemethod raisesNotImplementedErrorsince multipart descriptors cannot be constructed from a single sample.
Usage
Use this descriptor when a BentoML service endpoint needs to accept or return multiple different data types simultaneously, such as an image along with JSON metadata, or a numpy array paired with annotation data.
Code Reference
Source Location
- Repository: Bentoml_BentoML
- File: src/bentoml/_internal/io_descriptors/multipart.py
- Lines: 1-366
Signature
class Multipart(
IODescriptor[t.Dict[str, t.Any]],
descriptor_id="bentoml.io.Multipart",
proto_fields=("multipart",),
):
def __init__(self, **inputs: IODescriptor[t.Any]): ...
def _from_sample(cls, sample: dict[str, t.Any]) -> t.Any: ...
def input_type(self) -> dict[str, t.Type[t.Any] | UnionType | LazyType[t.Any]]: ...
def to_spec(self) -> dict[str, t.Any]: ...
@classmethod
def from_spec(cls, spec: dict[str, t.Any]) -> t.Self: ...
def openapi_schema(self) -> Schema: ...
def openapi_components(self) -> dict[str, t.Any] | None: ...
def openapi_example(self) -> t.Any: ...
def openapi_request_body(self) -> dict[str, t.Any]: ...
def openapi_responses(self) -> OpenAPIResponse: ...
async def from_http_request(self, request: Request) -> dict[str, t.Any]: ...
async def to_http_response(self, obj: dict[str, t.Any], ctx: Context | None = None) -> Response: ...
def validate_input_mapping(self, field: t.MutableMapping[str, t.Any]) -> None: ...
async def from_proto(self, field: pb.Multipart) -> dict[str, t.Any]: ...
async def to_proto(self, obj: dict[str, t.Any]) -> pb.Multipart: ...
async def to_proto_v1alpha1(self, obj: dict[str, t.Any]) -> pb_v1alpha1.Multipart: ...
Import
from bentoml.io import Multipart
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| **inputs | IODescriptor[Any] | Yes | Keyword arguments where each key is a field name and each value is an IODescriptor instance (e.g., NumpyNdarray, JSON, Image, Text, File). Nested Multipart is not allowed. |
Outputs
| Name | Type | Description |
|---|---|---|
| dict[str, Any] | dict | A dictionary mapping field names to their deserialized values, where each value is determined by the corresponding child IO descriptor. |
Usage Examples
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import bentoml
from bentoml.io import NumpyNdarray, Multipart, JSON
if TYPE_CHECKING:
from numpy.typing import NDArray
runner = bentoml.sklearn.get("sklearn_model_clf").to_runner()
svc = bentoml.legacy.Service("iris-classifier", runners=[runner])
input_spec = Multipart(arr=NumpyNdarray(), annotations=JSON())
output_spec = Multipart(output=NumpyNdarray(), result=JSON())
@svc.api(input=input_spec, output=output_spec)
async def predict(
arr: NDArray[Any], annotations: dict[str, Any]
) -> dict[str, NDArray[Any] | dict[str, Any]]:
res = await runner.run(arr)
return {"output": res, "result": annotations}