Skip to content

Commit 2051669

Browse files
bokelleyclaude
andcommitted
feat: add AdCPBaseModel with exclude_none serialization (#40)
* feat: add AdCPBaseModel with exclude_none serialization AdCP JSON schemas use `additionalProperties: false` and don't allow null for optional fields. Optional fields must be omitted entirely when not present. Changes: - Create AdCPBaseModel base class with exclude_none=True by default - Update code generator to use AdCPBaseModel instead of BaseModel - Regenerate all models with new base class - Export AdCPBaseModel from adcp.types This ensures spec-compliant JSON serialization where None values are omitted from the output instead of being sent as null. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: add type annotations to AdCPBaseModel methods Add proper type annotations to model_dump() and model_dump_json() to satisfy mypy strict type checking. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 371e30c commit 2051669

File tree

4 files changed

+36
-2
lines changed

4 files changed

+36
-2
lines changed

scripts/generate_models_simple.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ def add_custom_implementations(code: str) -> str:
501501
# The simple code generator produces type aliases (e.g., PreviewCreativeRequest = Any)
502502
# for complex schemas that use oneOf. We override them here with proper Pydantic classes
503503
# to maintain type safety and enable batch API support.
504+
# Note: All classes inherit from BaseModel (which is aliased to AdCPBaseModel for exclude_none).
504505
505506
506507
class FormatId(BaseModel):
@@ -723,7 +724,9 @@ def main():
723724
"import re",
724725
"from typing import Any, Literal",
725726
"",
726-
"from pydantic import BaseModel, ConfigDict, Field, field_validator",
727+
"from pydantic import ConfigDict, Field, field_validator",
728+
"",
729+
"from adcp.types.base import AdCPBaseModel as BaseModel",
727730
"",
728731
"",
729732
"",

src/adcp/types/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
"""Type definitions for AdCP client."""
44

5+
from adcp.types.base import AdCPBaseModel
56
from adcp.types.core import (
67
Activity,
78
ActivityType,
@@ -14,6 +15,7 @@
1415
)
1516

1617
__all__ = [
18+
"AdCPBaseModel",
1719
"AgentConfig",
1820
"Protocol",
1921
"TaskResult",

src/adcp/types/base.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from __future__ import annotations
2+
3+
"""Base model for AdCP types with spec-compliant serialization."""
4+
5+
from typing import Any
6+
7+
from pydantic import BaseModel
8+
9+
10+
class AdCPBaseModel(BaseModel):
11+
"""Base model for AdCP types with spec-compliant serialization.
12+
13+
AdCP JSON schemas use additionalProperties: false and do not allow null
14+
for optional fields. Therefore, optional fields must be omitted entirely
15+
when not present (not sent as null).
16+
"""
17+
18+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
19+
if "exclude_none" not in kwargs:
20+
kwargs["exclude_none"] = True
21+
return super().model_dump(**kwargs)
22+
23+
def model_dump_json(self, **kwargs: Any) -> str:
24+
if "exclude_none" not in kwargs:
25+
kwargs["exclude_none"] = True
26+
return super().model_dump_json(**kwargs)

src/adcp/types/generated.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
import re
1515
from typing import Any, Literal
1616

17-
from pydantic import BaseModel, ConfigDict, Field, field_validator
17+
from pydantic import ConfigDict, Field, field_validator
18+
19+
from adcp.types.base import AdCPBaseModel as BaseModel
1820

1921

2022

@@ -945,6 +947,7 @@ class TasksListResponse(BaseModel):
945947
# The simple code generator produces type aliases (e.g., PreviewCreativeRequest = Any)
946948
# for complex schemas that use oneOf. We override them here with proper Pydantic classes
947949
# to maintain type safety and enable batch API support.
950+
# Note: All classes inherit from BaseModel (which is aliased to AdCPBaseModel for exclude_none).
948951

949952

950953
class FormatId(BaseModel):

0 commit comments

Comments
 (0)