Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ print(products.products[0].name)
**Standard API** (`client.*`) - Recommended for production:
```python
from adcp.testing import test_agent
from adcp.types.generated import GetProductsRequest
from adcp import GetProductsRequest

# Explicit request objects and TaskResult wrapper
request = GetProductsRequest(brief='Coffee brands')
Expand Down Expand Up @@ -85,6 +85,8 @@ Pre-configured agents (all include `.simple` accessor):

See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.

> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.

## Quick Start: Distributed Operations

For production use, configure your own agents:
Expand Down Expand Up @@ -148,7 +150,7 @@ from adcp.testing import (
test_agent_no_auth, test_agent_a2a_no_auth,
creative_agent, test_agent_client, create_test_agent
)
from adcp.types.generated import GetProductsRequest, PreviewCreativeRequest
from adcp import GetProductsRequest, PreviewCreativeRequest

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

### Type Safety

Full type hints with Pydantic validation and auto-generated types from the AdCP spec:

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

#### Semantic Type Aliases

For discriminated union types (success/error responses), use semantic aliases for clearer code:

```python
from adcp import (
CreateMediaBuySuccessResponse, # Clear: this is the success case
CreateMediaBuyErrorResponse, # Clear: this is the error case
)

def handle_response(
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
) -> None:
if isinstance(response, CreateMediaBuySuccessResponse):
print(f"✅ Media buy created: {response.media_buy_id}")
else:
print(f"❌ Errors: {response.errors}")
```

**Available semantic aliases:**
- Response types: `*SuccessResponse` / `*ErrorResponse` (e.g., `CreateMediaBuySuccessResponse`)
- Request variants: `*FormatRequest` / `*ManifestRequest` (e.g., `PreviewCreativeFormatRequest`)
- Preview renders: `PreviewRenderImage` / `PreviewRenderHtml` / `PreviewRenderIframe`
- Activation keys: `PropertyIdActivationKey` / `PropertyTagActivationKey`

See `examples/type_aliases_demo.py` for more examples.

**Import guidelines:**
- ✅ **DO**: Import from main package: `from adcp import GetProductsRequest`
- ✅ **DO**: Use semantic aliases: `from adcp import CreateMediaBuySuccessResponse`
- ⚠️ **AVOID**: Import from internal modules: `from adcp.types.generated import CreateMediaBuyResponse1`

The main package exports provide a stable API while internal generated types may change.

### Multi-Agent Operations
Execute across multiple agents simultaneously:

Expand Down
58 changes: 58 additions & 0 deletions examples/type_aliases_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Demonstration of ergonomic type aliases.

This example shows how to use the semantic type aliases for better code clarity.
"""

from __future__ import annotations

# Import semantic aliases from the main package
from adcp import (
CreateMediaBuyErrorResponse,
CreateMediaBuySuccessResponse,
)


def handle_create_media_buy_response(
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse,
) -> None:
"""Handle a create media buy response with semantic types.

Before ergonomic aliases (unclear):
response: CreateMediaBuyResponse1 | CreateMediaBuyResponse2

After ergonomic aliases (clear):
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse

The semantic names make it immediately clear what each variant represents.
"""
# Type narrowing with isinstance works perfectly
if isinstance(response, CreateMediaBuySuccessResponse):
print(f"✅ Success! Media buy created: {response.media_buy_id}")
print(f" Buyer reference: {response.buyer_ref}")
print(f" Packages: {len(response.packages)}")
elif isinstance(response, CreateMediaBuyErrorResponse):
print("❌ Error creating media buy:")
for error in response.errors:
print(f" - {error.code}: {error.message}")


# Example usage
if __name__ == "__main__":
# Success case
success = CreateMediaBuySuccessResponse(
media_buy_id="mb_12345",
buyer_ref="ref_67890",
packages=[],
)
handle_create_media_buy_response(success)

print()

# Error case
error = CreateMediaBuyErrorResponse(
errors=[
{"code": "invalid_budget", "message": "Budget must be at least $100"},
{"code": "missing_dates", "message": "Start and end dates required"},
]
)
handle_create_media_buy_response(error)
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Issues = "https://github.com/adcontextprotocol/adcp-client-python/issues"
[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
adcp = ["py.typed"]

[tool.black]
line-length = 100
target-version = ["py310", "py311", "py312"]
Expand Down
127 changes: 125 additions & 2 deletions src/adcp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,75 @@
)

# Import all generated types - users can import what they need from adcp.types.generated
from adcp.types import generated
from adcp.types import aliases, generated

# Re-export semantic type aliases for better ergonomics
from adcp.types.aliases import (
ActivateSignalErrorResponse,
ActivateSignalSuccessResponse,
BuildCreativeErrorResponse,
BuildCreativeSuccessResponse,
CreateMediaBuyErrorResponse,
CreateMediaBuySuccessResponse,
PreviewCreativeFormatRequest,
PreviewCreativeInteractiveResponse,
PreviewCreativeManifestRequest,
PreviewCreativeStaticResponse,
PreviewRenderHtml,
PreviewRenderIframe,
PreviewRenderImage,
PropertyIdActivationKey,
PropertyTagActivationKey,
ProvidePerformanceFeedbackErrorResponse,
ProvidePerformanceFeedbackSuccessResponse,
SyncCreativesErrorResponse,
SyncCreativesSuccessResponse,
UpdateMediaBuyErrorResponse,
UpdateMediaBuyPackagesRequest,
UpdateMediaBuyPropertiesRequest,
UpdateMediaBuySuccessResponse,
)
from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata

# Re-export commonly-used request/response types for convenience
# Users should import from main package (e.g., `from adcp import GetProductsRequest`)
# rather than internal modules for better API stability
from adcp.types.generated import (
# Audience & Targeting
ActivateSignalRequest,
ActivateSignalResponse,
# Creative Operations
BuildCreativeRequest,
BuildCreativeResponse,
# Media Buy Operations
CreateMediaBuyRequest,
CreateMediaBuyResponse,
# Common data types
Error,
Format,
GetMediaBuyDeliveryRequest,
GetMediaBuyDeliveryResponse,
GetProductsRequest,
GetProductsResponse,
GetSignalsRequest,
GetSignalsResponse,
ListAuthorizedPropertiesRequest,
ListAuthorizedPropertiesResponse,
ListCreativeFormatsRequest,
ListCreativeFormatsResponse,
ListCreativesRequest,
ListCreativesResponse,
PreviewCreativeRequest,
PreviewCreativeResponse,
Product,
Property,
ProvidePerformanceFeedbackRequest,
ProvidePerformanceFeedbackResponse,
SyncCreativesRequest,
SyncCreativesResponse,
UpdateMediaBuyRequest,
UpdateMediaBuyResponse,
)
from adcp.types.generated import TaskStatus as GeneratedTaskStatus
from adcp.validation import (
ValidationError,
Expand All @@ -73,6 +140,37 @@
"TaskResult",
"TaskStatus",
"WebhookMetadata",
# Common request/response types (re-exported for convenience)
"CreateMediaBuyRequest",
"CreateMediaBuyResponse",
"GetMediaBuyDeliveryRequest",
"GetMediaBuyDeliveryResponse",
"GetProductsRequest",
"GetProductsResponse",
"UpdateMediaBuyRequest",
"UpdateMediaBuyResponse",
"BuildCreativeRequest",
"BuildCreativeResponse",
"ListCreativeFormatsRequest",
"ListCreativeFormatsResponse",
"ListCreativesRequest",
"ListCreativesResponse",
"PreviewCreativeRequest",
"PreviewCreativeResponse",
"SyncCreativesRequest",
"SyncCreativesResponse",
"ActivateSignalRequest",
"ActivateSignalResponse",
"GetSignalsRequest",
"GetSignalsResponse",
"ListAuthorizedPropertiesRequest",
"ListAuthorizedPropertiesResponse",
"ProvidePerformanceFeedbackRequest",
"ProvidePerformanceFeedbackResponse",
"Error",
"Format",
"Product",
"Property",
# Adagents validation
"fetch_adagents",
"verify_agent_authorization",
Expand Down Expand Up @@ -114,7 +212,32 @@
"validate_agent_authorization",
"validate_product",
"validate_publisher_properties_item",
# Generated types module
# Generated types modules
"generated",
"aliases",
"GeneratedTaskStatus",
# Semantic type aliases (for better API ergonomics)
"ActivateSignalSuccessResponse",
"ActivateSignalErrorResponse",
"BuildCreativeSuccessResponse",
"BuildCreativeErrorResponse",
"CreateMediaBuySuccessResponse",
"CreateMediaBuyErrorResponse",
"ProvidePerformanceFeedbackSuccessResponse",
"ProvidePerformanceFeedbackErrorResponse",
"SyncCreativesSuccessResponse",
"SyncCreativesErrorResponse",
"UpdateMediaBuySuccessResponse",
"UpdateMediaBuyErrorResponse",
"PreviewCreativeFormatRequest",
"PreviewCreativeManifestRequest",
"PreviewCreativeStaticResponse",
"PreviewCreativeInteractiveResponse",
"PreviewRenderImage",
"PreviewRenderHtml",
"PreviewRenderIframe",
"PropertyIdActivationKey",
"PropertyTagActivationKey",
"UpdateMediaBuyPackagesRequest",
"UpdateMediaBuyPropertiesRequest",
]
Empty file added src/adcp/py.typed
Empty file.
Loading