Skip to content

feat(types)!: default serialize_as_any=True in AdCPBaseModel.model_dump#639

Merged
bokelley merged 1 commit intomainfrom
claude/issue-615-serialize-as-any-default
May 10, 2026
Merged

feat(types)!: default serialize_as_any=True in AdCPBaseModel.model_dump#639
bokelley merged 1 commit intomainfrom
claude/issue-615-serialize-as-any-default

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

  • Defaults serialize_as_any=True in AdCPBaseModel.model_dump() and model_dump_json(), symmetric with the existing exclude_none=True default.
  • Subclass @model_serializer overrides now fire automatically through base-typed parent fields. Field(exclude=True) remains the wire-isolation contract and continues to suppress internal fields at every nesting depth.
  • Removes the per-response-type parent model_dump boilerplate that adopters previously needed to write (the salesagent SDK alone has ~14 such overrides on top of 58 Field(exclude=True) markers).

Closes #615.

Why this and not the docs-only fix in #630

#630 documented the workaround: "you must remember serialize_as_any=True at every call site." That's the strongest argument for making it a default — if the kwarg is worth documenting as a footgun, it's worth flipping. The change is a one-line addition that's perfectly symmetric with exclude_none=True and eliminates the need for the workaround.

Behavior change

Subclasses that add fields without Field(exclude=True) previously had those fields silently dropped by Pydantic's declared-schema firewall when nested under a base-typed parent. Under this PR they appear in model_dump() output. The migration is mechanical: audit each subclass, mark internal fields with Field(exclude=True). Tagged feat! so release-please bumps the major version and the CHANGELOG flags the change clearly. Adopters who want the prior behavior at a specific call site can pass serialize_as_any=False explicitly.

docs/extending-types.md is updated to reflect the new default and call out the audit step for adopters with non-excluded subclass fields.

Test plan

  • tests/test_serialize_as_any_default.py — 8 new tests pinning: subclass @model_serializer fires on singular and list fields, model_dump_json carries the same default, Field(exclude=True) continues to suppress internal fields, the new wire-leak path is observable (so adopters who relied on the firewall surface a failing test), explicit serialize_as_any=False opt-out works, and the two defaults (exclude_none, serialize_as_any) are independent.
  • Full suite green locally — 3910 passed, no existing test relied on the firewall behavior.
  • mypy clean on the touched files; mypy --strict tests/type_checks/ (the suite added in feat(testing): adopter type-checking test suite with zero-ignore contract #634) still clean.
  • Pre-commit hooks pass.

🤖 Generated with Claude Code

Symmetric with the existing exclude_none=True default. Removes the
parent-side model_dump boilerplate that adopters previously needed to
write per response type to make subclass @model_serializer overrides
fire through base-typed parent fields. Field(exclude=True) remains the
wire-isolation contract and continues to suppress internal fields at
every nesting depth.

BREAKING CHANGE: Subclasses that add fields without Field(exclude=True)
will now have those fields appear in model_dump() output where they
were previously dropped by Pydantic's declared-schema firewall. Audit
each subclass and mark internal fields with Field(exclude=True). To
restore the prior behavior at a specific call site, pass
serialize_as_any=False explicitly.

Closes #615

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 3160ace into main May 10, 2026
16 checks passed
@bokelley bokelley deleted the claude/issue-615-serialize-as-any-default branch May 10, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(types): nested model_dump() resolution in response models

1 participant