Skip to content

Commit d776bc6

Browse files
bokelleyclaude
andauthored
feat: Add semantic type aliases for discriminated unions (#49)
* feat: Add semantic type aliases for discriminated unions Add clear, descriptive type aliases for discriminated union types that were previously only available with auto-generated numbered names (e.g., PreviewRender1, SubAsset1). New semantic aliases: - Preview Renders (output_format discriminator): * UrlPreviewRender = PreviewRender1 (output_format='url') * HtmlPreviewRender = PreviewRender2 (output_format='html') * BothPreviewRender = PreviewRender3 (output_format='both') - VAST Assets (delivery_type discriminator): * UrlVastAsset = VastAsset1 (delivery_type='url') * InlineVastAsset = VastAsset2 (delivery_type='inline') - DAAST Assets (delivery_type discriminator): * UrlDaastAsset = DaastAsset1 (delivery_type='url') * InlineDaastAsset = DaastAsset2 (delivery_type='inline') - SubAssets (asset_kind discriminator): * MediaSubAsset = SubAsset1 (asset_kind='media') * TextSubAsset = SubAsset2 (asset_kind='text') All aliases are: - Exported from adcp.types.aliases - Exported from adcp.types.generated - Re-exported from main adcp package for convenience - Fully tested with comprehensive test coverage This improves code readability by conveying semantic meaning rather than relying on generated numbering. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: Document semantic type alias patterns and generated file policy Add comprehensive documentation for: - How to add semantic type aliases for discriminated unions - Process for creating, exporting, and testing aliases - Current list of all semantic aliases in the codebase - Guidelines for when to create aliases vs when not to - Clarify that generated.py must never be manually modified This ensures future developers understand the alias system and don't accidentally modify auto-generated code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Remove semantic aliases from generated.py (will be overwritten) The semantic type aliases were incorrectly added to generated.py, which is auto-generated and will be overwritten on next type generation. Moved all semantic alias imports to use aliases.py directly: - src/adcp/types/__init__.py now imports from aliases instead of generated - Removed all alias definitions and exports from generated.py - All functionality preserved through aliases.py This ensures aliases survive type regeneration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Remove misleading PreviewRender aliases Removed the old, misleading aliases for PreviewRender types: - PreviewRenderImage (misleading - actually output_format='url') - PreviewRenderHtml (ambiguous - doesn't clarify it's output_format) - PreviewRenderIframe (misleading - actually output_format='both') Kept only the accurate, discriminator-based aliases: - UrlPreviewRender (output_format='url') - HtmlPreviewRender (output_format='html') - BothPreviewRender (output_format='both') These names accurately reflect the discriminator field values and prevent confusion about what the types represent. Since this is brand new code with no existing users, removing the misleading names now prevents technical debt. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add comprehensive discriminator tests for semantic type aliases Verifies that semantic aliases match their discriminator field values: - PreviewRender: output_format ('url', 'html', 'both') - VastAsset: delivery_type ('url', 'inline') - DaastAsset: delivery_type ('url', 'inline') - SubAsset: asset_kind ('media', 'text') Tests validate: - Correct discriminator values are present - Correct fields exist for each variant - Wrong discriminator values are rejected - Serialization/deserialization roundtrips work correctly 21 new tests, all passing. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5053055 commit d776bc6

File tree

6 files changed

+561
-27
lines changed

6 files changed

+561
-27
lines changed

CLAUDE.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ PackageRequest = dict[str, Any]
1919

2020
**NEVER Modify Generated Files Directly**
2121

22-
Files in `src/adcp/types/generated_poc/` are auto-generated by `scripts/generate_types.py`. Any manual edits will be lost on regeneration.
22+
Files in `src/adcp/types/generated_poc/` and `src/adcp/types/generated.py` are auto-generated by `scripts/generate_types.py`. Any manual edits will be lost on regeneration.
23+
24+
**CRITICAL**: Do not add code to `generated.py` or any files in `generated_poc/` directory. These are regenerated from schemas.
2325

2426
**Post-Generation Fix System:**
2527

@@ -64,6 +66,100 @@ Edit `scripts/post_generate_fixes.py` and add a new function. The script:
6466
- Validates fixes were successfully applied
6567
- Fails loudly if schema changes break the fix patterns
6668

69+
## Semantic Type Aliases for Discriminated Unions
70+
71+
**Problem**: The code generator (`datamodel-code-generator`) creates numbered type names for discriminated union variants (e.g., `PreviewRender1`, `PreviewRender2`, `SubAsset1`, `SubAsset2`). While functionally correct, these don't convey semantic meaning.
72+
73+
**Solution**: Add semantic type aliases in `src/adcp/types/aliases.py` that provide clear, descriptive names based on the discriminator field value.
74+
75+
**Process for Adding New Semantic Aliases:**
76+
77+
1. **Identify the discriminated union types** - Look for numbered types (e.g., `TypeName1`, `TypeName2`) in generated files
78+
2. **Determine the discriminator field** - Check the schema/generated code for the `Literal` field that distinguishes variants
79+
3. **Create semantic aliases in `aliases.py`**:
80+
```python
81+
# Import the generated types
82+
from adcp.types.generated import PreviewRender1, PreviewRender2, PreviewRender3
83+
84+
# Create semantic aliases based on discriminator values
85+
UrlPreviewRender = PreviewRender1 # output_format='url'
86+
HtmlPreviewRender = PreviewRender2 # output_format='html'
87+
BothPreviewRender = PreviewRender3 # output_format='both'
88+
```
89+
90+
4. **Add to exports** in `aliases.py`:
91+
```python
92+
__all__ = [
93+
...,
94+
"UrlPreviewRender",
95+
"HtmlPreviewRender",
96+
"BothPreviewRender",
97+
]
98+
```
99+
100+
5. **Re-export from main package** in `src/adcp/__init__.py`:
101+
```python
102+
from adcp.types.aliases import (
103+
...,
104+
UrlPreviewRender,
105+
HtmlPreviewRender,
106+
BothPreviewRender,
107+
)
108+
109+
__all__ = [
110+
...,
111+
"UrlPreviewRender",
112+
"HtmlPreviewRender",
113+
"BothPreviewRender",
114+
]
115+
```
116+
117+
6. **Add comprehensive tests** in `tests/test_type_aliases.py`:
118+
- Test that aliases can be imported
119+
- Test that aliases point to correct generated types
120+
- Test that aliases are exported from main package
121+
122+
**Current Semantic Aliases:**
123+
124+
- **Preview Renders** (discriminated by `output_format`):
125+
- `UrlPreviewRender` = `PreviewRender1` (output_format='url')
126+
- `HtmlPreviewRender` = `PreviewRender2` (output_format='html')
127+
- `BothPreviewRender` = `PreviewRender3` (output_format='both')
128+
129+
- **VAST Assets** (discriminated by `delivery_type`):
130+
- `UrlVastAsset` = `VastAsset1` (delivery_type='url')
131+
- `InlineVastAsset` = `VastAsset2` (delivery_type='inline')
132+
133+
- **DAAST Assets** (discriminated by `delivery_type`):
134+
- `UrlDaastAsset` = `DaastAsset1` (delivery_type='url')
135+
- `InlineDaastAsset` = `DaastAsset2` (delivery_type='inline')
136+
137+
- **SubAssets** (discriminated by `asset_kind`):
138+
- `MediaSubAsset` = `SubAsset1` (asset_kind='media')
139+
- `TextSubAsset` = `SubAsset2` (asset_kind='text')
140+
141+
- **Response Types** (discriminated by success/error):
142+
- Success/Error variants for: ActivateSignal, BuildCreative, CreateMediaBuy, ProvidePerformanceFeedback, SyncCreatives, UpdateMediaBuy
143+
144+
- **Request Types** (discriminated by operation variant):
145+
- `PreviewCreativeFormatRequest`/`PreviewCreativeManifestRequest`
146+
- `UpdateMediaBuyPackagesRequest`/`UpdateMediaBuyPropertiesRequest`
147+
148+
- **Activation Keys** (discriminated by identifier type):
149+
- `PropertyIdActivationKey`/`PropertyTagActivationKey`
150+
151+
**Guidelines for Choosing What to Alias:**
152+
153+
**DO create aliases for:**
154+
- User-facing discriminated unions used in API calls
155+
- Types where the discriminator conveys important semantic meaning
156+
- Types where numbered suffixes cause confusion
157+
158+
**DON'T create aliases for:**
159+
- Internal helper types not commonly used directly
160+
- Types where parent context makes the meaning clear
161+
- Generic helper types (Input1, Parameters2, etc.)
162+
67163
**Type Checking Best Practices**
68164
- Use `TYPE_CHECKING` for optional dependencies to avoid runtime import errors
69165
- Use `cast()` for JSON deserialization to satisfy mypy's `no-any-return` checks

src/adcp/__init__.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,27 +56,33 @@
5656
from adcp.types.aliases import (
5757
ActivateSignalErrorResponse,
5858
ActivateSignalSuccessResponse,
59+
BothPreviewRender,
5960
BuildCreativeErrorResponse,
6061
BuildCreativeSuccessResponse,
6162
CreateMediaBuyErrorResponse,
6263
CreateMediaBuySuccessResponse,
64+
HtmlPreviewRender,
65+
InlineDaastAsset,
66+
InlineVastAsset,
67+
MediaSubAsset,
6368
PreviewCreativeFormatRequest,
6469
PreviewCreativeInteractiveResponse,
6570
PreviewCreativeManifestRequest,
6671
PreviewCreativeStaticResponse,
67-
PreviewRenderHtml,
68-
PreviewRenderIframe,
69-
PreviewRenderImage,
7072
PropertyIdActivationKey,
7173
PropertyTagActivationKey,
7274
ProvidePerformanceFeedbackErrorResponse,
7375
ProvidePerformanceFeedbackSuccessResponse,
7476
SyncCreativesErrorResponse,
7577
SyncCreativesSuccessResponse,
78+
TextSubAsset,
7679
UpdateMediaBuyErrorResponse,
7780
UpdateMediaBuyPackagesRequest,
7881
UpdateMediaBuyPropertiesRequest,
7982
UpdateMediaBuySuccessResponse,
83+
UrlDaastAsset,
84+
UrlPreviewRender,
85+
UrlVastAsset,
8086
)
8187
from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
8288

@@ -219,25 +225,31 @@
219225
# Semantic type aliases (for better API ergonomics)
220226
"ActivateSignalSuccessResponse",
221227
"ActivateSignalErrorResponse",
228+
"BothPreviewRender",
222229
"BuildCreativeSuccessResponse",
223230
"BuildCreativeErrorResponse",
224231
"CreateMediaBuySuccessResponse",
225232
"CreateMediaBuyErrorResponse",
226-
"ProvidePerformanceFeedbackSuccessResponse",
227-
"ProvidePerformanceFeedbackErrorResponse",
228-
"SyncCreativesSuccessResponse",
229-
"SyncCreativesErrorResponse",
230-
"UpdateMediaBuySuccessResponse",
231-
"UpdateMediaBuyErrorResponse",
233+
"HtmlPreviewRender",
234+
"InlineDaastAsset",
235+
"InlineVastAsset",
236+
"MediaSubAsset",
232237
"PreviewCreativeFormatRequest",
233238
"PreviewCreativeManifestRequest",
234239
"PreviewCreativeStaticResponse",
235240
"PreviewCreativeInteractiveResponse",
236-
"PreviewRenderImage",
237-
"PreviewRenderHtml",
238-
"PreviewRenderIframe",
239241
"PropertyIdActivationKey",
240242
"PropertyTagActivationKey",
243+
"ProvidePerformanceFeedbackSuccessResponse",
244+
"ProvidePerformanceFeedbackErrorResponse",
245+
"SyncCreativesSuccessResponse",
246+
"SyncCreativesErrorResponse",
247+
"TextSubAsset",
248+
"UpdateMediaBuySuccessResponse",
249+
"UpdateMediaBuyErrorResponse",
241250
"UpdateMediaBuyPackagesRequest",
242251
"UpdateMediaBuyPropertiesRequest",
252+
"UrlDaastAsset",
253+
"UrlPreviewRender",
254+
"UrlVastAsset",
243255
]

src/adcp/types/__init__.py

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

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

5+
from adcp.types.aliases import (
6+
BothPreviewRender,
7+
HtmlPreviewRender,
8+
InlineDaastAsset,
9+
InlineVastAsset,
10+
MediaSubAsset,
11+
TextSubAsset,
12+
UrlDaastAsset,
13+
UrlPreviewRender,
14+
UrlVastAsset,
15+
)
516
from adcp.types.base import AdCPBaseModel
617
from adcp.types.core import (
718
Activity,
@@ -24,4 +35,14 @@
2435
"Activity",
2536
"ActivityType",
2637
"DebugInfo",
38+
# Semantic aliases for discriminated unions
39+
"BothPreviewRender",
40+
"HtmlPreviewRender",
41+
"InlineDaastAsset",
42+
"InlineVastAsset",
43+
"MediaSubAsset",
44+
"TextSubAsset",
45+
"UrlDaastAsset",
46+
"UrlPreviewRender",
47+
"UrlVastAsset",
2748
]

src/adcp/types/aliases.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
# Create media buy responses
4646
CreateMediaBuyResponse1,
4747
CreateMediaBuyResponse2,
48+
# DAAST assets
49+
DaastAsset1,
50+
DaastAsset2,
4851
# Preview creative requests
4952
PreviewCreativeRequest1,
5053
PreviewCreativeRequest2,
@@ -58,6 +61,9 @@
5861
# Performance feedback responses
5962
ProvidePerformanceFeedbackResponse1,
6063
ProvidePerformanceFeedbackResponse2,
64+
# SubAssets
65+
SubAsset1,
66+
SubAsset2,
6167
# Sync creatives responses
6268
SyncCreativesResponse1,
6369
SyncCreativesResponse2,
@@ -67,6 +73,9 @@
6773
# Update media buy responses
6874
UpdateMediaBuyResponse1,
6975
UpdateMediaBuyResponse2,
76+
# VAST assets
77+
VastAsset1,
78+
VastAsset2,
7079
)
7180

7281
# ============================================================================
@@ -157,15 +166,40 @@
157166
PreviewCreativeInteractiveResponse = PreviewCreativeResponse2
158167
"""Preview response with interactive renders (iframe embedding)."""
159168

160-
# Preview Render Variants
161-
PreviewRenderImage = PreviewRender1
162-
"""Image-based preview render (PNG/JPEG)."""
169+
# Preview Render Variants (discriminated by output_format)
170+
UrlPreviewRender = PreviewRender1
171+
"""Preview render with output_format='url' - provides preview_url for iframe embedding."""
163172

164-
PreviewRenderHtml = PreviewRender2
165-
"""HTML-based preview render (static markup)."""
173+
HtmlPreviewRender = PreviewRender2
174+
"""Preview render with output_format='html' - provides preview_html for direct embedding."""
166175

167-
PreviewRenderIframe = PreviewRender3
168-
"""Interactive iframe-based preview render."""
176+
BothPreviewRender = PreviewRender3
177+
"""Preview render with output_format='both' - provides both preview_url and preview_html."""
178+
179+
# ============================================================================
180+
# ASSET TYPE ALIASES - Delivery & Kind Discriminated Unions
181+
# ============================================================================
182+
183+
# VAST Asset Variants (discriminated by delivery_type)
184+
UrlVastAsset = VastAsset1
185+
"""VAST asset delivered via URL endpoint - delivery_type='url'."""
186+
187+
InlineVastAsset = VastAsset2
188+
"""VAST asset with inline XML content - delivery_type='inline'."""
189+
190+
# DAAST Asset Variants (discriminated by delivery_type)
191+
UrlDaastAsset = DaastAsset1
192+
"""DAAST asset delivered via URL endpoint - delivery_type='url'."""
193+
194+
InlineDaastAsset = DaastAsset2
195+
"""DAAST asset with inline XML content - delivery_type='inline'."""
196+
197+
# SubAsset Variants (discriminated by asset_kind)
198+
MediaSubAsset = SubAsset1
199+
"""SubAsset for media content (images, videos) - asset_kind='media', provides content_uri."""
200+
201+
TextSubAsset = SubAsset2
202+
"""SubAsset for text content (headlines, body text) - asset_kind='text', provides content."""
169203

170204
# ============================================================================
171205
# EXPORTS
@@ -178,6 +212,16 @@
178212
# Activation keys
179213
"PropertyIdActivationKey",
180214
"PropertyTagActivationKey",
215+
# Asset type aliases
216+
"BothPreviewRender",
217+
"HtmlPreviewRender",
218+
"InlineDaastAsset",
219+
"InlineVastAsset",
220+
"MediaSubAsset",
221+
"TextSubAsset",
222+
"UrlDaastAsset",
223+
"UrlPreviewRender",
224+
"UrlVastAsset",
181225
# Build creative responses
182226
"BuildCreativeSuccessResponse",
183227
"BuildCreativeErrorResponse",
@@ -193,10 +237,6 @@
193237
# Preview creative responses
194238
"PreviewCreativeStaticResponse",
195239
"PreviewCreativeInteractiveResponse",
196-
# Preview renders
197-
"PreviewRenderImage",
198-
"PreviewRenderHtml",
199-
"PreviewRenderIframe",
200240
# Sync creatives responses
201241
"SyncCreativesSuccessResponse",
202242
"SyncCreativesErrorResponse",

0 commit comments

Comments
 (0)