Skip to content

Commit b4028b7

Browse files
bokelleyclaude
andauthored
fix: resolve Package type name collision with semantic aliases (#62)
* fix: resolve Package type name collision with semantic aliases The AdCP schemas define two different types both named "Package": - Full Package (package.json): Complete operational package with 12 fields - Created Package (create-media-buy-response.json): Minimal reference with 2 fields The code generator's "first wins" collision handling exported the response type, shadowing the domain model. This fix adds semantic aliases: - Package: The canonical full domain model (for MediaBuy, updates, etc.) - CreatedPackageReference: Minimal response type (for CreateMediaBuy responses) Imports Package directly from package.py module to bypass consolidation collision. Includes 7 comprehensive tests validating field structure and usage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Resolve linting errors (import sorting and line length) Fixed ruff linting issues that were causing CI failures: - Auto-fixed import block sorting in multiple test files - Split long assert messages across multiple lines - Shortened docstrings to fit 100-character limit All 282 tests still passing locally. 🤖 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 6f7fdf1 commit b4028b7

File tree

11 files changed

+220
-37
lines changed

11 files changed

+220
-37
lines changed

src/adcp/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
BothPreviewRender,
105105
BuildCreativeErrorResponse,
106106
BuildCreativeSuccessResponse,
107+
CreatedPackageReference,
107108
CreateMediaBuyErrorResponse,
108109
CreateMediaBuySuccessResponse,
109110
HtmlPreviewRender,
@@ -213,6 +214,8 @@
213214
"CreativeManifest",
214215
"MediaBuy",
215216
"Package",
217+
# Package type aliases
218+
"CreatedPackageReference",
216219
# Status enums (for control flow)
217220
"CreativeStatus",
218221
"MediaBuyStatus",

src/adcp/types/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from adcp.types.aliases import (
1414
BothPreviewRender,
15+
CreatedPackageReference,
1516
HtmlPreviewRender,
1617
InlineDaastAsset,
1718
InlineVastAsset,
@@ -86,6 +87,8 @@
8687
"UrlDaastAsset",
8788
"UrlPreviewRender",
8889
"UrlVastAsset",
90+
# Package type aliases
91+
"CreatedPackageReference",
8992
# Stable API types (commonly used)
9093
"BrandManifest",
9194
"Creative",

src/adcp/types/_generated.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
DO NOT EDIT MANUALLY.
1111
1212
Generated from: https://github.com/adcontextprotocol/adcp/tree/main/schemas
13-
Generation date: 2025-11-18 12:11:55 UTC
13+
Generation date: 2025-11-18 12:52:17 UTC
1414
"""
1515
# ruff: noqa: E501, I001
1616
from __future__ import annotations

src/adcp/types/aliases.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@
7878
VastAsset2,
7979
)
8080

81+
# Import Package types directly from their modules to avoid collision issues
82+
from adcp.types.generated_poc.create_media_buy_response import (
83+
Package as CreatedPackageInternal,
84+
)
85+
from adcp.types.generated_poc.package import Package as FullPackageInternal
86+
8187
# ============================================================================
8288
# RESPONSE TYPE ALIASES - Success/Error Discriminated Unions
8389
# ============================================================================
@@ -201,6 +207,51 @@
201207
TextSubAsset = SubAsset2
202208
"""SubAsset for text content (headlines, body text) - asset_kind='text', provides content."""
203209

210+
# ============================================================================
211+
# PACKAGE TYPE ALIASES - Resolving Type Name Collisions
212+
# ============================================================================
213+
# The AdCP schemas define two genuinely different types both named "Package":
214+
#
215+
# 1. Full Package (from package.json schema):
216+
# - Complete operational package with all fields (budget, pricing_option_id, etc.)
217+
# - Used in MediaBuy, update operations, and package management
218+
# - Has 12+ fields for full package configuration
219+
#
220+
# 2. Created Package (from create-media-buy-response.json schema):
221+
# - Minimal response type with only IDs (buyer_ref, package_id)
222+
# - Used in CreateMediaBuy success responses
223+
# - Only 2 fields - represents newly created package references
224+
#
225+
# The code generator's "first wins" collision handling exports the Created Package
226+
# as "Package", shadowing the Full Package. These semantic aliases provide clear,
227+
# unambiguous names for both types.
228+
229+
Package = FullPackageInternal
230+
"""Complete package configuration with all operational fields.
231+
232+
This is the canonical Package type used throughout AdCP for package management.
233+
234+
Used in:
235+
- MediaBuy.packages (list of full package details)
236+
- Update operations (modifying existing packages)
237+
- Package management (creating/configuring packages)
238+
239+
Fields include: budget, pricing_option_id, product_id, status, bid_price,
240+
creative_assignments, format_ids_to_provide, impressions, pacing, targeting_overlay
241+
"""
242+
243+
CreatedPackageReference = CreatedPackageInternal
244+
"""Minimal package reference with only IDs returned after creation.
245+
246+
This is NOT the full Package type - it's a lightweight reference returned
247+
in CreateMediaBuySuccessResponse to indicate which packages were created.
248+
249+
Used in:
250+
- CreateMediaBuySuccessResponse.packages (list of created package references)
251+
252+
Fields: buyer_ref, package_id only
253+
"""
254+
204255
# ============================================================================
205256
# EXPORTS
206257
# ============================================================================
@@ -246,4 +297,7 @@
246297
# Update media buy responses
247298
"UpdateMediaBuySuccessResponse",
248299
"UpdateMediaBuyErrorResponse",
300+
# Package type aliases
301+
"CreatedPackageReference",
302+
"Package",
249303
]

src/adcp/types/stable.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
from __future__ import annotations
2323

24-
# Import all generated types from internal consolidated module
2524
from adcp.types._generated import (
2625
# Core request/response types
2726
ActivateSignalRequest,
@@ -67,7 +66,6 @@
6766
MarkdownAsset,
6867
MediaBuy,
6968
MediaBuyStatus,
70-
Package,
7169
PackageStatus,
7270
PreviewCreativeRequest,
7371
PreviewCreativeResponse,
@@ -94,6 +92,10 @@
9492
WebhookAsset,
9593
)
9694

95+
# Import all generated types from internal consolidated module
96+
# Import Package directly from its module to avoid collision with Response Package
97+
from adcp.types.generated_poc.package import Package
98+
9799
# Note: BrandManifest is currently split into BrandManifest1/2 due to upstream schema
98100
# using anyOf incorrectly. This will be fixed upstream to create a single BrandManifest type.
99101
# For now, users should use BrandManifest1 (url required) which is most common.

tests/test_client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ async def test_get_products():
7777
"""Test get_products method with mock adapter."""
7878
from unittest.mock import patch
7979

80-
from adcp.types.core import TaskResult, TaskStatus
8180
from adcp.types._generated import GetProductsRequest, GetProductsResponse
81+
from adcp.types.core import TaskResult, TaskStatus
8282

8383
config = AgentConfig(
8484
id="test_agent",
@@ -229,8 +229,8 @@ async def test_multi_agent_parallel_execution():
229229
"""Test parallel execution across multiple agents."""
230230
from unittest.mock import patch
231231

232-
from adcp.types.core import TaskResult, TaskStatus
233232
from adcp.types._generated import GetProductsRequest
233+
from adcp.types.core import TaskResult, TaskStatus
234234

235235
agents = [
236236
AgentConfig(
@@ -280,8 +280,8 @@ async def test_list_creative_formats_parses_mcp_response():
280280
import json
281281
from unittest.mock import patch
282282

283-
from adcp.types.core import TaskResult, TaskStatus
284283
from adcp.types._generated import ListCreativeFormatsRequest, ListCreativeFormatsResponse
284+
from adcp.types.core import TaskResult, TaskStatus
285285

286286
config = AgentConfig(
287287
id="creative_agent",
@@ -330,8 +330,8 @@ async def test_list_creative_formats_parses_a2a_response():
330330
"""Test that list_creative_formats parses A2A dict response into structured response."""
331331
from unittest.mock import patch
332332

333-
from adcp.types.core import TaskResult, TaskStatus
334333
from adcp.types._generated import ListCreativeFormatsRequest, ListCreativeFormatsResponse
334+
from adcp.types.core import TaskResult, TaskStatus
335335

336336
config = AgentConfig(
337337
id="creative_agent",
@@ -374,8 +374,8 @@ async def test_list_creative_formats_handles_invalid_response():
374374
"""Test that list_creative_formats handles invalid response gracefully."""
375375
from unittest.mock import patch
376376

377-
from adcp.types.core import TaskResult, TaskStatus
378377
from adcp.types._generated import ListCreativeFormatsRequest
378+
from adcp.types.core import TaskResult, TaskStatus
379379

380380
config = AgentConfig(
381381
id="creative_agent",

tests/test_discriminated_unions.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
InlineDaastAsset,
1717
InlineVastAsset,
1818
MediaSubAsset,
19-
Product,
2019
TextSubAsset,
2120
UrlDaastAsset,
2221
UrlPreviewRender,
@@ -34,7 +33,6 @@
3433
Deployment2, # Agent
3534
Destination1, # Platform
3635
Destination2, # Agent
37-
PublisherProperties, # selection_type='all'
3836
PublisherProperties4, # selection_type='by_id'
3937
PublisherProperties5, # selection_type='by_tag'
4038
)
@@ -86,7 +84,7 @@ def test_property_ids_authorization_wrong_type_fails(self):
8684
assert "authorization_type" in error_msg.lower()
8785

8886
def test_property_tags_authorization(self):
89-
"""AuthorizedAgents1 (property_tags variant) requires property_tags and authorization_type."""
87+
"""AuthorizedAgents1 requires property_tags and authorization_type."""
9088
agent = AuthorizedAgents1(
9189
url="https://agent.example.com",
9290
authorized_for="All properties",
@@ -98,7 +96,7 @@ def test_property_tags_authorization(self):
9896
assert not hasattr(agent, "property_ids")
9997

10098
def test_inline_properties_authorization(self):
101-
"""AuthorizedAgents2 (inline_properties variant) requires properties and authorization_type."""
99+
"""AuthorizedAgents2 requires properties and authorization_type."""
102100
agent = AuthorizedAgents2(
103101
url="https://agent.example.com",
104102
authorized_for="All properties",
@@ -136,7 +134,7 @@ def test_inline_properties_authorization_from_json(self):
136134
assert len(agent.properties) == 1
137135

138136
def test_publisher_properties_authorization(self):
139-
"""AuthorizedAgents3 (publisher_properties variant) requires publisher_properties and authorization_type."""
137+
"""AuthorizedAgents3 requires publisher_properties and type."""
140138
agent = AuthorizedAgents3(
141139
url="https://agent.example.com",
142140
authorized_for="All properties",

tests/test_preview_html.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from adcp import ADCPClient
88
from adcp.types import AgentConfig, Protocol
9-
from adcp.types.core import TaskResult, TaskStatus
109
from adcp.types._generated import (
1110
CreativeManifest,
1211
Format,
@@ -19,6 +18,7 @@
1918
PreviewCreativeResponse1,
2019
Product,
2120
)
21+
from adcp.types.core import TaskResult, TaskStatus
2222
from adcp.utils.preview_cache import (
2323
PreviewURLGenerator,
2424
_create_sample_asset,

tests/test_public_api.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,20 @@ def test_client_types_are_exported():
109109

110110
def test_public_api_types_are_pydantic_models():
111111
"""Core types from public API are valid Pydantic models."""
112-
from adcp import Product, Format, MediaBuy, Property, BrandManifest
112+
from adcp import BrandManifest, Format, MediaBuy, Product, Property
113113

114114
types_to_test = [Product, Format, MediaBuy, Property, BrandManifest]
115115

116116
for model_class in types_to_test:
117117
# Should have Pydantic model methods
118-
assert hasattr(model_class, "model_validate"), f"{model_class.__name__} missing model_validate"
119-
assert hasattr(model_class, "model_dump"), f"{model_class.__name__} missing model_dump"
120-
assert hasattr(model_class, "model_validate_json"), f"{model_class.__name__} missing model_validate_json"
121-
assert hasattr(model_class, "model_dump_json"), f"{model_class.__name__} missing model_dump_json"
122-
assert hasattr(model_class, "model_fields"), f"{model_class.__name__} missing model_fields"
118+
name = model_class.__name__
119+
assert hasattr(model_class, "model_validate"), f"{name} missing model_validate"
120+
assert hasattr(model_class, "model_dump"), f"{name} missing model_dump"
121+
assert hasattr(model_class, "model_validate_json"), (
122+
f"{name} missing model_validate_json"
123+
)
124+
assert hasattr(model_class, "model_dump_json"), f"{name} missing model_dump_json"
125+
assert hasattr(model_class, "model_fields"), f"{name} missing model_fields"
123126

124127

125128
def test_product_has_expected_public_fields():
@@ -158,26 +161,32 @@ def test_format_has_expected_public_fields():
158161

159162
def test_pricing_options_are_discriminated_by_is_fixed():
160163
"""Pricing option types have is_fixed discriminator field."""
161-
from adcp import CpmFixedRatePricingOption, CpmAuctionPricingOption, CpcPricingOption
164+
from adcp import CpcPricingOption, CpmAuctionPricingOption, CpmFixedRatePricingOption
162165

163166
# Fixed-rate options should have is_fixed discriminator
164167
fixed_types = [CpmFixedRatePricingOption, CpcPricingOption]
165168
for pricing_type in fixed_types:
166-
assert "is_fixed" in pricing_type.model_fields, f"{pricing_type.__name__} missing is_fixed discriminator"
169+
name = pricing_type.__name__
170+
assert "is_fixed" in pricing_type.model_fields, (
171+
f"{name} missing is_fixed discriminator"
172+
)
167173

168174
# Auction options should have is_fixed discriminator
169175
auction_types = [CpmAuctionPricingOption]
170176
for pricing_type in auction_types:
171-
assert "is_fixed" in pricing_type.model_fields, f"{pricing_type.__name__} missing is_fixed discriminator"
177+
name = pricing_type.__name__
178+
assert "is_fixed" in pricing_type.model_fields, (
179+
f"{name} missing is_fixed discriminator"
180+
)
172181

173182

174183
def test_semantic_aliases_point_to_discriminated_variants():
175184
"""Semantic aliases successfully construct their respective variants."""
176185
from adcp import (
177-
UrlPreviewRender,
178-
HtmlPreviewRender,
179-
CreateMediaBuySuccessResponse,
180186
CreateMediaBuyErrorResponse,
187+
CreateMediaBuySuccessResponse,
188+
HtmlPreviewRender,
189+
UrlPreviewRender,
181190
)
182191

183192
# URL preview render should accept url output format

tests/test_simple_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
import pytest
88

99
from adcp.testing import test_agent
10-
from adcp.types.core import TaskResult, TaskStatus
1110
from adcp.types._generated import (
1211
GetProductsResponse,
1312
ListCreativeFormatsResponse,
1413
PreviewCreativeResponse1,
1514
Product,
1615
)
16+
from adcp.types.core import TaskResult, TaskStatus
1717

1818

1919
@pytest.mark.asyncio

0 commit comments

Comments
 (0)