Skip to content

[ray serve] incompatibility with pydantic >=2.0  #39722

@Yangqing

Description

@Yangqing

What happened + What you expected to happen

Basically, I was trying to run the ray serve example code in the documentation with fastapi integration:

from fastapi import FastAPI, WebSocket, WebSocketDisconnect

from ray import serve


app = FastAPI()


@serve.deployment
@serve.ingress(app)
class EchoServer:
    @app.websocket("/")
    async def echo(self, ws: WebSocket):
        await ws.accept()

        try:
            while True:
                text = await ws.receive_text()
                await ws.send_text(text)
        except WebSocketDisconnect:
            print("Client disconnected.")


serve_app = serve.run(EchoServer.bind())

expecting it would work out of box. And it produces pydantic user error as follows:

---------------------------------------------------------------------------
PydanticUserError                         Traceback (most recent call last)
Cell In[3], line 3
      1 from fastapi import FastAPI, WebSocket, WebSocketDisconnect
----> 3 from ray import serve
      6 app = FastAPI()
      9 @serve.deployment
     10 @serve.ingress(app)
     11 class EchoServer:

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/__init__.py:4
      1 import ray._private.worker
      3 try:
----> 4     from ray.serve.api import (
      5         build,
      6         deployment,
      7         get_deployment,
      8         get_replica_context,
      9         ingress,
     10         list_deployments,
     11         run,
     12         shutdown,
     13         start,
     14         delete,
     15         Application,
     16         BuiltApplication,
     17         Deployment,
     18         multiplexed,
     19         get_multiplexed_model_id,
     20     )
     21     from ray.serve.air_integrations import PredictorDeployment
     22     from ray.serve.batching import batch

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/api.py:15
     12 from ray.dag import DAGNode
     13 from ray.util.annotations import Deprecated, PublicAPI
---> 15 from ray.serve.built_application import BuiltApplication
     16 from ray.serve._private.client import ServeControllerClient
     17 from ray.serve.config import AutoscalingConfig, DeploymentConfig, HTTPOptions

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/built_application.py:7
      1 from typing import (
      2     Dict,
      3     Optional,
      4     List,
      5 )
----> 7 from ray.serve.deployment import Deployment
      8 from ray.util.annotations import PublicAPI
     11 @PublicAPI(stability="alpha")
     12 class ImmutableDeploymentDict(dict):

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/deployment.py:14
      4 from typing import (
      5     Any,
      6     Callable,
   (...)
     10     Union,
     11 )
     12 from ray._private.usage.usage_lib import TagKey, record_extra_usage_tag
---> 14 from ray.serve.context import get_global_client
     15 from ray.dag.dag_node import DAGNodeBase
     16 from ray.dag.class_node import ClassNode

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/context.py:12
     10 import ray
     11 from ray.exceptions import RayActorError
---> 12 from ray.serve._private.client import ServeControllerClient
     13 from ray.serve._private.common import ReplicaTag
     14 from ray.serve._private.constants import SERVE_CONTROLLER_NAME, SERVE_NAMESPACE

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/_private/client.py:25
     18 from ray.serve.config import DeploymentConfig, HTTPOptions
     19 from ray.serve._private.constants import (
     20     CLIENT_POLLING_INTERVAL_S,
     21     CLIENT_CHECK_CREATION_POLLING_INTERVAL_S,
     22     MAX_CACHED_HANDLES,
     23     SERVE_DEFAULT_APP_NAME,
     24 )
---> 25 from ray.serve._private.deploy_utils import get_deploy_args
     26 from ray.serve.controller import ServeController
     27 from ray.serve.exceptions import RayServeException

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/_private/deploy_utils.py:8
      5 import time
      7 from ray.serve.config import ReplicaConfig, DeploymentConfig
----> 8 from ray.serve.schema import ServeApplicationSchema
      9 from ray.serve._private.constants import SERVE_LOGGER_NAME
     10 from ray.serve._private.common import DeploymentInfo

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/schema.py:134
    124                     raise ValueError(
    125                         "runtime_envs in the Serve config support only "
    126                         "remote URIs in working_dir and py_modules. Got "
    127                         f"error when parsing URI: {e}"
    128                     )
    130         return v
    133 @PublicAPI(stability="beta")
--> 134 class DeploymentSchema(
    135     BaseModel, extra=Extra.forbid, allow_population_by_field_name=True
    136 ):
    137     """
    138     Specifies options for one deployment within a Serve application. For each deployment
    139     this can optionally be included in `ServeApplicationSchema` to override deployment
    140     options specified in code.
    141     """
    143     name: str = Field(
    144         ..., description=("Globally-unique name identifying this deployment.")
    145     )

File ~/anaconda3/lib/python3.8/site-packages/ray/serve/schema.py:242, in DeploymentSchema()
    231 ray_actor_options: RayActorOptionsSchema = Field(
    232     default=DEFAULT.VALUE, description="Options set for each replica actor."
    233 )
    235 is_driver_deployment: bool = Field(
    236     default=DEFAULT.VALUE,
    237     description="Indicate Whether the deployment is driver deployment "
    238     "Driver deployments are spawned one per node.",
    239 )
    241 @root_validator
--> 242 def num_replicas_and_autoscaling_config_mutually_exclusive(cls, values):
    243     if values.get("num_replicas", None) not in [DEFAULT.VALUE, None] and values.get(
    244         "autoscaling_config", None
    245     ) not in [DEFAULT.VALUE, None]:
    246         raise ValueError(
    247             "Manually setting num_replicas is not allowed "
    248             "when autoscaling_config is provided."
    249         )

File ~/anaconda3/lib/python3.8/site-packages/pydantic/deprecated/class_validators.py:222, in root_validator(pre, skip_on_failure, allow_reuse, *__args)
    212 warn(
    213     'Pydantic V1 style `@root_validator` validators are deprecated.'
    214     ' You should migrate to Pydantic V2 style `@model_validator` validators,'
   (...)
    217     stacklevel=2,
    218 )
    220 if __args:
    221     # Ensure a nice error is raised if someone attempts to use the bare decorator
--> 222     return root_validator()(*__args)  # type: ignore
    224 if allow_reuse is True:  # pragma: no cover
    225     warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning)

File ~/anaconda3/lib/python3.8/site-packages/pydantic/deprecated/class_validators.py:228, in root_validator(pre, skip_on_failure, allow_reuse, *__args)
    226 mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
    227 if pre is False and skip_on_failure is not True:
--> 228     raise PydanticUserError(
    229         'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
    230         ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
    231         code='root-validator-pre-skip',
    232     )
    234 wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
    236 def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:

PydanticUserError: If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`. Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.

For further information visit https://errors.pydantic.dev/2.3/u/root-validator-pre-skip

I believe there is the general compatibility issue reported by others in e.g. #37372 and #37019, but just submitting this to signal the bug specifically wrt ray serve. Hope it is helpful!

Versions / Dependencies

ray: 2.6.3 (default one shipped in the anyscale platform)
pydantic: 2.3.0

Reproduction script

Do "pip install -U pydantic" to update to 2.x.

Then, run the example code at https://docs.ray.io/en/latest/serve/http-guide.html#fastapi-http-deployments

import ray
import requests
from fastapi import FastAPI
from ray import serve

app = FastAPI()


@serve.deployment(route_prefix="/hello")
@serve.ingress(app)
class MyFastAPIDeployment:
    @app.get("/")
    def root(self):
        return "Hello, world!"


serve.run(MyFastAPIDeployment.bind())
resp = requests.get("http://localhost:8000/hello")
assert resp.json() == "Hello, world!"

Issue Severity

None

Metadata

Metadata

Labels

P1Issue that should be fixed within a few weeksbugSomething that is supposed to be working; but isn'tserveRay Serve Related Issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions