Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support additional content types in post requests #362

Open
ktrapeznikov opened this issue Nov 19, 2024 · 3 comments
Open

Support additional content types in post requests #362

ktrapeznikov opened this issue Nov 19, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@ktrapeznikov
Copy link

ktrapeznikov commented Nov 19, 2024


🚀 Feature

Add support to additional content-type requests such as application/octet-stream

Motivation

It will make the Litserv more flexible. It will also allow streaming binary data.

Pitch

Basically add another option to the lines here

if self.request_type == Request:

that returns request.body()

if self.request_type == Request:
        if request.headers["Content-Type"] == "application/x-www-form-urlencoded" or request.headers[
            "Content-Type"
        ].startswith("multipart/form-data"):
            payload = await request.form()
        elif request.headers["Content-Type"] == "application/octet-stream":
            payload = await request.body()
        else:
            payload = await request.json()

to support the following request

response = requests.post(API_URL, data = input_bytes, headers = {"Content-Type": "application/octet-stream"})

Alternatives

Additional context

@ktrapeznikov ktrapeznikov added the enhancement New feature or request label Nov 19, 2024
@ktrapeznikov ktrapeznikov changed the title Support addition content types in post requests Support additional content types in post requests Nov 19, 2024
@aniketmaurya
Copy link
Collaborator

aniketmaurya commented Nov 19, 2024

hi @ktrapeznikov, could you also share the use-case/example where you would need this format?

@ktrapeznikov
Copy link
Author

@aniketmaurya for example when using with amazon sagemaker async endpoint, an input provided as a location in an s3 bucket. AWS has some internal processors that read the file and sends it in a request. There is not much control on how the request is constructed. It basically just reads the file as bytes and sends in a body. It does not construct "multipart/form-data" request.

@PhilippeChatigny
Copy link

@ktrapeznikov
Here's a workaround if you want to send bytes. I am using using safetensors as a way to serialize here but it can be any bytes.

import litserve as ls
import safetensors.numpy as packer
from fastapi import HTTPException
from fastapi.responses import Response
class BaseLitAPI(ls.LitAPI):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def setup(self, device):
        self.device = device

    def decode_request(self, request): # do not put bytes as a typehint here!
        uploaded_file = request.get("file")
        if not uploaded_file:
            raise HTTPException(
                status_code=400, detail="File field is missing in form data"
            )
        file_bytes = uploaded_file.file.read()
        input_tensor = packer.load(file_bytes)["input"]
        return input_tensor

    def predict(self, x):
      return x

    def encode_response(self, output):
      response = packer.save({"output": output})
      return  Response(content=response, media_type="application/octet-stream")


if __name__ == "__main__":
    api = BaseLitAPI()
    server = ls.LitServer(api, accelerator="auto")
    server.run(port=8000)

    ## run the following client side ##
    import numpy as np
    import requests
    import safetensors.numpy as packer

    example_payload = {"input": np.random.randn(2, 10).astype(np.float32)}
    payload = packer.save(example_payload) 
    payload = {"file": payload}
    output = requests.post("http://127.0.0.1:8000/predict", files=payload)
    output = packer.load(output.content)

for litserve version 0.2.5, the content has to be a file or a JSON. Anything else by default will be parsed as JSON by default or return a 422 HTTP code if you modify the typehint of def decode_request(self, request). e.g.: def decode_request(self, request: bytes).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants