Skip to content

Commit 5c63dec

Browse files
bokelleyclaude
andauthored
feat: Add ergonomic type aliases and public API exports (#47)
Improve developer experience by providing semantic type names that match the AdCP spec: - CreateMediaBuySuccessResponse / CreateMediaBuyErrorResponse instead of Response1/2 - Re-export all commonly-used request/response types from main package - Add PEP 561 py.typed marker for full type checker support - Update documentation with import guidelines and examples Users can now import cleanly from adcp without relying on internal modules. 🤖 Generated with Claude Code Co-authored-by: Claude <noreply@anthropic.com>
1 parent ed348d8 commit 5c63dec

File tree

8 files changed

+643
-28
lines changed

8 files changed

+643
-28
lines changed

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ print(products.products[0].name)
5555
**Standard API** (`client.*`) - Recommended for production:
5656
```python
5757
from adcp.testing import test_agent
58-
from adcp.types.generated import GetProductsRequest
58+
from adcp import GetProductsRequest
5959

6060
# Explicit request objects and TaskResult wrapper
6161
request = GetProductsRequest(brief='Coffee brands')
@@ -85,6 +85,8 @@ Pre-configured agents (all include `.simple` accessor):
8585
8686
See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.
8787

88+
> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.
89+
8890
## Quick Start: Distributed Operations
8991

9092
For production use, configure your own agents:
@@ -148,7 +150,7 @@ from adcp.testing import (
148150
test_agent_no_auth, test_agent_a2a_no_auth,
149151
creative_agent, test_agent_client, create_test_agent
150152
)
151-
from adcp.types.generated import GetProductsRequest, PreviewCreativeRequest
153+
from adcp import GetProductsRequest, PreviewCreativeRequest
152154

153155
# 1. Single agent with authentication (MCP)
154156
result = await test_agent.get_products(
@@ -204,6 +206,7 @@ client = ADCPClient(config)
204206
- **Auto-detection**: Automatically detect which protocol an agent uses
205207

206208
### Type Safety
209+
207210
Full type hints with Pydantic validation and auto-generated types from the AdCP spec:
208211

209212
```python
@@ -219,6 +222,40 @@ if result.success:
219222
print(product.name, product.pricing_options) # Full IDE autocomplete!
220223
```
221224

225+
#### Semantic Type Aliases
226+
227+
For discriminated union types (success/error responses), use semantic aliases for clearer code:
228+
229+
```python
230+
from adcp import (
231+
CreateMediaBuySuccessResponse, # Clear: this is the success case
232+
CreateMediaBuyErrorResponse, # Clear: this is the error case
233+
)
234+
235+
def handle_response(
236+
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
237+
) -> None:
238+
if isinstance(response, CreateMediaBuySuccessResponse):
239+
print(f"✅ Media buy created: {response.media_buy_id}")
240+
else:
241+
print(f"❌ Errors: {response.errors}")
242+
```
243+
244+
**Available semantic aliases:**
245+
- Response types: `*SuccessResponse` / `*ErrorResponse` (e.g., `CreateMediaBuySuccessResponse`)
246+
- Request variants: `*FormatRequest` / `*ManifestRequest` (e.g., `PreviewCreativeFormatRequest`)
247+
- Preview renders: `PreviewRenderImage` / `PreviewRenderHtml` / `PreviewRenderIframe`
248+
- Activation keys: `PropertyIdActivationKey` / `PropertyTagActivationKey`
249+
250+
See `examples/type_aliases_demo.py` for more examples.
251+
252+
**Import guidelines:**
253+
-**DO**: Import from main package: `from adcp import GetProductsRequest`
254+
-**DO**: Use semantic aliases: `from adcp import CreateMediaBuySuccessResponse`
255+
- ⚠️ **AVOID**: Import from internal modules: `from adcp.types.generated import CreateMediaBuyResponse1`
256+
257+
The main package exports provide a stable API while internal generated types may change.
258+
222259
### Multi-Agent Operations
223260
Execute across multiple agents simultaneously:
224261

examples/type_aliases_demo.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Demonstration of ergonomic type aliases.
2+
3+
This example shows how to use the semantic type aliases for better code clarity.
4+
"""
5+
6+
from __future__ import annotations
7+
8+
# Import semantic aliases from the main package
9+
from adcp import (
10+
CreateMediaBuyErrorResponse,
11+
CreateMediaBuySuccessResponse,
12+
)
13+
14+
15+
def handle_create_media_buy_response(
16+
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse,
17+
) -> None:
18+
"""Handle a create media buy response with semantic types.
19+
20+
Before ergonomic aliases (unclear):
21+
response: CreateMediaBuyResponse1 | CreateMediaBuyResponse2
22+
23+
After ergonomic aliases (clear):
24+
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
25+
26+
The semantic names make it immediately clear what each variant represents.
27+
"""
28+
# Type narrowing with isinstance works perfectly
29+
if isinstance(response, CreateMediaBuySuccessResponse):
30+
print(f"✅ Success! Media buy created: {response.media_buy_id}")
31+
print(f" Buyer reference: {response.buyer_ref}")
32+
print(f" Packages: {len(response.packages)}")
33+
elif isinstance(response, CreateMediaBuyErrorResponse):
34+
print("❌ Error creating media buy:")
35+
for error in response.errors:
36+
print(f" - {error.code}: {error.message}")
37+
38+
39+
# Example usage
40+
if __name__ == "__main__":
41+
# Success case
42+
success = CreateMediaBuySuccessResponse(
43+
media_buy_id="mb_12345",
44+
buyer_ref="ref_67890",
45+
packages=[],
46+
)
47+
handle_create_media_buy_response(success)
48+
49+
print()
50+
51+
# Error case
52+
error = CreateMediaBuyErrorResponse(
53+
errors=[
54+
{"code": "invalid_budget", "message": "Budget must be at least $100"},
55+
{"code": "missing_dates", "message": "Start and end dates required"},
56+
]
57+
)
58+
handle_create_media_buy_response(error)

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Issues = "https://github.com/adcontextprotocol/adcp-client-python/issues"
5757
[tool.setuptools.packages.find]
5858
where = ["src"]
5959

60+
[tool.setuptools.package-data]
61+
adcp = ["py.typed"]
62+
6063
[tool.black]
6164
line-length = 100
6265
target-version = ["py310", "py311", "py312"]

src/adcp/__init__.py

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,75 @@
5050
)
5151

5252
# Import all generated types - users can import what they need from adcp.types.generated
53-
from adcp.types import generated
53+
from adcp.types import aliases, generated
54+
55+
# Re-export semantic type aliases for better ergonomics
56+
from adcp.types.aliases import (
57+
ActivateSignalErrorResponse,
58+
ActivateSignalSuccessResponse,
59+
BuildCreativeErrorResponse,
60+
BuildCreativeSuccessResponse,
61+
CreateMediaBuyErrorResponse,
62+
CreateMediaBuySuccessResponse,
63+
PreviewCreativeFormatRequest,
64+
PreviewCreativeInteractiveResponse,
65+
PreviewCreativeManifestRequest,
66+
PreviewCreativeStaticResponse,
67+
PreviewRenderHtml,
68+
PreviewRenderIframe,
69+
PreviewRenderImage,
70+
PropertyIdActivationKey,
71+
PropertyTagActivationKey,
72+
ProvidePerformanceFeedbackErrorResponse,
73+
ProvidePerformanceFeedbackSuccessResponse,
74+
SyncCreativesErrorResponse,
75+
SyncCreativesSuccessResponse,
76+
UpdateMediaBuyErrorResponse,
77+
UpdateMediaBuyPackagesRequest,
78+
UpdateMediaBuyPropertiesRequest,
79+
UpdateMediaBuySuccessResponse,
80+
)
5481
from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
82+
83+
# Re-export commonly-used request/response types for convenience
84+
# Users should import from main package (e.g., `from adcp import GetProductsRequest`)
85+
# rather than internal modules for better API stability
86+
from adcp.types.generated import (
87+
# Audience & Targeting
88+
ActivateSignalRequest,
89+
ActivateSignalResponse,
90+
# Creative Operations
91+
BuildCreativeRequest,
92+
BuildCreativeResponse,
93+
# Media Buy Operations
94+
CreateMediaBuyRequest,
95+
CreateMediaBuyResponse,
96+
# Common data types
97+
Error,
98+
Format,
99+
GetMediaBuyDeliveryRequest,
100+
GetMediaBuyDeliveryResponse,
101+
GetProductsRequest,
102+
GetProductsResponse,
103+
GetSignalsRequest,
104+
GetSignalsResponse,
105+
ListAuthorizedPropertiesRequest,
106+
ListAuthorizedPropertiesResponse,
107+
ListCreativeFormatsRequest,
108+
ListCreativeFormatsResponse,
109+
ListCreativesRequest,
110+
ListCreativesResponse,
111+
PreviewCreativeRequest,
112+
PreviewCreativeResponse,
113+
Product,
114+
Property,
115+
ProvidePerformanceFeedbackRequest,
116+
ProvidePerformanceFeedbackResponse,
117+
SyncCreativesRequest,
118+
SyncCreativesResponse,
119+
UpdateMediaBuyRequest,
120+
UpdateMediaBuyResponse,
121+
)
55122
from adcp.types.generated import TaskStatus as GeneratedTaskStatus
56123
from adcp.validation import (
57124
ValidationError,
@@ -73,6 +140,37 @@
73140
"TaskResult",
74141
"TaskStatus",
75142
"WebhookMetadata",
143+
# Common request/response types (re-exported for convenience)
144+
"CreateMediaBuyRequest",
145+
"CreateMediaBuyResponse",
146+
"GetMediaBuyDeliveryRequest",
147+
"GetMediaBuyDeliveryResponse",
148+
"GetProductsRequest",
149+
"GetProductsResponse",
150+
"UpdateMediaBuyRequest",
151+
"UpdateMediaBuyResponse",
152+
"BuildCreativeRequest",
153+
"BuildCreativeResponse",
154+
"ListCreativeFormatsRequest",
155+
"ListCreativeFormatsResponse",
156+
"ListCreativesRequest",
157+
"ListCreativesResponse",
158+
"PreviewCreativeRequest",
159+
"PreviewCreativeResponse",
160+
"SyncCreativesRequest",
161+
"SyncCreativesResponse",
162+
"ActivateSignalRequest",
163+
"ActivateSignalResponse",
164+
"GetSignalsRequest",
165+
"GetSignalsResponse",
166+
"ListAuthorizedPropertiesRequest",
167+
"ListAuthorizedPropertiesResponse",
168+
"ProvidePerformanceFeedbackRequest",
169+
"ProvidePerformanceFeedbackResponse",
170+
"Error",
171+
"Format",
172+
"Product",
173+
"Property",
76174
# Adagents validation
77175
"fetch_adagents",
78176
"verify_agent_authorization",
@@ -114,7 +212,32 @@
114212
"validate_agent_authorization",
115213
"validate_product",
116214
"validate_publisher_properties_item",
117-
# Generated types module
215+
# Generated types modules
118216
"generated",
217+
"aliases",
119218
"GeneratedTaskStatus",
219+
# Semantic type aliases (for better API ergonomics)
220+
"ActivateSignalSuccessResponse",
221+
"ActivateSignalErrorResponse",
222+
"BuildCreativeSuccessResponse",
223+
"BuildCreativeErrorResponse",
224+
"CreateMediaBuySuccessResponse",
225+
"CreateMediaBuyErrorResponse",
226+
"ProvidePerformanceFeedbackSuccessResponse",
227+
"ProvidePerformanceFeedbackErrorResponse",
228+
"SyncCreativesSuccessResponse",
229+
"SyncCreativesErrorResponse",
230+
"UpdateMediaBuySuccessResponse",
231+
"UpdateMediaBuyErrorResponse",
232+
"PreviewCreativeFormatRequest",
233+
"PreviewCreativeManifestRequest",
234+
"PreviewCreativeStaticResponse",
235+
"PreviewCreativeInteractiveResponse",
236+
"PreviewRenderImage",
237+
"PreviewRenderHtml",
238+
"PreviewRenderIframe",
239+
"PropertyIdActivationKey",
240+
"PropertyTagActivationKey",
241+
"UpdateMediaBuyPackagesRequest",
242+
"UpdateMediaBuyPropertiesRequest",
120243
]

src/adcp/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)