Skip to content

types: provide a version-scoped Pydantic extension surface #1050

Description

@bokelley

Problem

#1048 adds valuable generated .pyi stubs for version-scoped models, but it
intentionally leaves their runtime implementation as
RootModel[dict[str, Any]]. The module documentation explicitly says that
subclassing to add Pydantic fields is unsupported and recommends composing
adopter state beside the boundary model.

That leaves the extension half of #1046 unresolved for seller frameworks whose
public schemas extend SDK schemas with excluded internal fields:

from adcp.types.v31 import ListCreativesRequest
from pydantic import Field

class SellerListCreativesRequest(ListCreativesRequest):
    internal_tenant_id: str = Field(exclude=True)

On 8.0.0b4 (and with #1048's unchanged RootModel runtime), class definition
fails with:

NameError: Unexpected field with name 'internal_tenant_id'; only 'root' is
allowed as a field of a RootModel

Static stubs make direct construction and attribute access pleasant for type
checkers, but they do not provide runtime Pydantic fields or nested Pydantic
models. Composition beside the boundary object does not replace an extension
model at transport/business-logic boundaries where one validated model must
carry both protocol and excluded internal state.

The practical result for a 3.1 seller is that it must still inherit the
unversioned 3.2-generated classes and patch the version delta locally. Our SDK
8 migration currently has to:

  • remove assignment_projection and assignment_limit during 3.1
    ListCreativesRequest serialization; and
  • add a validator restoring the 3.1-required PackageRequest.budget.

That is exactly the semantic drift the version-scoped surface should let an
adopter avoid.

Requested extension surface

Please provide a supported way to define a normal Pydantic model that extends
one pinned protocol version. Full isolated per-version runtime codegen is one
valid implementation, but the API shape is open.

Acceptance criteria:

  1. An adopter can add an exclude=True internal field to a 3.1 request or
    response without converting through an untyped root dictionary.
  2. Protocol fields appear in runtime model_fields, not only in .pyi.
  3. Nested protocol objects retain typed Pydantic runtime values where the
    normal generated surface provides them.
  4. The extended model emits the pinned version's JSON Schema and wire shape;
    excluded adopter fields never serialize.
  5. The 3.1 extension does not admit 3.2-only creative assignment fields and
    keeps package budget required.
  6. The solution has a stable public import/factory path and does not require
    generated_poc imports.

Relationship to #1046 and #1048

This is the explicitly deferred runtime-extension portion of #1046. The stubs
in #1048 are useful and should remain; this asks for the complementary runtime
Pydantic extension seam needed by schema-extending adopters.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions