Repository: bentoml/BentoML
Pull request #5221 (2025-02-19 02:50:07)

fix: input data validation for root input
Signed-off-by: Frost Ming <me@frostming.com>

## What does this PR address?

<!--
Thanks for sending a pull request!

Congrats for making it this far! Here's a 🍱 for you. There are still a few steps ahead.

Please make sure to read the contribution guidelines, then fill out the blanks below before requesting a code review.

Name your Pull Request with one of the following prefixes, e.g. "feat: add support for PyTorch", to indicate the type of changes proposed. This is based on the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/#summary).
  - feat: (new feature for the user, not a new feature for build script)
  - fix: (bug fix for the user, not a fix to a build script)
  - docs: (changes to the documentation)
  - style: (formatting, missing semicolons, etc; no production code change)
  - refactor: (refactoring production code, eg. renaming a variable)
  - perf: (code changes that improve performance)
  - test: (adding missing tests, refactoring tests; no production code change)
  - chore: (updating grunt tasks etc; no production code change)
  - build: (changes that affect the build system or external dependencies)
  - ci: (changes to configuration files and scripts)
  - revert: (reverts a previous commit)

Describe your changes in detail. Attach screenshots here if appropriate.

Once you're done with this, someone from BentoML team or community member will help review your PR (see "Who can help review?" section for potential reviewers.). If no one has reviewed your PR after a week have passed, don't hesitate to post a new comment and ping @-the same person. Notifications sometimes get lost 🥲.
-->

<!-- Remove if not applicable -->

Fixes #(issue)

## Before submitting:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
<!--- If you plan to update documentation or tests in follow-up, please note -->

- [ ] Does the Pull Request follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/#summary) naming? Here are [GitHub's
      guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) on how to create a pull request.
- [ ] Does the code follow BentoML's code style, `pre-commit run -a` script has passed ([instructions](https://github.com/bentoml/BentoML/blob/main/DEVELOPMENT.md#style-check-auto-formatting-type-checking))?
- [ ] Did you read through [contribution guidelines](https://github.com/bentoml/BentoML/blob/main/CONTRIBUTING.md#ways-to-contribute) and follow [development guidelines](https://github.com/bentoml/BentoML/blob/main/DEVELOPMENT.md#start-developing)?
- [ ] Did your changes require updates to the documentation? Have you updated
      those accordingly? Here are [documentation guidelines](https://github.com/bentoml/BentoML/tree/main/docs) and [tips on writting docs](https://github.com/bentoml/BentoML/tree/main/docs#writing-documentation).
- [ ] Did you write tests to cover your changes?



Patch:
diff --git a/src/_bentoml_sdk/gradio.py b/src/_bentoml_sdk/gradio.py
index a1c915a8..79401214 100644
--- a/src/_bentoml_sdk/gradio.py
+++ b/src/_bentoml_sdk/gradio.py
@@ -39,10 +39,10 @@ def mount_gradio_app(blocks: Blocks, path: str, name: str = "gradio_ui"):
         class MyService: ...
 
     """
-    from _bentoml_impl import server
     from _bentoml_sdk.service import Service
+    from bentoml._internal import server
 
-    favicon_path = (
+    favicon_path = str(
         Path(server.__file__).parent / "static_content" / "favicon-light-32x32.png"
     )
     assert path.startswith("/"), "Routed paths must start with '/'"
@@ -51,6 +51,7 @@ def mount_gradio_app(blocks: Blocks, path: str, name: str = "gradio_ui"):
     def decorator(obj: R) -> R:
         blocks.dev_mode = False
         blocks.show_error = True
+        blocks.max_file_size = None
         blocks.validate_queue_settings()
         blocks.root_path = path
         blocks.favicon_path = favicon_path
diff --git a/src/_bentoml_sdk/io_models.py b/src/_bentoml_sdk/io_models.py
index ba10279f..cf16aa02 100644
--- a/src/_bentoml_sdk/io_models.py
+++ b/src/_bentoml_sdk/io_models.py
@@ -474,7 +474,8 @@ class IORootModel(IODescriptor, t.Generic[RootModelRootType]):
     def model_validate_json(
         cls, json_data: str | bytes | bytearray, **kwargs: t.Any
     ) -> t.Self:
-        if getattr(cls, "__root_input__", False):
+        json_schema = cls.model_json_schema()
+        if json_schema.get("type") in ("string", "file"):
             parsed = json_data
         else:
             parsed = json.loads(json_data)


Test patch:
diff --git a/tests/e2e/bento_new_sdk/test_asgi.py b/tests/e2e/bento_new_sdk/test_asgi.py
index a3795d33..4f439054 100644
--- a/tests/e2e/bento_new_sdk/test_asgi.py
+++ b/tests/e2e/bento_new_sdk/test_asgi.py
@@ -27,6 +27,10 @@ class RootInput:
     def read(self, path: Path, /) -> str:
         return path.read_text()
 
+    @bentoml.api
+    def chat(self, messages: list[str], /) -> str:
+        return " ".join(messages)
+
 
 class InputModel(pydantic.BaseModel):
     key: str
@@ -77,6 +81,11 @@ def test_service_root_input(tmp_path: Path):
         assert resp.status_code == 200
         assert resp.text == "hello"
 
+        resp = client.post("/chat", json=["hello", "world"])
+
+        assert resp.status_code == 200
+        assert resp.text == "hello world"
+
     with TestClient(app=RootModelInput.to_asgi()) as client:
         resp = client.post("/get", json={"key": "hello"})