Validate Request¶
This example shows how to use the TypedMsgPackMixin
to validate the request with the help of msgspec
.
Request validation can provide the following benefits:
The client can know the exact expected data schema from the type definition.
Validation failure will return the details of the failure reason to help the client debug.
Ensure that the service is working on the correct data without fear.
First of all, define the request type with msgspec.Struct
like:
class Request(msgspec.Struct):
media: str
binary: bytes
Then, apply the TypedMsgPackMixin
mixin and add the type you defined to the annotation of forward(self, data)
:
class Inference(TypedMsgPackMixin, Worker):
def forward(self, data: Request):
pass
Note
If you are using dynamic batch inference as the first stage, just use the List[Request]
as the annotation.
You can check the full demo code below.
Server¶
# Copyright 2023 MOSEC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Request validation example."""
from typing import Any, List
from msgspec import Struct
from mosec import Server, Worker
from mosec.mixin import TypedMsgPackMixin
class Request(Struct):
"""User request struct."""
# pylint: disable=too-few-public-methods
bin: bytes
name: str = "test"
class Preprocess(TypedMsgPackMixin, Worker):
"""Dummy preprocess to exit early if the validation failed."""
def forward(self, data: Request) -> Any:
"""Input will be parse as the `Request`."""
print(f"received {data}")
return data.bin
class Inference(TypedMsgPackMixin, Worker):
"""Dummy batch inference."""
def forward(self, data: List[bytes]) -> List[int]:
return [len(buf) for buf in data]
if __name__ == "__main__":
server = Server()
server.append_worker(Preprocess)
server.append_worker(Inference, max_batch_size=16)
server.run()
Client¶
# Copyright 2023 MOSEC Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from http import HTTPStatus
import httpx
import msgspec
req = {
"bin": b"hello mosec",
"name": "type check",
}
resp = httpx.post(
"http://127.0.0.1:8000/inference", content=msgspec.msgpack.encode(req)
)
if resp.status_code == HTTPStatus.OK:
print(f"OK: {msgspec.msgpack.decode(resp.content)}")
else:
print(f"err[{resp.status_code}] {resp.text}")
Test¶
python client.py