dstack icon indicating copy to clipboard operation
dstack copied to clipboard

[Feature]: Support for Pydantic v2

Open movchan74 opened this issue 1 year ago • 11 comments

Problem

I'm encountering a dependency conflict due to incompatible versions of pydantic between dstack and another package my project relies on. Specifically:

  • My project (aana version 0.2.2.2) requires pydantic >=2.0.
  • dstack (version 0.18.17) depends on pydantic >=1.10.10, <2.0.0.

As a result, I'm unable to resolve dependencies when adding dstack alongside other tools in my environment that are already using Pydantic v2.

The broader AI and software community are shifting towards Pydantic v2 for its improved performance and features, and many AI projects have either fully migrated to v2 or at least added support for it. This version conflict is limiting adoption and making it challenging to integrate dstack into modern AI/ML projects that have already upgraded to Pydantic v2.

Solution

No response

Workaround

No response

Would you like to help us implement this feature by sending a PR?

No

movchan74 avatar Oct 16 '24 10:10 movchan74

@movchan74, we have plans to migrate dstack to Pydantic v2. The migration is currently non-trivial since some of the dstack dependencies do not yet support Pydantic v2 (https://github.com/zmievsa/pydantic-duality for sure). We'll explore how it can be done.

You're using the dstack Python API, so you cannot install dstack into a separate venv? Is it the case?

r4victor avatar Oct 16 '24 11:10 r4victor

It's great to hear that migrating to Pydantic v2 is on the roadmap! I understand that it’s non-trivial.

For now, this isn't a major blocker, as I can indeed set up a separate venv for dstack as you suggested. I'm still exploring how best to integrate dstack into our product, so I’ll work with this workaround in the meantime.

Thanks again for the quick response and for considering the upgrade!

movchan74 avatar Oct 16 '24 11:10 movchan74

Will add support to pydantic duality for pydantic 2 this week.

zmievsa avatar Oct 25 '24 18:10 zmievsa

Added it in pydantic-duality==2.0.0

zmievsa avatar Oct 27 '24 21:10 zmievsa

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Nov 27 '24 02:11 github-actions[bot]

Once @r4victor is back, we can discuss how/when we move this forward

peterschmidt85 avatar Nov 27 '24 07:11 peterschmidt85

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Dec 28 '24 01:12 github-actions[bot]

Any idea if this will be addressed within the next few weeks/months? For now we're likely to rely on the CLI instead of using the python API directly, which is not ideal. Thanks!

tomzx avatar Apr 29 '25 21:04 tomzx

Any idea if this will be addressed within the next few weeks/months? For now we're likely to rely on the CLI instead of using the python API directly, which is not ideal. Thanks!

@tomzx We are 100% going to implement this. The plan is to do it by the end of Q2.

peterschmidt85 avatar Apr 30 '25 11:04 peterschmidt85

Pydantic v1 => Pydantic v2 migration guide: https://docs.pydantic.dev/latest/migration/

All Pydantic V1 features are still available in Pydantic v2 via pydantic.v1 imports. So we could migrate to Pydantic V2 initially just by changing imports. Unfortunately, this mode is not compatible with FastAPI, so we need to do full Pydantic V2 migration.

Main changes relevant for dstack codebase:

  1. Set Optional[type] = None
  2. Replace __root__ with RootModel (not sure if it works with pydantic_duality)
  3. Replace deprecated functions/methods:
    1. parse_raw => model_validate_json
    2. parse_obj => model_validate
    3. .json() => model_dump_json
    4. @validator => @field_validator, @classmethod
    5. @root_validator => @model_validator, @classmethod
    6. __get_validators__ => __get_pydantic_core_schema__ or see Adding validation and serialization.
  4. (GenericModel) => (BaseModel, Generic[T])
  5. Drop or rename Field properties (const, regex, ...)
  6. Config => model_config
    1. schema_extra => ConfigDict(json_schema_extra=my_schema_extra)
  7. Consider using @computed_field

r4victor avatar May 28 '25 07:05 r4victor

Replacing __root__ with RootModel is not obvious since RootModel is not compatible with pydantic-duality. In dstack, __root__ models are used to parse unions discriminated by type, e.g.:

class AWSCreds(CoreModel):
    __root__: AnyAWSCreds = Field(..., discriminator="type")

Option 1. Use RootModel without pydantic-duality:

from pydantic import RootModel

class AWSCreds(RootModel):
    root: AnyAWSCreds = Field(..., discriminator="type")

Works out-of-the-box but we would have to duplicate models if we need different extra settings (forbid/ignore) for parsing.

Option 2. Implement custom RootModel compatible with pydantic-duality. Something along the lines:

class CoreRootModel(CoreModel):

    @model_validator(mode="before")
    @classmethod
    def populate_root(cls, values):
        return {'root': values}

    @model_serializer(mode="wrap")
    def _serialize(self, handler, info):
        data = handler(self)
        if info.mode == "json":
            return data["root"]
        else:
            return data

    @classmethod 
    def model_modify_json_schema(cls, json_schema): 
        return json_schema["properties"]["root"]


class AWSCreds(CoreRootModel):
    root: AnyAWSCreds = Field(..., discriminator="type")

Not sure what shortcomings of this implementation may be, so the usage of these models should be limited to top-level parsing and these models should not be included in other models (and there should be no need for that since unions are included directly).

r4victor avatar May 28 '25 07:05 r4victor