adcp 1.2.0__py3-none-any.whl → 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adcp/__init__.py +187 -17
- adcp/client.py +12 -5
- adcp/protocols/base.py +5 -2
- adcp/types/generated.py +464 -55
- adcp/utils/response_parser.py +64 -15
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/METADATA +3 -1
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/RECORD +11 -11
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/WHEEL +0 -0
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/entry_points.txt +0 -0
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/licenses/LICENSE +0 -0
- {adcp-1.2.0.dist-info → adcp-1.2.1.dist-info}/top_level.txt +0 -0
adcp/__init__.py
CHANGED
|
@@ -20,33 +20,120 @@ from adcp.exceptions import (
|
|
|
20
20
|
)
|
|
21
21
|
from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
|
|
22
22
|
from adcp.types.generated import (
|
|
23
|
+
ActivateSignalError,
|
|
24
|
+
# Request/Response types
|
|
23
25
|
ActivateSignalRequest,
|
|
24
26
|
ActivateSignalResponse,
|
|
27
|
+
ActivateSignalSuccess,
|
|
28
|
+
ActivationKey,
|
|
29
|
+
AgentDeployment,
|
|
30
|
+
AgentDestination,
|
|
31
|
+
BothPreviewRender,
|
|
32
|
+
# Brand types
|
|
33
|
+
BrandManifest,
|
|
34
|
+
BrandManifestRef,
|
|
35
|
+
BuildCreativeRequest,
|
|
36
|
+
BuildCreativeResponse,
|
|
37
|
+
# Channel types
|
|
38
|
+
Channels,
|
|
39
|
+
CreateMediaBuyError,
|
|
25
40
|
CreateMediaBuyRequest,
|
|
26
41
|
CreateMediaBuyResponse,
|
|
42
|
+
CreateMediaBuySuccess,
|
|
43
|
+
# Creative types
|
|
44
|
+
CreativeAsset,
|
|
45
|
+
CreativeAssignment,
|
|
46
|
+
CreativeManifest,
|
|
47
|
+
CreativePolicy,
|
|
48
|
+
DaastAsset,
|
|
49
|
+
# Metrics types
|
|
50
|
+
DeliveryMetrics,
|
|
51
|
+
# Delivery types
|
|
52
|
+
DeliveryType,
|
|
53
|
+
Deployment,
|
|
54
|
+
# Deployment types
|
|
55
|
+
Destination,
|
|
56
|
+
Error,
|
|
57
|
+
Format,
|
|
58
|
+
FormatId,
|
|
59
|
+
FrequencyCap,
|
|
27
60
|
GetMediaBuyDeliveryRequest,
|
|
28
61
|
GetMediaBuyDeliveryResponse,
|
|
29
62
|
GetProductsRequest,
|
|
30
63
|
GetProductsResponse,
|
|
31
64
|
GetSignalsRequest,
|
|
32
65
|
GetSignalsResponse,
|
|
66
|
+
HtmlPreviewRender,
|
|
67
|
+
InlineDaastAsset,
|
|
68
|
+
InlineVastAsset,
|
|
69
|
+
Key_valueActivationKey,
|
|
33
70
|
ListAuthorizedPropertiesRequest,
|
|
34
71
|
ListAuthorizedPropertiesResponse,
|
|
35
72
|
ListCreativeFormatsRequest,
|
|
36
73
|
ListCreativeFormatsResponse,
|
|
37
74
|
ListCreativesRequest,
|
|
38
75
|
ListCreativesResponse,
|
|
76
|
+
Measurement,
|
|
77
|
+
# Core domain types
|
|
39
78
|
MediaBuy,
|
|
79
|
+
# Status enums
|
|
80
|
+
MediaBuyStatus,
|
|
81
|
+
# Sub-asset types
|
|
82
|
+
MediaSubAsset,
|
|
83
|
+
Pacing,
|
|
84
|
+
Package,
|
|
85
|
+
PackageStatus,
|
|
86
|
+
PerformanceFeedback,
|
|
87
|
+
Placement,
|
|
88
|
+
PlatformDeployment,
|
|
89
|
+
PlatformDestination,
|
|
90
|
+
PreviewCreativeRequest,
|
|
91
|
+
PreviewCreativeResponse,
|
|
92
|
+
# Preview render types
|
|
93
|
+
PreviewRender,
|
|
94
|
+
PricingModel,
|
|
95
|
+
# Pricing types
|
|
96
|
+
PricingOption,
|
|
40
97
|
Product,
|
|
98
|
+
PromotedProducts,
|
|
99
|
+
# Property and placement types
|
|
100
|
+
Property,
|
|
101
|
+
ProtocolEnvelope,
|
|
41
102
|
ProvidePerformanceFeedbackRequest,
|
|
42
103
|
ProvidePerformanceFeedbackResponse,
|
|
104
|
+
PushNotificationConfig,
|
|
105
|
+
ReportingCapabilities,
|
|
106
|
+
Response,
|
|
107
|
+
Segment_idActivationKey,
|
|
108
|
+
StandardFormatIds,
|
|
109
|
+
StartTiming,
|
|
110
|
+
SubAsset,
|
|
111
|
+
SyncCreativesError,
|
|
43
112
|
SyncCreativesRequest,
|
|
44
113
|
SyncCreativesResponse,
|
|
114
|
+
SyncCreativesSuccess,
|
|
115
|
+
# Targeting types
|
|
116
|
+
Targeting,
|
|
117
|
+
# Task types
|
|
118
|
+
TaskType,
|
|
119
|
+
TextSubAsset,
|
|
120
|
+
UpdateMediaBuyError,
|
|
45
121
|
UpdateMediaBuyRequest,
|
|
46
122
|
UpdateMediaBuyResponse,
|
|
123
|
+
UpdateMediaBuySuccess,
|
|
124
|
+
UrlDaastAsset,
|
|
125
|
+
UrlPreviewRender,
|
|
126
|
+
UrlVastAsset,
|
|
127
|
+
# Asset delivery types (VAST/DAAST)
|
|
128
|
+
VastAsset,
|
|
129
|
+
# Protocol types
|
|
130
|
+
WebhookPayload,
|
|
131
|
+
)
|
|
132
|
+
from adcp.types.generated import (
|
|
133
|
+
TaskStatus as GeneratedTaskStatus,
|
|
47
134
|
)
|
|
48
135
|
|
|
49
|
-
__version__ = "1.2.
|
|
136
|
+
__version__ = "1.2.1"
|
|
50
137
|
|
|
51
138
|
__all__ = [
|
|
52
139
|
# Client classes
|
|
@@ -67,30 +154,113 @@ __all__ = [
|
|
|
67
154
|
"ADCPToolNotFoundError",
|
|
68
155
|
"ADCPWebhookError",
|
|
69
156
|
"ADCPWebhookSignatureError",
|
|
70
|
-
#
|
|
71
|
-
"
|
|
72
|
-
"
|
|
157
|
+
# Request/Response types
|
|
158
|
+
"ActivateSignalRequest",
|
|
159
|
+
"ActivateSignalResponse",
|
|
160
|
+
"ActivateSignalSuccess",
|
|
161
|
+
"ActivateSignalError",
|
|
162
|
+
"ActivationKey",
|
|
163
|
+
"Segment_idActivationKey",
|
|
164
|
+
"Key_valueActivationKey",
|
|
165
|
+
"BuildCreativeRequest",
|
|
166
|
+
"BuildCreativeResponse",
|
|
73
167
|
"CreateMediaBuyRequest",
|
|
74
168
|
"CreateMediaBuyResponse",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"SyncCreativesRequest",
|
|
78
|
-
"SyncCreativesResponse",
|
|
79
|
-
"ListCreativesRequest",
|
|
80
|
-
"ListCreativesResponse",
|
|
81
|
-
"ListCreativeFormatsRequest",
|
|
82
|
-
"ListCreativeFormatsResponse",
|
|
169
|
+
"CreateMediaBuySuccess",
|
|
170
|
+
"CreateMediaBuyError",
|
|
83
171
|
"GetMediaBuyDeliveryRequest",
|
|
84
172
|
"GetMediaBuyDeliveryResponse",
|
|
85
|
-
"
|
|
86
|
-
"
|
|
173
|
+
"GetProductsRequest",
|
|
174
|
+
"GetProductsResponse",
|
|
87
175
|
"GetSignalsRequest",
|
|
88
176
|
"GetSignalsResponse",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
177
|
+
"ListAuthorizedPropertiesRequest",
|
|
178
|
+
"ListAuthorizedPropertiesResponse",
|
|
179
|
+
"ListCreativeFormatsRequest",
|
|
180
|
+
"ListCreativeFormatsResponse",
|
|
181
|
+
"ListCreativesRequest",
|
|
182
|
+
"ListCreativesResponse",
|
|
183
|
+
"PreviewCreativeRequest",
|
|
184
|
+
"PreviewCreativeResponse",
|
|
91
185
|
"ProvidePerformanceFeedbackRequest",
|
|
92
186
|
"ProvidePerformanceFeedbackResponse",
|
|
187
|
+
"SyncCreativesRequest",
|
|
188
|
+
"SyncCreativesResponse",
|
|
189
|
+
"SyncCreativesSuccess",
|
|
190
|
+
"SyncCreativesError",
|
|
191
|
+
"UpdateMediaBuyRequest",
|
|
192
|
+
"UpdateMediaBuyResponse",
|
|
193
|
+
"UpdateMediaBuySuccess",
|
|
194
|
+
"UpdateMediaBuyError",
|
|
93
195
|
# Core domain types
|
|
94
|
-
"Product",
|
|
95
196
|
"MediaBuy",
|
|
197
|
+
"Product",
|
|
198
|
+
"Package",
|
|
199
|
+
"Error",
|
|
200
|
+
# Creative types
|
|
201
|
+
"CreativeAsset",
|
|
202
|
+
"CreativeManifest",
|
|
203
|
+
"CreativeAssignment",
|
|
204
|
+
"CreativePolicy",
|
|
205
|
+
"Format",
|
|
206
|
+
"FormatId",
|
|
207
|
+
# Property and placement types
|
|
208
|
+
"Property",
|
|
209
|
+
"Placement",
|
|
210
|
+
# Targeting types
|
|
211
|
+
"Targeting",
|
|
212
|
+
"FrequencyCap",
|
|
213
|
+
"Pacing",
|
|
214
|
+
# Brand types
|
|
215
|
+
"BrandManifest",
|
|
216
|
+
"BrandManifestRef",
|
|
217
|
+
# Metrics types
|
|
218
|
+
"DeliveryMetrics",
|
|
219
|
+
"Measurement",
|
|
220
|
+
"PerformanceFeedback",
|
|
221
|
+
# Status enums
|
|
222
|
+
"MediaBuyStatus",
|
|
223
|
+
"PackageStatus",
|
|
224
|
+
# Pricing types
|
|
225
|
+
"PricingOption",
|
|
226
|
+
"PricingModel",
|
|
227
|
+
# Delivery types
|
|
228
|
+
"DeliveryType",
|
|
229
|
+
"StartTiming",
|
|
230
|
+
# Channel types
|
|
231
|
+
"Channels",
|
|
232
|
+
"StandardFormatIds",
|
|
233
|
+
# Protocol types
|
|
234
|
+
"WebhookPayload",
|
|
235
|
+
"ProtocolEnvelope",
|
|
236
|
+
"Response",
|
|
237
|
+
"PromotedProducts",
|
|
238
|
+
"PushNotificationConfig",
|
|
239
|
+
"ReportingCapabilities",
|
|
240
|
+
# Deployment types
|
|
241
|
+
"Destination",
|
|
242
|
+
"Deployment",
|
|
243
|
+
"PlatformDestination",
|
|
244
|
+
"AgentDestination",
|
|
245
|
+
"PlatformDeployment",
|
|
246
|
+
"AgentDeployment",
|
|
247
|
+
# Sub-asset types
|
|
248
|
+
"MediaSubAsset",
|
|
249
|
+
"SubAsset",
|
|
250
|
+
"TextSubAsset",
|
|
251
|
+
# Asset delivery types (VAST/DAAST)
|
|
252
|
+
"VastAsset",
|
|
253
|
+
"UrlVastAsset",
|
|
254
|
+
"InlineVastAsset",
|
|
255
|
+
"DaastAsset",
|
|
256
|
+
"UrlDaastAsset",
|
|
257
|
+
"InlineDaastAsset",
|
|
258
|
+
# Preview render types
|
|
259
|
+
"PreviewRender",
|
|
260
|
+
"UrlPreviewRender",
|
|
261
|
+
"HtmlPreviewRender",
|
|
262
|
+
"BothPreviewRender",
|
|
263
|
+
# Task types
|
|
264
|
+
"TaskType",
|
|
265
|
+
"GeneratedTaskStatus",
|
|
96
266
|
]
|
adcp/client.py
CHANGED
|
@@ -11,6 +11,8 @@ from collections.abc import Callable
|
|
|
11
11
|
from datetime import datetime, timezone
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
14
16
|
from adcp.exceptions import ADCPWebhookSignatureError
|
|
15
17
|
from adcp.protocols.a2a import A2AAdapter
|
|
16
18
|
from adcp.protocols.base import ProtocolAdapter
|
|
@@ -154,7 +156,9 @@ class ADCPClient:
|
|
|
154
156
|
)
|
|
155
157
|
)
|
|
156
158
|
|
|
157
|
-
result = self.adapter._parse_response(
|
|
159
|
+
result: TaskResult[GetProductsResponse] = self.adapter._parse_response(
|
|
160
|
+
raw_result, GetProductsResponse
|
|
161
|
+
)
|
|
158
162
|
|
|
159
163
|
if fetch_previews and result.success and result.data and creative_agent_client:
|
|
160
164
|
from adcp.utils.preview_cache import add_preview_urls_to_products
|
|
@@ -215,7 +219,9 @@ class ADCPClient:
|
|
|
215
219
|
)
|
|
216
220
|
)
|
|
217
221
|
|
|
218
|
-
result = self.adapter._parse_response(
|
|
222
|
+
result: TaskResult[ListCreativeFormatsResponse] = self.adapter._parse_response(
|
|
223
|
+
raw_result, ListCreativeFormatsResponse
|
|
224
|
+
)
|
|
219
225
|
|
|
220
226
|
if fetch_previews and result.success and result.data:
|
|
221
227
|
from adcp.utils.preview_cache import add_preview_urls_to_formats
|
|
@@ -617,15 +623,16 @@ class ADCPClient:
|
|
|
617
623
|
from adcp.utils.response_parser import parse_json_or_text
|
|
618
624
|
|
|
619
625
|
# Map task types to their response types (using string literals, not enum)
|
|
620
|
-
|
|
626
|
+
# Note: Some response types are Union types (e.g., ActivateSignalResponse = Success | Error)
|
|
627
|
+
response_type_map: dict[str, type[BaseModel] | Any] = {
|
|
621
628
|
"get_products": GetProductsResponse,
|
|
622
629
|
"list_creative_formats": ListCreativeFormatsResponse,
|
|
623
|
-
"sync_creatives": SyncCreativesResponse,
|
|
630
|
+
"sync_creatives": SyncCreativesResponse, # Union type
|
|
624
631
|
"list_creatives": ListCreativesResponse,
|
|
625
632
|
"get_media_buy_delivery": GetMediaBuyDeliveryResponse,
|
|
626
633
|
"list_authorized_properties": ListAuthorizedPropertiesResponse,
|
|
627
634
|
"get_signals": GetSignalsResponse,
|
|
628
|
-
"activate_signal": ActivateSignalResponse,
|
|
635
|
+
"activate_signal": ActivateSignalResponse, # Union type
|
|
629
636
|
"provide_performance_feedback": ProvidePerformanceFeedbackResponse,
|
|
630
637
|
}
|
|
631
638
|
|
adcp/protocols/base.py
CHANGED
|
@@ -30,15 +30,18 @@ class ProtocolAdapter(ABC):
|
|
|
30
30
|
# Helper methods for response parsing
|
|
31
31
|
# ========================================================================
|
|
32
32
|
|
|
33
|
-
def _parse_response(
|
|
33
|
+
def _parse_response(
|
|
34
|
+
self, raw_result: TaskResult[Any], response_type: type[T] | Any
|
|
35
|
+
) -> TaskResult[T]:
|
|
34
36
|
"""
|
|
35
37
|
Parse raw TaskResult into typed TaskResult.
|
|
36
38
|
|
|
37
39
|
Handles both MCP content arrays and A2A dict responses.
|
|
40
|
+
Supports both single types and Union types (for oneOf discriminated unions).
|
|
38
41
|
|
|
39
42
|
Args:
|
|
40
43
|
raw_result: Raw TaskResult from adapter
|
|
41
|
-
response_type: Expected Pydantic response type
|
|
44
|
+
response_type: Expected Pydantic response type (can be a Union type)
|
|
42
45
|
|
|
43
46
|
Returns:
|
|
44
47
|
Typed TaskResult
|
adcp/types/generated.py
CHANGED
|
@@ -14,20 +14,9 @@ from __future__ import annotations
|
|
|
14
14
|
import re
|
|
15
15
|
from typing import Any, Literal
|
|
16
16
|
|
|
17
|
-
from pydantic import BaseModel, Field, field_validator
|
|
17
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
# ============================================================================
|
|
21
|
-
# MISSING SCHEMA TYPES (referenced but not provided by upstream)
|
|
22
|
-
# ============================================================================
|
|
23
|
-
|
|
24
|
-
# These types are referenced in schemas but don't have schema files
|
|
25
|
-
# Defining them as type aliases to maintain type safety
|
|
26
|
-
ActivationKey = dict[str, Any]
|
|
27
|
-
PackageRequest = dict[str, Any]
|
|
28
|
-
PushNotificationConfig = dict[str, Any]
|
|
29
|
-
ReportingCapabilities = dict[str, Any]
|
|
30
|
-
|
|
31
20
|
|
|
32
21
|
# ============================================================================
|
|
33
22
|
# CORE DOMAIN TYPES
|
|
@@ -287,13 +276,28 @@ class StartTimingVariant2(BaseModel):
|
|
|
287
276
|
StartTiming = StartTimingVariant1 | StartTimingVariant2
|
|
288
277
|
|
|
289
278
|
|
|
290
|
-
|
|
291
|
-
|
|
279
|
+
# Sub-asset for multi-asset creative formats, including carousel images and native ad template variables
|
|
280
|
+
|
|
281
|
+
class MediaSubAsset(BaseModel):
|
|
282
|
+
model_config = ConfigDict(extra="forbid")
|
|
283
|
+
|
|
284
|
+
asset_kind: Literal["media"] = Field(description="Discriminator indicating this is a media asset with content_uri")
|
|
285
|
+
asset_type: str = Field(description="Type of asset. Common types: thumbnail_image, product_image, featured_image, logo")
|
|
286
|
+
asset_id: str = Field(description="Unique identifier for the asset within the creative")
|
|
287
|
+
content_uri: str = Field(description="URL for media assets (images, videos, etc.)")
|
|
288
|
+
|
|
292
289
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
290
|
+
class TextSubAsset(BaseModel):
|
|
291
|
+
model_config = ConfigDict(extra="forbid")
|
|
292
|
+
|
|
293
|
+
asset_kind: Literal["text"] = Field(description="Discriminator indicating this is a text asset with content")
|
|
294
|
+
asset_type: str = Field(description="Type of asset. Common types: headline, body_text, cta_text, price_text, sponsor_name, author_name, click_url")
|
|
295
|
+
asset_id: str = Field(description="Unique identifier for the asset within the creative")
|
|
296
|
+
content: Any = Field(description="Text content for text-based assets like headlines, body text, CTA text, etc.")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Union type for Sub-Asset
|
|
300
|
+
SubAsset = MediaSubAsset | TextSubAsset
|
|
297
301
|
|
|
298
302
|
|
|
299
303
|
class WebhookPayload(BaseModel):
|
|
@@ -308,7 +312,7 @@ class WebhookPayload(BaseModel):
|
|
|
308
312
|
message: str | None = Field(None, description="Human-readable summary of the current task state. Provides context about what happened and what action may be needed.")
|
|
309
313
|
context_id: str | None = Field(None, description="Session/conversation identifier. Use this to continue the conversation if input-required status needs clarification or additional parameters.")
|
|
310
314
|
progress: dict[str, Any] | None = Field(None, description="Progress information for tasks still in 'working' state. Rarely seen in webhooks since 'working' tasks typically complete synchronously, but may appear if a task transitions from 'submitted' to 'working'.")
|
|
311
|
-
result: Any | None = Field(None, description="Task-specific payload for this status update.
|
|
315
|
+
result: Any | None = Field(None, description="Task-specific payload for this status update. Validated against the appropriate response schema based on task_type.")
|
|
312
316
|
error: Any | None = Field(None, description="Error message for failed tasks. Only present when status is 'failed'.")
|
|
313
317
|
|
|
314
318
|
|
|
@@ -344,12 +348,16 @@ class PromotedProducts(BaseModel):
|
|
|
344
348
|
# A destination platform where signals can be activated (DSP, sales agent, etc.)
|
|
345
349
|
|
|
346
350
|
class PlatformDestination(BaseModel):
|
|
351
|
+
model_config = ConfigDict(extra="forbid")
|
|
352
|
+
|
|
347
353
|
type: Literal["platform"] = Field(description="Discriminator indicating this is a platform-based destination")
|
|
348
354
|
platform: str = Field(description="Platform identifier for DSPs (e.g., 'the-trade-desk', 'amazon-dsp')")
|
|
349
355
|
account: str | None = Field(None, description="Optional account identifier on the platform")
|
|
350
356
|
|
|
351
357
|
|
|
352
358
|
class AgentDestination(BaseModel):
|
|
359
|
+
model_config = ConfigDict(extra="forbid")
|
|
360
|
+
|
|
353
361
|
type: Literal["agent"] = Field(description="Discriminator indicating this is an agent URL-based destination")
|
|
354
362
|
agent_url: str = Field(description="URL identifying the destination agent (for sales agents, etc.)")
|
|
355
363
|
account: str | None = Field(None, description="Optional account identifier on the agent")
|
|
@@ -362,6 +370,8 @@ Destination = PlatformDestination | AgentDestination
|
|
|
362
370
|
# A signal deployment to a specific destination platform with activation status and key
|
|
363
371
|
|
|
364
372
|
class PlatformDeployment(BaseModel):
|
|
373
|
+
model_config = ConfigDict(extra="forbid")
|
|
374
|
+
|
|
365
375
|
type: Literal["platform"] = Field(description="Discriminator indicating this is a platform-based deployment")
|
|
366
376
|
platform: str = Field(description="Platform identifier for DSPs")
|
|
367
377
|
account: str | None = Field(None, description="Account identifier if applicable")
|
|
@@ -372,6 +382,8 @@ class PlatformDeployment(BaseModel):
|
|
|
372
382
|
|
|
373
383
|
|
|
374
384
|
class AgentDeployment(BaseModel):
|
|
385
|
+
model_config = ConfigDict(extra="forbid")
|
|
386
|
+
|
|
375
387
|
type: Literal["agent"] = Field(description="Discriminator indicating this is an agent URL-based deployment")
|
|
376
388
|
agent_url: str = Field(description="URL identifying the destination agent")
|
|
377
389
|
account: str | None = Field(None, description="Account identifier if applicable")
|
|
@@ -385,6 +397,45 @@ class AgentDeployment(BaseModel):
|
|
|
385
397
|
Deployment = PlatformDeployment | AgentDeployment
|
|
386
398
|
|
|
387
399
|
|
|
400
|
+
# Universal identifier for using a signal on a destination platform. Can be either a segment ID or a key-value pair depending on the platform's targeting mechanism.
|
|
401
|
+
|
|
402
|
+
class Segment_idActivationKey(BaseModel):
|
|
403
|
+
model_config = ConfigDict(extra="forbid")
|
|
404
|
+
|
|
405
|
+
type: Literal["segment_id"] = Field(description="Segment ID based targeting")
|
|
406
|
+
segment_id: str = Field(description="The platform-specific segment identifier to use in campaign targeting")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class Key_valueActivationKey(BaseModel):
|
|
410
|
+
model_config = ConfigDict(extra="forbid")
|
|
411
|
+
|
|
412
|
+
type: Literal["key_value"] = Field(description="Key-value pair based targeting")
|
|
413
|
+
key: str = Field(description="The targeting parameter key")
|
|
414
|
+
value: str = Field(description="The targeting parameter value")
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
# Union type for Activation Key
|
|
418
|
+
ActivationKey = Segment_idActivationKey | Key_valueActivationKey
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class PushNotificationConfig(BaseModel):
|
|
422
|
+
"""Webhook configuration for asynchronous task notifications. Uses A2A-compatible PushNotificationConfig structure. Supports Bearer tokens (simple) or HMAC signatures (production-recommended)."""
|
|
423
|
+
|
|
424
|
+
url: str = Field(description="Webhook endpoint URL for task status notifications")
|
|
425
|
+
token: str | None = Field(None, description="Optional client-provided token for webhook validation. Echoed back in webhook payload to validate request authenticity.")
|
|
426
|
+
authentication: dict[str, Any] = Field(description="Authentication configuration for webhook delivery (A2A-compatible)")
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class ReportingCapabilities(BaseModel):
|
|
430
|
+
"""Reporting capabilities available for a product"""
|
|
431
|
+
|
|
432
|
+
available_reporting_frequencies: list[Literal["hourly", "daily", "monthly"]] = Field(description="Supported reporting frequency options")
|
|
433
|
+
expected_delay_minutes: int = Field(description="Expected delay in minutes before reporting data becomes available (e.g., 240 for 4-hour delay)")
|
|
434
|
+
timezone: str = Field(description="Timezone for reporting periods. Use 'UTC' or IANA timezone (e.g., 'America/New_York'). Critical for daily/monthly frequency alignment.")
|
|
435
|
+
supports_webhooks: bool = Field(description="Whether this product supports webhook-based reporting notifications")
|
|
436
|
+
available_metrics: list[Literal["impressions", "spend", "clicks", "ctr", "video_completions", "completion_rate", "conversions", "viewability", "engagement_rate"]] = Field(description="Metrics available in reporting. Impressions and spend are always implicitly included.")
|
|
437
|
+
|
|
438
|
+
|
|
388
439
|
# Type alias for Advertising Channels
|
|
389
440
|
# Standard advertising channels supported by AdCP
|
|
390
441
|
Channels = Literal["display", "video", "audio", "native", "dooh", "ctv", "podcast", "retail", "social"]
|
|
@@ -472,17 +523,118 @@ PricingOption = PricingOptionVariant1 | PricingOptionVariant2 | PricingOptionVar
|
|
|
472
523
|
StandardFormatIds = Literal["display_300x250", "display_728x90", "display_320x50", "display_160x600", "display_970x250", "display_336x280", "display_expandable_300x250", "display_expandable_728x90", "display_interstitial_320x480", "display_interstitial_desktop", "display_dynamic_300x250", "display_responsive", "native_in_feed", "native_content_recommendation", "native_product", "video_skippable_15s", "video_skippable_30s", "video_non_skippable_15s", "video_non_skippable_30s", "video_outstream_autoplay", "video_vertical_story", "video_rewarded_30s", "video_pause_ad", "video_ctv_non_skippable_30s", "audio_standard_15s", "audio_standard_30s", "audio_podcast_host_read", "audio_programmatic", "universal_carousel", "universal_canvas", "universal_takeover", "universal_gallery", "universal_reveal", "dooh_landscape_static", "dooh_portrait_video"]
|
|
473
524
|
|
|
474
525
|
|
|
526
|
+
# VAST (Video Ad Serving Template) tag for third-party video ad serving
|
|
527
|
+
|
|
528
|
+
class UrlVastAsset(BaseModel):
|
|
529
|
+
model_config = ConfigDict(extra="forbid")
|
|
530
|
+
|
|
531
|
+
delivery_type: Literal["url"] = Field(description="Discriminator indicating VAST is delivered via URL endpoint")
|
|
532
|
+
url: str = Field(description="URL endpoint that returns VAST XML")
|
|
533
|
+
vast_version: Literal["2.0", "3.0", "4.0", "4.1", "4.2"] | None = Field(None, description="VAST specification version")
|
|
534
|
+
vpaid_enabled: bool | None = Field(None, description="Whether VPAID (Video Player-Ad Interface Definition) is supported")
|
|
535
|
+
duration_ms: int | None = Field(None, description="Expected video duration in milliseconds (if known)")
|
|
536
|
+
tracking_events: list[Literal["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "impression", "click", "pause", "resume", "skip", "mute", "unmute", "fullscreen", "exitFullscreen", "playerExpand", "playerCollapse"]] | None = Field(None, description="Tracking events supported by this VAST tag")
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class InlineVastAsset(BaseModel):
|
|
540
|
+
model_config = ConfigDict(extra="forbid")
|
|
541
|
+
|
|
542
|
+
delivery_type: Literal["inline"] = Field(description="Discriminator indicating VAST is delivered as inline XML content")
|
|
543
|
+
content: str = Field(description="Inline VAST XML content")
|
|
544
|
+
vast_version: Literal["2.0", "3.0", "4.0", "4.1", "4.2"] | None = Field(None, description="VAST specification version")
|
|
545
|
+
vpaid_enabled: bool | None = Field(None, description="Whether VPAID (Video Player-Ad Interface Definition) is supported")
|
|
546
|
+
duration_ms: int | None = Field(None, description="Expected video duration in milliseconds (if known)")
|
|
547
|
+
tracking_events: list[Literal["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "impression", "click", "pause", "resume", "skip", "mute", "unmute", "fullscreen", "exitFullscreen", "playerExpand", "playerCollapse"]] | None = Field(None, description="Tracking events supported by this VAST tag")
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
# Union type for VAST Asset
|
|
551
|
+
VastAsset = UrlVastAsset | InlineVastAsset
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
# DAAST (Digital Audio Ad Serving Template) tag for third-party audio ad serving
|
|
555
|
+
|
|
556
|
+
class UrlDaastAsset(BaseModel):
|
|
557
|
+
model_config = ConfigDict(extra="forbid")
|
|
558
|
+
|
|
559
|
+
delivery_type: Literal["url"] = Field(description="Discriminator indicating DAAST is delivered via URL endpoint")
|
|
560
|
+
url: str = Field(description="URL endpoint that returns DAAST XML")
|
|
561
|
+
daast_version: Literal["1.0", "1.1"] | None = Field(None, description="DAAST specification version")
|
|
562
|
+
duration_ms: int | None = Field(None, description="Expected audio duration in milliseconds (if known)")
|
|
563
|
+
tracking_events: list[Literal["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "impression", "pause", "resume", "skip", "mute", "unmute"]] | None = Field(None, description="Tracking events supported by this DAAST tag")
|
|
564
|
+
companion_ads: bool | None = Field(None, description="Whether companion display ads are included")
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
class InlineDaastAsset(BaseModel):
|
|
568
|
+
model_config = ConfigDict(extra="forbid")
|
|
569
|
+
|
|
570
|
+
delivery_type: Literal["inline"] = Field(description="Discriminator indicating DAAST is delivered as inline XML content")
|
|
571
|
+
content: str = Field(description="Inline DAAST XML content")
|
|
572
|
+
daast_version: Literal["1.0", "1.1"] | None = Field(None, description="DAAST specification version")
|
|
573
|
+
duration_ms: int | None = Field(None, description="Expected audio duration in milliseconds (if known)")
|
|
574
|
+
tracking_events: list[Literal["start", "firstQuartile", "midpoint", "thirdQuartile", "complete", "impression", "pause", "resume", "skip", "mute", "unmute"]] | None = Field(None, description="Tracking events supported by this DAAST tag")
|
|
575
|
+
companion_ads: bool | None = Field(None, description="Whether companion display ads are included")
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
# Union type for DAAST Asset
|
|
579
|
+
DaastAsset = UrlDaastAsset | InlineDaastAsset
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
# A single rendered piece of a creative preview with discriminated output format
|
|
583
|
+
|
|
584
|
+
class UrlPreviewRender(BaseModel):
|
|
585
|
+
"""URL-only preview format"""
|
|
586
|
+
|
|
587
|
+
model_config = ConfigDict(extra="forbid")
|
|
588
|
+
|
|
589
|
+
render_id: str = Field(description="Unique identifier for this rendered piece within the variant")
|
|
590
|
+
output_format: Literal["url"] = Field(description="Discriminator indicating preview_url is provided")
|
|
591
|
+
preview_url: str = Field(description="URL to an HTML page that renders this piece. Can be embedded in an iframe.")
|
|
592
|
+
role: str = Field(description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles.")
|
|
593
|
+
dimensions: dict[str, Any] | None = Field(None, description="Dimensions for this rendered piece")
|
|
594
|
+
embedding: dict[str, Any] | None = Field(None, description="Optional security and embedding metadata for safe iframe integration")
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
class HtmlPreviewRender(BaseModel):
|
|
598
|
+
"""HTML-only preview format"""
|
|
599
|
+
|
|
600
|
+
model_config = ConfigDict(extra="forbid")
|
|
601
|
+
|
|
602
|
+
render_id: str = Field(description="Unique identifier for this rendered piece within the variant")
|
|
603
|
+
output_format: Literal["html"] = Field(description="Discriminator indicating preview_html is provided")
|
|
604
|
+
preview_html: str = Field(description="Raw HTML for this rendered piece. Can be embedded directly in the page without iframe. Security warning: Only use with trusted creative agents as this bypasses iframe sandboxing.")
|
|
605
|
+
role: str = Field(description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles.")
|
|
606
|
+
dimensions: dict[str, Any] | None = Field(None, description="Dimensions for this rendered piece")
|
|
607
|
+
embedding: dict[str, Any] | None = Field(None, description="Optional security and embedding metadata")
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class BothPreviewRender(BaseModel):
|
|
611
|
+
"""Both URL and HTML preview format"""
|
|
612
|
+
|
|
613
|
+
model_config = ConfigDict(extra="forbid")
|
|
614
|
+
|
|
615
|
+
render_id: str = Field(description="Unique identifier for this rendered piece within the variant")
|
|
616
|
+
output_format: Literal["both"] = Field(description="Discriminator indicating both preview_url and preview_html are provided")
|
|
617
|
+
preview_url: str = Field(description="URL to an HTML page that renders this piece. Can be embedded in an iframe.")
|
|
618
|
+
preview_html: str = Field(description="Raw HTML for this rendered piece. Can be embedded directly in the page without iframe. Security warning: Only use with trusted creative agents as this bypasses iframe sandboxing.")
|
|
619
|
+
role: str = Field(description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles.")
|
|
620
|
+
dimensions: dict[str, Any] | None = Field(None, description="Dimensions for this rendered piece")
|
|
621
|
+
embedding: dict[str, Any] | None = Field(None, description="Optional security and embedding metadata for safe iframe integration")
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
# Union type for Preview Render
|
|
625
|
+
PreviewRender = UrlPreviewRender | HtmlPreviewRender | BothPreviewRender
|
|
626
|
+
|
|
627
|
+
|
|
475
628
|
|
|
476
629
|
# ============================================================================
|
|
477
630
|
# TASK REQUEST/RESPONSE TYPES
|
|
478
631
|
# ============================================================================
|
|
479
632
|
|
|
480
633
|
class ActivateSignalRequest(BaseModel):
|
|
481
|
-
"""Request parameters for activating a signal on a specific
|
|
634
|
+
"""Request parameters for activating a signal on a specific destination"""
|
|
482
635
|
|
|
483
636
|
signal_agent_segment_id: str = Field(description="The universal identifier for the signal to activate")
|
|
484
|
-
|
|
485
|
-
account: str | None = Field(None, description="Account identifier (required for account-specific activation)")
|
|
637
|
+
destinations: list[Destination] = Field(description="Target destination(s) for activation. If the authenticated caller matches one of these destinations, activation keys will be included in the response.")
|
|
486
638
|
|
|
487
639
|
|
|
488
640
|
class BuildCreativeRequest(BaseModel):
|
|
@@ -527,7 +679,7 @@ class GetSignalsRequest(BaseModel):
|
|
|
527
679
|
"""Request parameters for discovering signals based on description"""
|
|
528
680
|
|
|
529
681
|
signal_spec: str = Field(description="Natural language description of the desired signals")
|
|
530
|
-
deliver_to: dict[str, Any] = Field(description="
|
|
682
|
+
deliver_to: dict[str, Any] = Field(description="Destination platforms where signals need to be activated")
|
|
531
683
|
filters: dict[str, Any] | None = Field(None, description="Filters to refine results")
|
|
532
684
|
max_results: int | None = Field(None, description="Maximum number of results to return")
|
|
533
685
|
|
|
@@ -564,6 +716,21 @@ class ListCreativesRequest(BaseModel):
|
|
|
564
716
|
fields: list[Literal["creative_id", "name", "format", "status", "created_date", "updated_date", "tags", "assignments", "performance", "sub_assets"]] | None = Field(None, description="Specific fields to include in response (omit for all fields)")
|
|
565
717
|
|
|
566
718
|
|
|
719
|
+
class PackageRequest(BaseModel):
|
|
720
|
+
"""Package configuration for media buy creation"""
|
|
721
|
+
|
|
722
|
+
buyer_ref: str = Field(description="Buyer's reference identifier for this package")
|
|
723
|
+
product_id: str = Field(description="Product ID for this package")
|
|
724
|
+
format_ids: list[FormatId] | None = Field(None, description="Array of format IDs that will be used for this package - must be supported by the product. If omitted, defaults to all formats supported by the product.")
|
|
725
|
+
budget: float = Field(description="Budget allocation for this package in the media buy's currency")
|
|
726
|
+
pacing: Pacing | None = None
|
|
727
|
+
pricing_option_id: str = Field(description="ID of the selected pricing option from the product's pricing_options array")
|
|
728
|
+
bid_price: float | None = Field(None, description="Bid price for auction-based CPM pricing (required if using cpm-auction-option)")
|
|
729
|
+
targeting_overlay: Targeting | None = None
|
|
730
|
+
creative_ids: list[str] | None = Field(None, description="Creative IDs to assign to this package at creation time (references existing library creatives)")
|
|
731
|
+
creatives: list[CreativeAsset] | None = Field(None, description="Full creative objects to upload and assign to this package at creation time (alternative to creative_ids - creatives will be added to library). Supports both static and generative creatives.")
|
|
732
|
+
|
|
733
|
+
|
|
567
734
|
class ProvidePerformanceFeedbackRequest(BaseModel):
|
|
568
735
|
"""Request payload for provide_performance_feedback task"""
|
|
569
736
|
|
|
@@ -588,6 +755,22 @@ class SyncCreativesRequest(BaseModel):
|
|
|
588
755
|
push_notification_config: PushNotificationConfig | None = Field(None, description="Optional webhook configuration for async sync notifications. Publisher will send webhook when sync completes if operation takes longer than immediate response time (typically for large bulk operations or manual approval/HITL).")
|
|
589
756
|
|
|
590
757
|
|
|
758
|
+
class TasksGetRequest(BaseModel):
|
|
759
|
+
"""Request parameters for retrieving a specific task by ID with optional conversation history across all AdCP domains"""
|
|
760
|
+
|
|
761
|
+
task_id: str = Field(description="Unique identifier of the task to retrieve")
|
|
762
|
+
include_history: bool | None = Field(None, description="Include full conversation history for this task (may increase response size)")
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
class TasksListRequest(BaseModel):
|
|
766
|
+
"""Request parameters for listing and filtering async tasks across all AdCP domains with state reconciliation capabilities"""
|
|
767
|
+
|
|
768
|
+
filters: dict[str, Any] | None = Field(None, description="Filter criteria for querying tasks")
|
|
769
|
+
sort: dict[str, Any] | None = Field(None, description="Sorting parameters")
|
|
770
|
+
pagination: dict[str, Any] | None = Field(None, description="Pagination parameters")
|
|
771
|
+
include_history: bool | None = Field(None, description="Include full conversation history for each task (may significantly increase response size)")
|
|
772
|
+
|
|
773
|
+
|
|
591
774
|
class UpdateMediaBuyRequest(BaseModel):
|
|
592
775
|
"""Request parameters for updating campaign and package settings"""
|
|
593
776
|
|
|
@@ -600,30 +783,26 @@ class UpdateMediaBuyRequest(BaseModel):
|
|
|
600
783
|
push_notification_config: PushNotificationConfig | None = Field(None, description="Optional webhook configuration for async update notifications. Publisher will send webhook when update completes if operation takes longer than immediate response time.")
|
|
601
784
|
|
|
602
785
|
|
|
603
|
-
|
|
604
|
-
"""Response payload for activate_signal task"""
|
|
786
|
+
# Response containing the transformed or generated creative manifest, ready for use with preview_creative or sync_creatives. Returns either the complete creative manifest OR error information, never both.
|
|
605
787
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
deployed_at: str | None = Field(None, description="Timestamp when activation completed (optional)")
|
|
609
|
-
errors: list[Error] | None = Field(None, description="Task-specific errors and warnings (e.g., activation failures, platform issues)")
|
|
788
|
+
class BuildCreativeResponseVariant1(BaseModel):
|
|
789
|
+
"""Success response - creative manifest generated successfully"""
|
|
610
790
|
|
|
611
|
-
|
|
612
|
-
class BuildCreativeResponse(BaseModel):
|
|
613
|
-
"""Response containing the transformed or generated creative manifest, ready for use with preview_creative or sync_creatives"""
|
|
791
|
+
model_config = ConfigDict(extra="forbid")
|
|
614
792
|
|
|
615
793
|
creative_manifest: CreativeManifest = Field(description="The generated or transformed creative manifest")
|
|
616
|
-
errors: list[Error] | None = Field(None, description="Task-specific errors and warnings")
|
|
617
794
|
|
|
618
795
|
|
|
619
|
-
class
|
|
620
|
-
"""
|
|
796
|
+
class BuildCreativeResponseVariant2(BaseModel):
|
|
797
|
+
"""Error response - creative generation failed"""
|
|
798
|
+
|
|
799
|
+
model_config = ConfigDict(extra="forbid")
|
|
800
|
+
|
|
801
|
+
errors: list[Error] = Field(description="Array of errors explaining why creative generation failed")
|
|
621
802
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
packages: list[dict[str, Any]] | None = Field(None, description="Array of created packages")
|
|
626
|
-
errors: list[Error] | None = Field(None, description="Task-specific errors and warnings (e.g., partial package creation failures)")
|
|
803
|
+
|
|
804
|
+
# Union type for Build Creative Response
|
|
805
|
+
BuildCreativeResponse = BuildCreativeResponseVariant1 | BuildCreativeResponseVariant2
|
|
627
806
|
|
|
628
807
|
|
|
629
808
|
class GetMediaBuyDeliveryResponse(BaseModel):
|
|
@@ -685,28 +864,50 @@ class ListCreativesResponse(BaseModel):
|
|
|
685
864
|
status_summary: dict[str, Any] | None = Field(None, description="Breakdown of creatives by status")
|
|
686
865
|
|
|
687
866
|
|
|
688
|
-
|
|
689
|
-
|
|
867
|
+
# Response payload for provide_performance_feedback task. Returns either success confirmation OR error information, never both.
|
|
868
|
+
|
|
869
|
+
class ProvidePerformanceFeedbackResponseVariant1(BaseModel):
|
|
870
|
+
"""Success response - feedback received and processed"""
|
|
871
|
+
|
|
872
|
+
model_config = ConfigDict(extra="forbid")
|
|
873
|
+
|
|
874
|
+
success: Literal[True] = Field(description="Whether the performance feedback was successfully received")
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
class ProvidePerformanceFeedbackResponseVariant2(BaseModel):
|
|
878
|
+
"""Error response - feedback rejected or could not be processed"""
|
|
879
|
+
|
|
880
|
+
model_config = ConfigDict(extra="forbid")
|
|
690
881
|
|
|
691
|
-
|
|
692
|
-
errors: list[Error] | None = Field(None, description="Task-specific errors and warnings (e.g., invalid measurement period, missing campaign data)")
|
|
882
|
+
errors: list[Error] = Field(description="Array of errors explaining why feedback was rejected (e.g., invalid measurement period, missing campaign data)")
|
|
693
883
|
|
|
694
884
|
|
|
695
|
-
|
|
696
|
-
|
|
885
|
+
# Union type for Provide Performance Feedback Response
|
|
886
|
+
ProvidePerformanceFeedbackResponse = ProvidePerformanceFeedbackResponseVariant1 | ProvidePerformanceFeedbackResponseVariant2
|
|
697
887
|
|
|
698
|
-
dry_run: bool | None = Field(None, description="Whether this was a dry run (no actual changes made)")
|
|
699
|
-
creatives: list[dict[str, Any]] = Field(description="Results for each creative processed")
|
|
700
888
|
|
|
889
|
+
class TasksGetResponse(BaseModel):
|
|
890
|
+
"""Response containing detailed information about a specific task including status and optional conversation history across all AdCP domains"""
|
|
701
891
|
|
|
702
|
-
|
|
703
|
-
"
|
|
892
|
+
task_id: str = Field(description="Unique identifier for this task")
|
|
893
|
+
task_type: TaskType = Field(description="Type of AdCP operation")
|
|
894
|
+
domain: Literal["media-buy", "signals"] = Field(description="AdCP domain this task belongs to")
|
|
895
|
+
status: TaskStatus = Field(description="Current task status")
|
|
896
|
+
created_at: str = Field(description="When the task was initially created (ISO 8601)")
|
|
897
|
+
updated_at: str = Field(description="When the task was last updated (ISO 8601)")
|
|
898
|
+
completed_at: str | None = Field(None, description="When the task completed (ISO 8601, only for completed/failed/canceled tasks)")
|
|
899
|
+
has_webhook: bool | None = Field(None, description="Whether this task has webhook configuration")
|
|
900
|
+
progress: dict[str, Any] | None = Field(None, description="Progress information for long-running tasks")
|
|
901
|
+
error: dict[str, Any] | None = Field(None, description="Error details for failed tasks")
|
|
902
|
+
history: list[dict[str, Any]] | None = Field(None, description="Complete conversation history for this task (only included if include_history was true in request)")
|
|
704
903
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
904
|
+
|
|
905
|
+
class TasksListResponse(BaseModel):
|
|
906
|
+
"""Response from task listing query with filtered results and state reconciliation data across all AdCP domains"""
|
|
907
|
+
|
|
908
|
+
query_summary: dict[str, Any] = Field(description="Summary of the query that was executed")
|
|
909
|
+
tasks: list[dict[str, Any]] = Field(description="Array of tasks matching the query criteria")
|
|
910
|
+
pagination: dict[str, Any] = Field(description="Pagination information")
|
|
710
911
|
|
|
711
912
|
|
|
712
913
|
|
|
@@ -761,3 +962,211 @@ class PreviewCreativeResponse(BaseModel):
|
|
|
761
962
|
|
|
762
963
|
# Batch mode field
|
|
763
964
|
results: list[dict[str, Any]] | None = Field(default=None, description="Array of preview results for batch processing")
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
# ============================================================================
|
|
968
|
+
# ONEOF DISCRIMINATED UNIONS FOR RESPONSE TYPES
|
|
969
|
+
# ============================================================================
|
|
970
|
+
# These response types use oneOf semantics: success XOR error, never both.
|
|
971
|
+
# Implemented as Union types with distinct Success/Error variants.
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class ActivateSignalSuccess(BaseModel):
|
|
975
|
+
"""Successful signal activation response"""
|
|
976
|
+
|
|
977
|
+
decisioning_platform_segment_id: str = Field(
|
|
978
|
+
description="The platform-specific ID to use once activated"
|
|
979
|
+
)
|
|
980
|
+
estimated_activation_duration_minutes: float | None = None
|
|
981
|
+
deployed_at: str | None = None
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
class ActivateSignalError(BaseModel):
|
|
985
|
+
"""Failed signal activation response"""
|
|
986
|
+
|
|
987
|
+
errors: list[Error] = Field(description="Task-specific errors and warnings")
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
# Override the generated ActivateSignalResponse type alias
|
|
991
|
+
ActivateSignalResponse = ActivateSignalSuccess | ActivateSignalError
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
class CreateMediaBuySuccess(BaseModel):
|
|
995
|
+
"""Successful media buy creation response"""
|
|
996
|
+
|
|
997
|
+
media_buy_id: str = Field(description="The unique ID for the media buy")
|
|
998
|
+
buyer_ref: str = Field(description="The buyer's reference ID for this media buy")
|
|
999
|
+
packages: list[Package] = Field(
|
|
1000
|
+
description="Array of approved packages. Each package is ready for creative assignment."
|
|
1001
|
+
)
|
|
1002
|
+
creative_deadline: str | None = Field(
|
|
1003
|
+
None,
|
|
1004
|
+
description="ISO 8601 date when creatives must be provided for launch",
|
|
1005
|
+
)
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
class CreateMediaBuyError(BaseModel):
|
|
1009
|
+
"""Failed media buy creation response"""
|
|
1010
|
+
|
|
1011
|
+
errors: list[Error] = Field(description="Task-specific errors and warnings")
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
# Override the generated CreateMediaBuyResponse type alias
|
|
1015
|
+
CreateMediaBuyResponse = CreateMediaBuySuccess | CreateMediaBuyError
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
class UpdateMediaBuySuccess(BaseModel):
|
|
1019
|
+
"""Successful media buy update response"""
|
|
1020
|
+
|
|
1021
|
+
media_buy_id: str = Field(description="The unique ID for the media buy")
|
|
1022
|
+
buyer_ref: str = Field(description="The buyer's reference ID for this media buy")
|
|
1023
|
+
packages: list[Package] = Field(
|
|
1024
|
+
description="Array of updated packages reflecting the changes"
|
|
1025
|
+
)
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
class UpdateMediaBuyError(BaseModel):
|
|
1029
|
+
"""Failed media buy update response"""
|
|
1030
|
+
|
|
1031
|
+
errors: list[Error] = Field(description="Task-specific errors and warnings")
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
# Override the generated UpdateMediaBuyResponse type alias
|
|
1035
|
+
UpdateMediaBuyResponse = UpdateMediaBuySuccess | UpdateMediaBuyError
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
class SyncCreativesSuccess(BaseModel):
|
|
1039
|
+
"""Successful creative sync response"""
|
|
1040
|
+
|
|
1041
|
+
assignments: list[CreativeAssignment] = Field(
|
|
1042
|
+
description="Array of creative assignments with updated status"
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
class SyncCreativesError(BaseModel):
|
|
1047
|
+
"""Failed creative sync response"""
|
|
1048
|
+
|
|
1049
|
+
errors: list[Error] = Field(description="Task-specific errors and warnings")
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
# Override the generated SyncCreativesResponse type alias
|
|
1053
|
+
SyncCreativesResponse = SyncCreativesSuccess | SyncCreativesError
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
# Explicit exports for module interface
|
|
1057
|
+
__all__ = [
|
|
1058
|
+
"ActivateSignalError",
|
|
1059
|
+
"ActivateSignalRequest",
|
|
1060
|
+
"ActivateSignalResponse",
|
|
1061
|
+
"ActivateSignalSuccess",
|
|
1062
|
+
"ActivationKey",
|
|
1063
|
+
"AgentDeployment",
|
|
1064
|
+
"AgentDestination",
|
|
1065
|
+
"BothPreviewRender",
|
|
1066
|
+
"BrandManifest",
|
|
1067
|
+
"BrandManifestRef",
|
|
1068
|
+
"BrandManifestRefVariant1",
|
|
1069
|
+
"BrandManifestRefVariant2",
|
|
1070
|
+
"BuildCreativeRequest",
|
|
1071
|
+
"BuildCreativeResponse",
|
|
1072
|
+
"BuildCreativeResponseVariant1",
|
|
1073
|
+
"BuildCreativeResponseVariant2",
|
|
1074
|
+
"Channels",
|
|
1075
|
+
"CreateMediaBuyError",
|
|
1076
|
+
"CreateMediaBuyRequest",
|
|
1077
|
+
"CreateMediaBuyResponse",
|
|
1078
|
+
"CreateMediaBuySuccess",
|
|
1079
|
+
"CreativeAsset",
|
|
1080
|
+
"CreativeAssignment",
|
|
1081
|
+
"CreativeManifest",
|
|
1082
|
+
"CreativePolicy",
|
|
1083
|
+
"DaastAsset",
|
|
1084
|
+
"DeliveryMetrics",
|
|
1085
|
+
"DeliveryType",
|
|
1086
|
+
"Deployment",
|
|
1087
|
+
"Destination",
|
|
1088
|
+
"Error",
|
|
1089
|
+
"Format",
|
|
1090
|
+
"FormatId",
|
|
1091
|
+
"FrequencyCap",
|
|
1092
|
+
"GetMediaBuyDeliveryRequest",
|
|
1093
|
+
"GetMediaBuyDeliveryResponse",
|
|
1094
|
+
"GetProductsRequest",
|
|
1095
|
+
"GetProductsResponse",
|
|
1096
|
+
"GetSignalsRequest",
|
|
1097
|
+
"GetSignalsResponse",
|
|
1098
|
+
"HtmlPreviewRender",
|
|
1099
|
+
"InlineDaastAsset",
|
|
1100
|
+
"InlineVastAsset",
|
|
1101
|
+
"Key_valueActivationKey",
|
|
1102
|
+
"ListAuthorizedPropertiesRequest",
|
|
1103
|
+
"ListAuthorizedPropertiesResponse",
|
|
1104
|
+
"ListCreativeFormatsRequest",
|
|
1105
|
+
"ListCreativeFormatsResponse",
|
|
1106
|
+
"ListCreativesRequest",
|
|
1107
|
+
"ListCreativesResponse",
|
|
1108
|
+
"Measurement",
|
|
1109
|
+
"MediaBuy",
|
|
1110
|
+
"MediaBuyStatus",
|
|
1111
|
+
"MediaSubAsset",
|
|
1112
|
+
"Pacing",
|
|
1113
|
+
"Package",
|
|
1114
|
+
"PackageRequest",
|
|
1115
|
+
"PackageStatus",
|
|
1116
|
+
"PerformanceFeedback",
|
|
1117
|
+
"Placement",
|
|
1118
|
+
"PlatformDeployment",
|
|
1119
|
+
"PlatformDestination",
|
|
1120
|
+
"PreviewCreativeRequest",
|
|
1121
|
+
"PreviewCreativeResponse",
|
|
1122
|
+
"PreviewRender",
|
|
1123
|
+
"PricingModel",
|
|
1124
|
+
"PricingOption",
|
|
1125
|
+
"PricingOptionVariant1",
|
|
1126
|
+
"PricingOptionVariant2",
|
|
1127
|
+
"PricingOptionVariant3",
|
|
1128
|
+
"PricingOptionVariant4",
|
|
1129
|
+
"PricingOptionVariant5",
|
|
1130
|
+
"PricingOptionVariant6",
|
|
1131
|
+
"PricingOptionVariant7",
|
|
1132
|
+
"PricingOptionVariant8",
|
|
1133
|
+
"PricingOptionVariant9",
|
|
1134
|
+
"Product",
|
|
1135
|
+
"PromotedProducts",
|
|
1136
|
+
"Property",
|
|
1137
|
+
"ProtocolEnvelope",
|
|
1138
|
+
"ProvidePerformanceFeedbackRequest",
|
|
1139
|
+
"ProvidePerformanceFeedbackResponse",
|
|
1140
|
+
"ProvidePerformanceFeedbackResponseVariant1",
|
|
1141
|
+
"ProvidePerformanceFeedbackResponseVariant2",
|
|
1142
|
+
"PushNotificationConfig",
|
|
1143
|
+
"ReportingCapabilities",
|
|
1144
|
+
"Response",
|
|
1145
|
+
"Segment_idActivationKey",
|
|
1146
|
+
"StandardFormatIds",
|
|
1147
|
+
"StartTiming",
|
|
1148
|
+
"StartTimingVariant1",
|
|
1149
|
+
"StartTimingVariant2",
|
|
1150
|
+
"SubAsset",
|
|
1151
|
+
"SyncCreativesError",
|
|
1152
|
+
"SyncCreativesRequest",
|
|
1153
|
+
"SyncCreativesResponse",
|
|
1154
|
+
"SyncCreativesSuccess",
|
|
1155
|
+
"Targeting",
|
|
1156
|
+
"TaskStatus",
|
|
1157
|
+
"TaskType",
|
|
1158
|
+
"TasksGetRequest",
|
|
1159
|
+
"TasksGetResponse",
|
|
1160
|
+
"TasksListRequest",
|
|
1161
|
+
"TasksListResponse",
|
|
1162
|
+
"TextSubAsset",
|
|
1163
|
+
"UpdateMediaBuyError",
|
|
1164
|
+
"UpdateMediaBuyRequest",
|
|
1165
|
+
"UpdateMediaBuyResponse",
|
|
1166
|
+
"UpdateMediaBuySuccess",
|
|
1167
|
+
"UrlDaastAsset",
|
|
1168
|
+
"UrlPreviewRender",
|
|
1169
|
+
"UrlVastAsset",
|
|
1170
|
+
"VastAsset",
|
|
1171
|
+
"WebhookPayload",
|
|
1172
|
+
]
|
adcp/utils/response_parser.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Any, TypeVar
|
|
7
|
+
from typing import Any, TypeVar, Union, cast, get_args, get_origin
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, ValidationError
|
|
10
10
|
|
|
@@ -13,6 +13,56 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
T = TypeVar("T", bound=BaseModel)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
def _validate_union_type(data: dict[str, Any], response_type: type[T]) -> T:
|
|
17
|
+
"""
|
|
18
|
+
Validate data against a Union type by trying each variant.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
data: Data to validate
|
|
22
|
+
response_type: Union type to validate against
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Validated model instance
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
ValidationError: If data doesn't match any Union variant
|
|
29
|
+
"""
|
|
30
|
+
# Check if this is a Union type (handles both typing.Union and types.UnionType)
|
|
31
|
+
origin = get_origin(response_type)
|
|
32
|
+
|
|
33
|
+
# In Python 3.10+, X | Y creates a types.UnionType, not typing.Union
|
|
34
|
+
# We need to check both the origin and the type itself
|
|
35
|
+
is_union = origin is Union or str(type(response_type).__name__) == "UnionType"
|
|
36
|
+
|
|
37
|
+
if is_union:
|
|
38
|
+
# Get union args - works for both typing.Union and types.UnionType
|
|
39
|
+
args = get_args(response_type)
|
|
40
|
+
if not args: # types.UnionType case
|
|
41
|
+
# For types.UnionType, we need to access __args__ directly
|
|
42
|
+
args = getattr(response_type, "__args__", ())
|
|
43
|
+
|
|
44
|
+
errors = []
|
|
45
|
+
for variant in args:
|
|
46
|
+
try:
|
|
47
|
+
return cast(T, variant.model_validate(data))
|
|
48
|
+
except ValidationError as e:
|
|
49
|
+
errors.append((variant.__name__, e))
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
# If we get here, none of the variants worked
|
|
53
|
+
error_msgs = [f"{name}: {str(e)}" for name, e in errors]
|
|
54
|
+
# Raise a ValueError instead of ValidationError for better error messages
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"Data doesn't match any Union variant. "
|
|
57
|
+
f"Attempted variants: {', '.join([e[0] for e in errors])}. "
|
|
58
|
+
f"Errors: {'; '.join(error_msgs)}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Not a Union type, use regular validation
|
|
62
|
+
# Cast is needed because response_type is typed as type[T] | Any
|
|
63
|
+
return cast(T, response_type.model_validate(data)) # type: ignore[redundant-cast]
|
|
64
|
+
|
|
65
|
+
|
|
16
66
|
def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) -> T:
|
|
17
67
|
"""
|
|
18
68
|
Parse MCP content array into structured response type.
|
|
@@ -48,8 +98,8 @@ def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) ->
|
|
|
48
98
|
try:
|
|
49
99
|
# Try parsing as JSON
|
|
50
100
|
data = json.loads(text)
|
|
51
|
-
# Validate against expected schema
|
|
52
|
-
return
|
|
101
|
+
# Validate against expected schema (handles Union types)
|
|
102
|
+
return _validate_union_type(data, response_type)
|
|
53
103
|
except json.JSONDecodeError:
|
|
54
104
|
# Not JSON, try next item
|
|
55
105
|
continue
|
|
@@ -61,7 +111,7 @@ def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) ->
|
|
|
61
111
|
elif item.get("type") == "resource":
|
|
62
112
|
# Resource content might have structured data
|
|
63
113
|
try:
|
|
64
|
-
return
|
|
114
|
+
return _validate_union_type(item, response_type)
|
|
65
115
|
except ValidationError:
|
|
66
116
|
# Try next item
|
|
67
117
|
continue
|
|
@@ -98,25 +148,24 @@ def parse_json_or_text(data: Any, response_type: type[T]) -> T:
|
|
|
98
148
|
# If already a dict, try direct validation
|
|
99
149
|
if isinstance(data, dict):
|
|
100
150
|
try:
|
|
101
|
-
return
|
|
151
|
+
return _validate_union_type(data, response_type)
|
|
102
152
|
except ValidationError as e:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
) from e
|
|
153
|
+
# Get the type name, handling Union types
|
|
154
|
+
type_name = getattr(response_type, "__name__", str(response_type))
|
|
155
|
+
raise ValueError(f"Response doesn't match expected schema {type_name}: {e}") from e
|
|
106
156
|
|
|
107
157
|
# If string, try JSON parsing
|
|
108
158
|
if isinstance(data, str):
|
|
109
159
|
try:
|
|
110
160
|
parsed = json.loads(data)
|
|
111
|
-
return
|
|
161
|
+
return _validate_union_type(parsed, response_type)
|
|
112
162
|
except json.JSONDecodeError as e:
|
|
113
163
|
raise ValueError(f"Response is not valid JSON: {e}") from e
|
|
114
164
|
except ValidationError as e:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) from e
|
|
165
|
+
# Get the type name, handling Union types
|
|
166
|
+
type_name = getattr(response_type, "__name__", str(response_type))
|
|
167
|
+
raise ValueError(f"Response doesn't match expected schema {type_name}: {e}") from e
|
|
118
168
|
|
|
119
169
|
# Unsupported type
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
170
|
+
type_name = getattr(response_type, "__name__", str(response_type))
|
|
171
|
+
raise ValueError(f"Cannot parse response of type {type(data).__name__} into {type_name}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adcp
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Official Python client for the Ad Context Protocol (AdCP)
|
|
5
5
|
Author-email: AdCP Community <maintainers@adcontextprotocol.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -59,6 +59,8 @@ AdCP operations are **distributed and asynchronous by default**. An agent might:
|
|
|
59
59
|
pip install adcp
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
> **Note**: This client requires Python 3.10 or later and supports both synchronous and asynchronous workflows.
|
|
63
|
+
|
|
62
64
|
## Quick Start: Distributed Operations
|
|
63
65
|
|
|
64
66
|
```python
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
adcp/__init__.py,sha256=
|
|
1
|
+
adcp/__init__.py,sha256=3UpoB71XItkSZ_r1QuzE2GZIQsgVjt50ZawxQUO2-qs,6232
|
|
2
2
|
adcp/__main__.py,sha256=Avy_C71rruh2lOuojvuXDj09tkFOaek74nJ-dbx25Sw,12838
|
|
3
|
-
adcp/client.py,sha256=
|
|
3
|
+
adcp/client.py,sha256=co4tKdkjDrqZltCUPcYwVRr8LQfbBbG-DLjapLjcVuo,28246
|
|
4
4
|
adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
|
|
5
5
|
adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
|
|
6
6
|
adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
|
|
7
7
|
adcp/protocols/a2a.py,sha256=FHgc6G_eU2qD0vH7_RyS1eZvUFSb2j3-EsceoHPi384,12467
|
|
8
|
-
adcp/protocols/base.py,sha256=
|
|
8
|
+
adcp/protocols/base.py,sha256=vBHD23Fzl_CCk_Gy9nvSbBYopcJlYkYyzoz-rhI8wHg,5214
|
|
9
9
|
adcp/protocols/mcp.py,sha256=eIk8snCinZm-ZjdarGVMt5nEYJ4_8POM9Fa5Mkw7xxU,15902
|
|
10
10
|
adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
|
|
11
11
|
adcp/types/core.py,sha256=RXkKCWCXS9BVJTNpe3Opm5O1I_LaQPMUuVwa-ipvS1Q,4839
|
|
12
|
-
adcp/types/generated.py,sha256=
|
|
12
|
+
adcp/types/generated.py,sha256=Ig4ucbJzKRuHlwYzsqvMF9M3w2KghhQQqsXuOnBqVMM,74993
|
|
13
13
|
adcp/types/tasks.py,sha256=Ae9TSwG2F7oWXTcl4TvLhAzinbQkHNGF1Pc0q8RMNNM,23424
|
|
14
14
|
adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
|
|
15
15
|
adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
|
|
16
16
|
adcp/utils/preview_cache.py,sha256=8_2qs5CgrHv1_WOnD4bs43VWueu-rcZRu5PZMQ_lyuE,17573
|
|
17
|
-
adcp/utils/response_parser.py,sha256=
|
|
18
|
-
adcp-1.2.
|
|
19
|
-
adcp-1.2.
|
|
20
|
-
adcp-1.2.
|
|
21
|
-
adcp-1.2.
|
|
22
|
-
adcp-1.2.
|
|
23
|
-
adcp-1.2.
|
|
17
|
+
adcp/utils/response_parser.py,sha256=uPk2vIH-RYZmq7y3i8lC4HTMQ3FfKdlgXKTjgJ1955M,6253
|
|
18
|
+
adcp-1.2.1.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
|
|
19
|
+
adcp-1.2.1.dist-info/METADATA,sha256=mDI2lRdXxOluUZ9vgRE3fyoGSnj8DQ8nqBU30Tuphjg,14568
|
|
20
|
+
adcp-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
21
|
+
adcp-1.2.1.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
|
|
22
|
+
adcp-1.2.1.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
|
|
23
|
+
adcp-1.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|