Repository: bentoml/BentoML
Pull request #3870 (2023-05-16 19:18:40)

chore(cli): remove path from `bentoml list`
this is a chore, but adding path for models when users do `bentoml models list`
is synonymous to when `bentoml list` also shows path

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3e342c33..a0b9ccb9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -73,7 +73,7 @@ jobs:
         run: |
           npm install -g npm@^7 pyright
       - name: Setup bufbuild/buf
-        uses: bufbuild/buf-setup-action@v1.18.0
+        uses: bufbuild/buf-setup-action@v1.19.0
         with:
           github_token: ${{ github.token }}
       - name: Cache pip dependencies
diff --git a/.gitignore b/.gitignore
index 1dbefce4..13c66cc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -159,3 +159,6 @@ vendor
 *.plan
 *.bin
 yolov5*
+
+# MLflow runs
+mlruns/
diff --git a/docs/source/concepts/deploy.rst b/docs/source/concepts/deploy.rst
index 0f49bc9d..91195eff 100644
--- a/docs/source/concepts/deploy.rst
+++ b/docs/source/concepts/deploy.rst
@@ -6,18 +6,79 @@ Deploying Bento
 Deployment Overview
 -------------------
 
-The three most common deployment options with BentoML are:
+BentoML is designed to provide a unified packaging format, for deploying AI applications
+via a wide range of serving patterns, including real-time inference API, offline batch inference, 
+streaming inference, and custom integrations.
+
+For online API use cases, here are the three most common cloud deployment solutions:
+
+* `☁️ Deploy to BentoCloud <https://www.bentoml.com/>`_
+  - Serverless cloud for AI, the best place to deploy and operate BentoML for AI teams. `Sign up here <https://www.bentoml.com/bento-cloud/>`_ for early access.
+* `🦄️ Deploy on Kubernetes with Yatai <https://github.com/bentoml/Yatai>`_
+  - Cloud-native AI deployment on Kubernetes, comes with advanced auto-scaling 
+  and CI/CD workflows. Requires professional DevOps team to maintain and operate.
+* `🚀 Fast Cloud Deployment with BentoCTL <https://github.com/bentoml/bentoctl>`_
+  - Great for proof-of-concept deployments directly running on public cloud services (EC2, ECS, SageMaker, Lambda, GCP, etc).
+  Requires working knowledge of Cloud Services and their limitations for AI-specific workloads.
+
+
+Feature comparison across deployment options:
+
+.. list-table::
+   :header-rows: 1
+
+   * - Feature
+     - `🍱 BentoCloud <https://www.bentoml.com/>`_
+     - `Yatai on Kubernetes <https://github.com/bentoml/Yatai>`_
+     - Cloud Deployment with `BentoCTL <https://github.com/bentoml/bentoctl>`_ 
+   * - Auto-scaling
+     - ✅ Fast auto-scaling optimized for AI
+     - ✅ Kubernetes-native with custom metrics
+     - Only available on some Cloud Services, e.g. ECS, requires manual configurations
+   * - Scaling-to-zero
+     - ✅ Scaling at individual Model/Runner level
+     - Not supported
+     - Supported on AWS Lambda, GCP Functions with limitations on model size and access to GPU
+   * - GPU Support
+     - ✅
+     - ✅
+     - Supported on EC2, AWS SageMaker, requires manual configurations
+   * - Observability
+     - ✅ Auto-generated dashboards for key metrics
+     - Requires manual configurations
+     - Requires manual configurations with cloud provider
+   * - Endpoint Security
+     - ✅ Access token management and authentication
+     - Requires manual setup
+     - Requires manual setup
+   * - UI and API
+     - ✅ Web UI dashboards, REST API, CLI command, and Python API
+     - ✅ CLI(kubectl) + k8s CRD resource definition
+     - ✅ CLI(bentoctl, terraform)
+   * - CI/CD
+     - ✅ Rich integrated API for programmatic access in CI/CD, support common GitOps and MLOps workflows
+     - ✅ Cloud-native design supporting Kubernetes CRD and GitOps workflow
+     - ✅ Native Terraform integration, easily customizable
+   * - Access control
+     - ✅ Flexible API token management and Role-based access control
+     - Inherits Kubernetes' account and RBAC mechanism, no model/bento/endpoint level access control 
+     - No access control besides basic cloud platform permissions such as creating/deleting resources
+
+
+All three deployment solutions above rely on BentoML's Docker containerization feature
+underneath. In order to ensure a smooth path to production with BentoML, it is important
+to understand the Bento specification, how to run inference with it, and how to build
+docker images from a Bento. This is not only useful for testing a Bento's environment
+and lifecycle configurations, but also for building custom integrations with the BentoML
+eco-system.
 
-- 🐳 Generate container images from Bento for custom docker deployment
-- `🦄️ Yatai <https://github.com/bentoml/Yatai>`_: Model Deployment at scale on Kubernetes
-- `🚀 bentoctl <https://github.com/bentoml/bentoctl>`_: Fast model deployment on any cloud platform
 
 
-Containerize Bentos
--------------------
+Docker Containers
+-----------------
 
-Containerizing bentos as Docker images allows users to easily distribute and deploy
-bentos. Once services are built as bentos and saved to the bento store, we can
+Containerizing bentos as Docker images allows users to easily test out Bento's environment and
+dependency configurations locally. Once Bentos are built and saved to the bento store, we can
 containerize saved bentos with the CLI command :ref:`bentoml containerize <reference/cli:containerize>`.
 
 Start the Docker engine. Verify using ``docker info``.
@@ -73,28 +134,25 @@ Run the generated docker image:
 
 .. code-block:: bash
 
-    $ docker run -p 3000:3000 iris_classifier:ejwnswg5kw6qnuqj serve
+    $ docker run -p 3000:3000 iris_classifier:ejwnswg5kw6qnuqj
+
 
 .. seealso::
 
    :ref:`guides/containerization:Containerization with different container engines.`
    goes into more details on our containerization process and how to use different container runtime.
 
-.. todo::
-
-    - Add sample code for working with GPU and --gpu flag
 
-
-Deploy with Yatai
------------------
+Deploy with Yatai on Kubernetes
+-------------------------------
 
 Yatai helps ML teams to deploy large scale model serving workloads on Kubernetes. It
-standardizes BentoML deployment on Kubernetes, provides UI and APis for managing all
+standardizes BentoML deployment on Kubernetes, provides UI and APIs for managing all
 your ML models and deployments in one place, and enables advanced GitOps and CI/CD
 workflows.
 
-Yatai is Kubernetes native, integrates well with other cloud native tools in the K8s
-eco-system.
+Yatai is Kubernetes native, providing native CRD for managing BentoML deployments, and
+integrates well with other tools in the K8s eco-system.
 
 To get started, get an API token from Yatai Web UI and login from your :code:`bentoml`
 CLI command:
@@ -109,62 +167,8 @@ Push your local Bentos to yatai:
 
     bentoml push iris_classifier:latest
 
-.. tip::
-    Yatai will automatically start building container images for a new Bento pushed.
-
-
-Deploy via Web UI
-^^^^^^^^^^^^^^^^^
-
-Although not always recommended for production workloads, Yatai offers an easy-to-use
-web UI for quickly creating deployments. This is convenient for data scientists to test
-out Bento deployments end-to-end from a development or testing environment:
-
-.. image:: /_static/img/yatai-deployment-creation.png
-    :alt: Yatai Deployment creation UI
-
-The web UI is also very helpful for viewing system status, monitoring services, and
-debugging issues.
-
-.. image:: /_static/img/yatai-deployment-details.png
-    :alt: Yatai Deployment Details UI
-
-Commonly we recommend using APIs or Kubernetes CRD objects to automate the deployment
-pipeline for production workloads.
-
-Deploy via API
-^^^^^^^^^^^^^^
-
-Yatai's REST API specification can be found under the :code:`/swagger` endpoint. If you
-have Yatai deployed locally with minikube, visit:
-http://yatai.127.0.0.1.sslip.io/swagger/. The Swagger API spec covers all core Yatai
-functionalities ranging from model/bento management, cluster management to deployment
-automation.
-
-.. note::
-
-    Python APIs for creating deployment on Yatai is on our roadmap. See :issue:`2405`.
-    Current proposal looks like this:
-
-    .. code-block:: python
-
-        yatai_client = bentoml.YataiClient.from_env()
-
-        bento = yatai_client.get_bento('my_svc:v1')
-        assert bento and bento.status.is_ready()
-
-        yatai_client.create_deployment('my_deployment', bento.tag, ...)
-
-        # For updating a deployment:
-        yatai_client.update_deployment('my_deployment', bento.tag)
-
-        # check deployment_info.status
-        deployment_info = yatai_client.get_deployment('my_deployment')
-
-
-Deploy via kubectl and CRD
-^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Yatai is designed to be a cloud-native tool, providing
 For DevOps managing production model serving workloads along with other kubernetes
 resources, the best option is to use :code:`kubectl` and directly create
 :code:`BentoDeployment` objects in the cluster, which will be handled by the Yatai
@@ -191,8 +195,8 @@ deployment CRD controller.
 
 
 
-Deploy with bentoctl
---------------------
+Deploy with BentoControl
+------------------------
 
 :code:`bentoctl` is a CLI tool for deploying Bentos to run on any cloud platform. It
 supports all major cloud providers, including AWS, Azure, Google Cloud, and many more.
@@ -206,10 +210,8 @@ customizable, users can fine-tune all configurations provided by the cloud platf
 is also extensible, for users to define additional terraform templates to be attached
 to a deployment.
 
-Quick Tour
-^^^^^^^^^^
-
-Install aws-lambda plugin for :code:`bentoctl` as an example:
+Here's an example of using :code:`bentoctl` for deploying to AWS Lambda. First, install
+the `aws-lambda` operator plugin:
 
 .. code-block:: bash
 
@@ -281,9 +283,13 @@ Testing the endpoint deployed:
         $URL
 
 
-Supported Cloud Platforms
+Learn More about BentoCTL
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Check out BentoCTL docs `here <https://github.com/bentoml/bentoctl/blob/main/docs/README.md>`_.
+
+Supported cloud platforms:
+
 - AWS Lambda: https://github.com/bentoml/aws-lambda-deploy
 - AWS SageMaker: https://github.com/bentoml/aws-sagemaker-deploy
 - AWS EC2: https://github.com/bentoml/aws-ec2-deploy
@@ -293,20 +299,9 @@ Supported Cloud Platforms
 - Azure Container Instances: https://github.com/bentoml/azure-container-instances-deploy
 - Heroku: https://github.com/bentoml/heroku-deploy
 
-.. TODO::
-    Explain limitations of each platform, e.g. GPU support
-    Explain how to customize the terraform workflow
 
+Deploy to BentoCloud
+--------------------
 
-About Horizontal Auto-scaling
------------------------------
-
-Auto-scaling is one of the most sought-after features when it comes to deploying models. Autoscaling helps optimize resource usage and cost by automatically provisioning up and scaling down depending on incoming traffic.
-
-Among deployment options introduced in this guide, Yatai on Kubernetes is the
-recommended approach if auto-scaling and resource efficiency are required for your team’s workflow.
-Yatai enables users to fine-tune resource requirements and
-auto-scaling policy at the Runner level, which inherently improves interoperability between auto-scaling and data aggregated at Runner's adaptive batching layer in real-time.
-
-Many of bentoctl’s deployment targets also come with a certain level of auto-scaling
-capabilities, including AWS EC2 and AWS Lambda.
+`BentoCloud <https://www.bentoml.com>`_ is currently under private beta. Please contact
+us by scheduling a demo request `here <https://www.bentoml.com/bento-cloud/>`_.
\ No newline at end of file
diff --git a/examples/custom_runner/torch_hub_yolov5/requirements.txt b/examples/custom_runner/torch_hub_yolov5/requirements.txt
index 40cb5088..ab870bce 100644
--- a/examples/custom_runner/torch_hub_yolov5/requirements.txt
+++ b/examples/custom_runner/torch_hub_yolov5/requirements.txt
@@ -5,7 +5,8 @@ matplotlib>=3.2.2
 numpy>=1.18.5
 opencv-python>=4.1.1
 pandas>=1.1.4
-protobuf<4.21.3  # https://github.com/ultralytics/yolov5/issues/8012
+protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
+ipython
 requests>=2.23.0
 scipy>=1.4.1  # Google Colab version
 seaborn>=0.11.0
diff --git a/examples/custom_runner/torch_hub_yolov5/service.py b/examples/custom_runner/torch_hub_yolov5/service.py
index a6b04a83..ec6d8903 100644
--- a/examples/custom_runner/torch_hub_yolov5/service.py
+++ b/examples/custom_runner/torch_hub_yolov5/service.py
@@ -10,7 +10,7 @@ class Yolov5Runnable(bentoml.Runnable):
     SUPPORTS_CPU_MULTI_THREADING = True
 
     def __init__(self):
-        self.model = torch.hub.load("ultralytics/yolov5", "yolov5s")
+        self.model = torch.hub.load("ultralytics/yolov5:v6.2", "yolov5s")
 
         if torch.cuda.is_available():
             self.model.cuda()
diff --git a/examples/triton/onnx/serve_bento.py b/examples/triton/onnx/serve_bento.py
index e31ee885..5df7adf8 100644
--- a/examples/triton/onnx/serve_bento.py
+++ b/examples/triton/onnx/serve_bento.py
@@ -35,7 +35,7 @@ if __name__ == "__main__":
         )
         try:
             while True:
-                bentoml.client.Client.wait_until_server_is_ready(
+                bentoml.client.Client.wait_until_server_ready(
                     server.host, server.port, 1000
                 )
                 client = bentoml.client.Client.from_url(
diff --git a/examples/triton/pytorch/serve_bento.py b/examples/triton/pytorch/serve_bento.py
index 90a052f4..ab84bf1d 100644
--- a/examples/triton/pytorch/serve_bento.py
+++ b/examples/triton/pytorch/serve_bento.py
@@ -35,7 +35,7 @@ if __name__ == "__main__":
         )
         try:
             while True:
-                bentoml.client.Client.wait_until_server_is_ready(
+                bentoml.client.Client.wait_until_server_ready(
                     server.host, server.port, 1000
                 )
                 client = bentoml.client.Client.from_url(
diff --git a/examples/triton/tensorflow/serve_bento.py b/examples/triton/tensorflow/serve_bento.py
index 4be36c81..aa0e98a3 100644
--- a/examples/triton/tensorflow/serve_bento.py
+++ b/examples/triton/tensorflow/serve_bento.py
@@ -35,7 +35,7 @@ if __name__ == "__main__":
         )
         try:
             while True:
-                bentoml.client.Client.wait_until_server_is_ready(
+                bentoml.client.Client.wait_until_server_ready(
                     server.host, server.port, 1000
                 )
                 client = bentoml.client.Client.from_url(
diff --git a/src/bentoml/_internal/batch/spark.py b/src/bentoml/_internal/batch/spark.py
index 671228d8..6f8c432a 100644
--- a/src/bentoml/_internal/batch/spark.py
+++ b/src/bentoml/_internal/batch/spark.py
@@ -79,7 +79,7 @@ def _get_process(
             pass
 
         server = serve(bento_tag, port=port)
-        Client.wait_until_server_is_ready("localhost", server.port, 30)
+        Client.wait_until_server_ready("localhost", server.port, 30)
         client = HTTPClient(svc, f"http://localhost:{server.port}")
 
         for batch in iterator:
diff --git a/src/bentoml/_internal/frameworks/transformers.py b/src/bentoml/_internal/frameworks/transformers.py
index 303c2da6..6cb1add8 100644
--- a/src/bentoml/_internal/frameworks/transformers.py
+++ b/src/bentoml/_internal/frameworks/transformers.py
@@ -25,6 +25,8 @@ from ..models.model import ModelOptions
 from ..models.model import ModelSignature
 
 if t.TYPE_CHECKING:
+    import torch
+    import tensorflow as tf
     import cloudpickle
     from transformers.models.auto.auto_factory import (
         _BaseAutoModelClass as BaseAutoModelClass,
@@ -49,6 +51,10 @@ if t.TYPE_CHECKING:
     TupleAutoModel = tuple[BaseAutoModelClass, ...]
 
     class PreTrainedProtocol(t.Protocol):
+        @property
+        def framework(self) -> str:
+            ...
+
         def save_pretrained(self, save_directory: str, **kwargs: t.Any) -> None:
             ...
 
@@ -64,6 +70,8 @@ else:
     TupleStr = TupleAutoModel = tuple
     DefaultMapping = SimpleDefaultMapping = ModelDefaultMapping = dict
 
+    torch = LazyLoader("torch", globals(), "torch")
+    tf = LazyLoader("tf", globals(), "tensorflow")
     cloudpickle = LazyLoader(
         "cloudpickle",
         globals(),
@@ -853,7 +861,13 @@ def save_model(
         if metadata is None:
             metadata = {}
 
-        metadata["_pretrained_class"] = pretrained.__class__.__name__
+        metadata.update(
+            {
+                "_pretrained_class": pretrained.__class__.__name__,
+                "_framework": pretrained.framework,
+            }
+        )
+
         with bentoml.models.create(
             name,
             module=MODULE_NAME,
@@ -886,19 +900,56 @@ def get_runnable(bento_model: bentoml.Model) -> type[bentoml.Runnable]:
             super().__init__()
 
             available_gpus = os.getenv("CUDA_VISIBLE_DEVICES", "")
+            # assign CPU resources
+            kwargs = {}
+
             if available_gpus not in ("", "-1"):
                 # assign GPU resources
                 if not available_gpus.isdigit():
                     raise ValueError(
                         f"Expecting numeric value for CUDA_VISIBLE_DEVICES, got {available_gpus}."
                     )
-                else:
-                    kwargs = {"device": int(available_gpus)}
+                if "_pretrained_class" not in bento_model.info.metadata:
+                    # NOTE: then this is a pipeline. We then pass the device to it.
+                    kwargs["device"] = int(available_gpus)
+            if "_pretrained_class" not in bento_model.info.metadata:
+                self.model = load_model(bento_model, **kwargs)
             else:
-                # assign CPU resources
-                kwargs = {}
-
-            self.model = load_model(bento_model, **kwargs)
+                if "_framework" in bento_model.info.metadata:
+                    if "torch" == bento_model.info.metadata["_framework"]:
+                        self.model = t.cast(
+                            transformers.PreTrainedModel,
+                            load_model(bento_model, **kwargs),
+                        ).to(
+                            torch.device(
+                                "cuda" if available_gpus not in ("", "-1") else "cpu"
+                            )
+                        )
+                        torch.set_default_tensor_type("torch.cuda.FloatTensor")
+                    elif "tf" == bento_model.info.metadata["_framework"]:
+                        with tf.device(
+                            "/device:CPU:0"
+                            if available_gpus in ("", "-1")
+                            else f"/device:GPU:{available_gpus}"
+                        ):
+                            self.model = t.cast(
+                                transformers.TFPreTrainedModel,
+                                load_model(bento_model, **kwargs),
+                            )
+                    else:
+                        # NOTE: we need to hide all GPU from TensorFlow, otherwise it will try to allocate
+                        # memory on the GPU and make it unavailable for JAX.
+                        tf.config.experimental.set_visible_devices([], "GPU")
+                        self.model = t.cast(
+                            transformers.FlaxPreTrainedModel,
+                            load_model(bento_model, **kwargs),
+                        )
+                else:
+                    logger.warning(
+                        "Current %s is saved with an older version of BentoML. Setting GPUs on this won't work as expected. Make sure to save it with a newer version of BentoML.",
+                        bento_model,
+                    )
+                    self.model = load_model(bento_model, **kwargs)
 
             # NOTE: backward compatibility with previous BentoML versions.
             self.pipeline = self.model
diff --git a/src/bentoml/_internal/io_descriptors/json.py b/src/bentoml/_internal/io_descriptors/json.py
index 6cd6ea23..dbd77c83 100644
--- a/src/bentoml/_internal/io_descriptors/json.py
+++ b/src/bentoml/_internal/io_descriptors/json.py
@@ -29,6 +29,10 @@ if t.TYPE_CHECKING:
 
     import pydantic
     import pydantic.schema as schema
+
+    if pkg_version_info("pydantic")[0] >= 2:
+        import pydantic.json_schema as jschema
+
     from google.protobuf import message as _message
     from google.protobuf import struct_pb2
     from typing_extensions import Self
@@ -39,6 +43,9 @@ if t.TYPE_CHECKING:
 else:
     pydantic = LazyLoader("pydantic", globals(), "pydantic", exc_msg=EXC_MSG)
     schema = LazyLoader("schema", globals(), "pydantic.schema", exc_msg=EXC_MSG)
+    jschema = LazyLoader(
+        "jschema", globals(), "pydantic.json_schema", exc_msg="Pydantic v2 is required."
+    )
     # lazy load our proto generated.
     struct_pb2 = LazyLoader("struct_pb2", globals(), "google.protobuf.struct_pb2")
     # lazy load numpy for processing ndarray.
@@ -184,10 +191,6 @@ class JSON(
         json_encoder: type[json.JSONEncoder] = DefaultJsonEncoder,
     ):
         if pydantic_model is not None:
-            if pkg_version_info("pydantic")[0] >= 2:
-                raise BadInput(
-                    "pydantic 2.x is not yet supported. Add upper bound to 'pydantic': 'pip install \"pydantic<2\"'"
-                ) from None
             assert issubclass(
                 pydantic_model, pydantic.BaseModel
             ), "'pydantic_model' must be a subclass of 'pydantic.BaseModel'."
@@ -306,15 +309,24 @@ class JSON(
             return Schema(type="object")
 
         # returns schemas from pydantic_model.
-        return Schema(
-            **schema.model_process_schema(
-                self._pydantic_model,
-                model_name_map=schema.get_model_name_map(
-                    schema.get_flat_models_from_model(self._pydantic_model)
-                ),
-                ref_prefix=REF_PREFIX,
-            )[0]
-        )
+        if pkg_version_info("pydantic")[0] >= 2:
+            json_schema = jschema.model_json_schema(
+                self._pydantic_model, ref_template=REF_PREFIX + "{model}"
+            )
+            # NOTE: we don't need def here, as these will be available in openapi.components.
+            if "$defs" in json_schema:
+                json_schema.pop("$defs", None)
+            return Schema(**json_schema)
+        else:
+            return Schema(
+                **schema.model_process_schema(
+                    self._pydantic_model,
+                    model_name_map=schema.get_model_name_map(
+                        schema.get_flat_models_from_model(self._pydantic_model)
+                    ),
+                    ref_prefix=REF_PREFIX,
+                )[0]
+            )
 
     def openapi_components(self) -> dict[str, t.Any] | None:
         if not self._pydantic_model:
@@ -329,7 +341,10 @@ class JSON(
             if LazyType["pydantic.BaseModel"]("pydantic.BaseModel").isinstance(
                 self.sample
             ):
-                return self.sample.dict()
+                if pkg_version_info("pydantic")[0] >= 2:
+                    return self.sample.model_dump()
+                else:
+                    return self.sample.dict()
             elif isinstance(self.sample, (str, list)):
                 return json.dumps(
                     self.sample,
@@ -373,7 +388,10 @@ class JSON(
 
         if self._pydantic_model:
             try:
-                pydantic_model = self._pydantic_model.parse_obj(json_obj)
+                if pkg_version_info("pydantic")[0] >= 2:
+                    pydantic_model = self._pydantic_model.model_validate(json_obj)
+                else:
+                    pydantic_model = self._pydantic_model.parse_obj(json_obj)
                 return pydantic_model
             except pydantic.ValidationError as e:
                 raise BadInput(f"Invalid JSON input received: {e}") from None
@@ -385,7 +403,10 @@ class JSON(
     ):
         # This is to prevent cases where custom JSON encoder is used.
         if LazyType["pydantic.BaseModel"]("pydantic.BaseModel").isinstance(obj):
-            obj = obj.dict()
+            if pkg_version_info("pydantic")[0] >= 2:
+                obj = obj.model_dump()
+            else:
+                obj = obj.dict()
 
         json_str = (
             json.dumps(
@@ -419,7 +440,12 @@ class JSON(
             content = field
             if self._pydantic_model:
                 try:
-                    return self._pydantic_model.parse_raw(content)
+                    if pkg_version_info("pydantic")[0] >= 2:
+                        return self._pydantic_model.model_validate_json(
+                            json.loads(content)
+                        )
+                    else:
+                        return self._pydantic_model.parse_raw(content)
                 except pydantic.ValidationError as e:
                     raise BadInput(f"Invalid JSON input received: {e}") from None
             try:
@@ -432,14 +458,20 @@ class JSON(
 
             if self._pydantic_model:
                 try:
-                    return self._pydantic_model.parse_obj(parsed)
+                    if pkg_version_info("pydantic")[0] >= 2:
+                        return self._pydantic_model.model_validate(parsed)
+                    else:
+                        return self._pydantic_model.parse_obj(parsed)
                 except pydantic.ValidationError as e:
                     raise BadInput(f"Invalid JSON input received: {e}") from None
         return parsed
 
     async def to_proto(self, obj: JSONType) -> struct_pb2.Value:
         if LazyType["pydantic.BaseModel"]("pydantic.BaseModel").isinstance(obj):
-            obj = obj.dict()
+            if pkg_version_info("pydantic")[0] >= 2:
+                obj = obj.model_dump()
+            else:
+                obj = obj.dict()
         msg = struct_pb2.Value()
         return parse_dict_to_proto(obj, msg, json_encoder=self._json_encoder)
 
diff --git a/src/bentoml/_internal/service/openapi/utils.py b/src/bentoml/_internal/service/openapi/utils.py
index d2212531..6e58d28e 100644
--- a/src/bentoml/_internal/service/openapi/utils.py
+++ b/src/bentoml/_internal/service/openapi/utils.py
@@ -9,12 +9,16 @@ from bentoml.exceptions import InvalidArgument
 from bentoml.exceptions import InternalServerError
 
 from ...utils import LazyLoader
+from ...utils.pkg import pkg_version_info
 from .specification import Schema
 
 if TYPE_CHECKING:
     import pydantic
     import pydantic.schema as schema
 
+    if pkg_version_info("pydantic")[0] >= 2:
+        import pydantic.json_schema as jschema
+
     from bentoml.exceptions import BentoMLException
 
 else:
@@ -23,11 +27,33 @@ else:
     )
     pydantic = LazyLoader("pydantic", globals(), "pydantic", exc_msg=_exc_msg)
     schema = LazyLoader("schema", globals(), "pydantic.schema", exc_msg=_exc_msg)
+    jschema = LazyLoader(
+        "jschema", globals(), "pydantic.json_schema", exc_msg="Pydantic v2 is required."
+    )
 
 REF_PREFIX = "#/components/schemas/"
 
 
-def pydantic_components_schema(pydantic_model: t.Type[pydantic.BaseModel]):
+def pydantic_components_schema(pydantic_model: type[pydantic.BaseModel]):
+    if pkg_version_info("pydantic")[0] >= 2:
+        return pydantic_v2_components_schema(pydantic_model)
+    else:
+        return pydantic_v1_components_schema(pydantic_model)
+
+
+def pydantic_v2_components_schema(pydantic_model: type[pydantic.BaseModel]):
+    json_schema = jschema.model_json_schema(
+        pydantic_model, ref_template=REF_PREFIX + "{model}"
+    )
+    defs = json_schema.pop("$defs", None)
+    components: dict[str, Schema] = {pydantic_model.__name__: Schema(**json_schema)}
+    if defs is not None:
+        # NOTE: This is a nested models, hence we will update the definitions
+        components.update({k: Schema(**v) for k, v in defs.items()})
+    return components
+
+
+def pydantic_v1_components_schema(pydantic_model: type[pydantic.BaseModel]):
     flat_models = schema.get_flat_models_from_model(pydantic_model)
     model_name_map = schema.get_model_name_map(flat_models)
 
diff --git a/src/bentoml/_internal/utils/__init__.py b/src/bentoml/_internal/utils/__init__.py
index c635c051..394b1a7b 100644
--- a/src/bentoml/_internal/utils/__init__.py
+++ b/src/bentoml/_internal/utils/__init__.py
@@ -58,7 +58,6 @@ __all__ = [
     "reserve_free_port",
     "LazyLoader",
     "validate_or_create_dir",
-    "display_path_under_home",
     "rich_console",
     "experimental",
     "compose",
@@ -162,16 +161,6 @@ def calc_dir_size(path: PathType) -> int:
     return sum(f.stat().st_size for f in Path(path).glob("**/*") if f.is_file())
 
 
-def display_path_under_home(path: str) -> str:
-    # Shorten path under home directory with leading `~`
-    # e.g. from `/Users/foo/bar` to just `~/bar`
-    try:
-        return str("~" / Path(path).relative_to(Path.home()))
-    except ValueError:
-        # when path is not under home directory, return original full path
-        return path
-
-
 def human_readable_size(size: t.Union[int, float], decimal_places: int = 2) -> str:
     for unit in ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]:
         if size < 1024.0 or unit == "PiB":
diff --git a/src/bentoml/bentos.py b/src/bentoml/bentos.py
index 3c9968ed..5a702d7d 100644
--- a/src/bentoml/bentos.py
+++ b/src/bentoml/bentos.py
@@ -268,8 +268,8 @@ def build(
     name: str | None = None,
     labels: dict[str, str] | None = None,
     description: str | None = None,
-    include: list[str] | None = None,
-    exclude: list[str] | None = None,
+    include: t.List[str] | None = None,
+    exclude: t.List[str] | None = None,
     docker: dict[str, t.Any] | None = None,
     python: dict[str, t.Any] | None = None,
     conda: dict[str, t.Any] | None = None,
diff --git a/src/bentoml_cli/bentos.py b/src/bentoml_cli/bentos.py
index 57accad2..3f39df6b 100644
--- a/src/bentoml_cli/bentos.py
+++ b/src/bentoml_cli/bentos.py
@@ -55,7 +55,6 @@ def add_bento_management_commands(cli: Group):
     from bentoml._internal.utils import calc_dir_size
     from bentoml._internal.utils import human_readable_size
     from bentoml._internal.utils import resolve_user_filepath
-    from bentoml._internal.utils import display_path_under_home
     from bentoml._internal.bento.bento import Bento
     from bentoml._internal.bento.bento import DEFAULT_BENTO_BUILD_FILE
     from bentoml._internal.configuration import get_quiet_mode
@@ -114,7 +113,6 @@ def add_bento_management_commands(cli: Group):
         res = [
             {
                 "tag": str(bento.tag),
-                "path": display_path_under_home(bento.path),
                 "size": human_readable_size(calc_dir_size(bento.path)),
                 "creation_time": bento.info.creation_time.astimezone().strftime(
                     "%Y-%m-%d %H:%M:%S"
@@ -136,13 +134,11 @@ def add_bento_management_commands(cli: Group):
             table.add_column("Tag")
             table.add_column("Size")
             table.add_column("Creation Time")
-            table.add_column("Path")
             for bento in res:
                 table.add_row(
                     bento["tag"],
                     bento["size"],
                     bento["creation_time"],
-                    bento["path"],
                 )
             console.print(table)
 


Test patch:
diff --git a/requirements/tests-requirements.txt b/requirements/tests-requirements.txt
index 2eec8b23..9910c0e3 100644
--- a/requirements/tests-requirements.txt
+++ b/requirements/tests-requirements.txt
@@ -4,13 +4,13 @@ pydantic
 scikit-learn
 yamllint==1.31.0
 black[jupyter]==22.12.0
-coverage[toml]==7.2.4
+coverage[toml]==7.2.5
 setuptools>=63
 isort==5.11.4
-ruff==0.0.267
+ruff==0.0.269
 pytest-cov==4.0.0
 pytest==7.3.1
-pytest-xdist[psutil]==3.3.0
+pytest-xdist[psutil]==3.3.1
 pytest-asyncio==0.21.0
 pyarrow==12.0.0
 tritonclient[all]==2.33.0
diff --git a/tests/unit/_internal/io/test_json.py b/tests/unit/_internal/io/test_json.py
index 81f576ee..63365d84 100644
--- a/tests/unit/_internal/io/test_json.py
+++ b/tests/unit/_internal/io/test_json.py
@@ -16,10 +16,8 @@ import pydantic
 
 from bentoml.io import JSON
 from bentoml.exceptions import BadInput
-from bentoml.exceptions import UnprocessableEntity
 from bentoml.grpc.utils import import_generated_stubs
 from bentoml._internal.utils import LazyLoader
-from bentoml._internal.utils.pkg import pkg_version_info
 from bentoml._internal.io_descriptors.json import DefaultJsonEncoder
 
 if TYPE_CHECKING:
@@ -80,16 +78,6 @@ dumps = partial(
 )
 
 
-@pytest.mark.skipif(
-    pkg_version_info("pydantic")[0] < 2 and pkg_version_info("bentoml")[:2] <= (1, 1),
-    reason="Pydantic 2.x is not yet supported until official releases of Pydantic.",
-)
-def test_not_yet_supported_pydantic():
-    with pytest.raises(UnprocessableEntity) as exc_info:
-        JSON(pydantic_model=Nested)
-    assert "pydantic 2.x is not yet supported" in str(exc_info.value)
-
-
 def test_invalid_init():
     with pytest.raises(AssertionError) as exc_info:
         JSON(pydantic_model=ExampleAttrsClass)  # type: ignore (testing exception)