bentoml / BentoML

The easiest way to serve AI apps and models - Build reliable Inference APIs, LLM apps, Multi-model chains, RAG service, and much more!
https://bentoml.com
Apache License 2.0
7.02k stars 781 forks source link

bug: Unable to upload audio file with Content-Type: audio/mpeg in io-descriptors example #5006

Open e1ijah1 opened 3 days ago

e1ijah1 commented 3 days ago

Describe the bug

When using the AudioSpeedUp example from the io-descriptors folder, uploading an audio file fails with both Content-Type: audio/mpeg and without a specified Content-Type. This indicates a problem with audio file handling in the io-descriptors implementation.

To reproduce

  1. Clone the BentoML repository and navigate to the examples/io-descriptors folder.

  2. Start the AudioSpeedUp service:

    bentoml serve service:AudioSpeedUp
  3. Attempt to upload an audio file using the following curl command:

    curl -i -XPOST 127.0.0.1:3000/speed_up_audio -H 'Content-Type: audio/mpeg' -F 'audio=@/path/to/audio.mp3'

The server returns a 500 status code with the following traceback:

Traceback (most recent call last):
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_impl/server/app.py", line 562, in api_endpoint_wrapper
    resp = await self.api_endpoint(name, request)
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_impl/server/app.py", line 621, in api_endpoint
    serde = ALL_SERDE[media_type]()
KeyError: 'audio/mpeg'

If the Content-Type header is omitted, a different error occurs:

curl -i -XPOST 127.0.0.1:3000/speed_up_audio -F 'audio=@/path/to/audio.mp3'

This results in a 400 error with the following traceback:

Traceback (most recent call last):
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_impl/server/app.py", line 562, in api_endpoint_wrapper
    resp = await self.api_endpoint(name, request)
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_impl/server/app.py", line 622, in api_endpoint
    input_data = await method.input_spec.from_http_request(request, serde)
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_sdk/io_models.py", line 171, in from_http_request
    return await serde.parse_request(request, t.cast(t.Type[IODescriptor], cls))
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/_bentoml_impl/serde.py", line 205, in parse_request
    return cls.model_validate(data)
  File "/root/miniconda3/envs/infer_dev/lib/python3.10/site-packages/pydantic/main.py", line 532, in model_validate
    return cls.__pydantic_validator__.validate_python(
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Input
audio
  Value error, Invalid content type application/octet-stream, expected audio/mpeg [type=value_error, input_value=UploadFile(filename='audio...ication/octet-stream'})), input_type=UploadFile]
    For further information visit https://errors.pydantic.dev/2.7/v/value_error
velocity
  Field required [type=missing, input_value={'audio': UploadFile(file...cation/octet-stream'}))}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.7/v/missing

This issue seems to be related to a lack of proper validation for audio file uploads in BentoML. A similar issue has been reported previously: https://github.com/bentoml/BentoML/issues/4651

Expected behavior

The service should accept the audio file and process it as intended.

Environment

bentoml==1.3.5

Python 3.10.14

Platform: CentOS 7

parano commented 2 days ago

The client CURL command for that example service should be:

curl -X 'POST' \
  'http://localhost:3000/speed_up_audio' \
  -H 'accept: audio/mp3' \
  -H 'Content-Type: multipart/form-data' \
  -F 'audio=@example.mp3;type=audio/mpeg' \
  -F 'velocity=2' \
  -o output.mp3

If you're not sure what's the API specification, try visit http://localhost:3000/ to send request with the built-in OpenAPI UI, or download the OpenAPI spec json from http://localhost:3000/docs.json.

Screenshot 2024-09-29 at 9 31 54 PM

BentoML's built-in Python client is also very easy to use. The client call simply matches the API function definition in the service:

import bentoml
from pathlib import Path

with bentoml.SyncHTTPClient("http://localhost:3000") as client:
    result = client.speed_up_audio(
        audio=Path("example.mp3"),
       velocity=2,
    )