adcp 2.12.2__py3-none-any.whl → 2.14.0__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.
Files changed (62) hide show
  1. adcp/__init__.py +14 -1
  2. adcp/adagents.py +53 -9
  3. adcp/client.py +361 -57
  4. adcp/protocols/mcp.py +1 -3
  5. adcp/types/__init__.py +9 -33
  6. adcp/types/_generated.py +82 -13
  7. adcp/types/aliases.py +23 -0
  8. adcp/types/base.py +193 -0
  9. adcp/types/generated_poc/adagents.py +9 -13
  10. adcp/types/generated_poc/core/activation_key.py +12 -2
  11. adcp/types/generated_poc/core/assets/daast_asset.py +12 -2
  12. adcp/types/generated_poc/core/assets/image_asset.py +9 -5
  13. adcp/types/generated_poc/core/assets/vast_asset.py +12 -2
  14. adcp/types/generated_poc/core/assets/video_asset.py +9 -5
  15. adcp/types/generated_poc/core/async_response_data.py +72 -0
  16. adcp/types/generated_poc/core/brand_manifest_ref.py +35 -0
  17. adcp/types/generated_poc/core/creative_asset.py +4 -6
  18. adcp/types/generated_poc/core/creative_manifest.py +4 -6
  19. adcp/types/generated_poc/core/deployment.py +16 -8
  20. adcp/types/generated_poc/core/destination.py +12 -2
  21. adcp/types/generated_poc/core/format.py +3 -3
  22. adcp/types/generated_poc/core/{webhook_payload.py → mcp_webhook_payload.py} +8 -33
  23. adcp/types/generated_poc/core/pricing_option.py +51 -0
  24. adcp/types/generated_poc/core/product.py +4 -29
  25. adcp/types/generated_poc/core/promoted_offerings.py +5 -21
  26. adcp/types/generated_poc/core/property.py +4 -6
  27. adcp/types/generated_poc/core/publisher_property_selector.py +15 -2
  28. adcp/types/generated_poc/core/push_notification_config.py +1 -4
  29. adcp/types/generated_poc/core/start_timing.py +18 -0
  30. adcp/types/generated_poc/core/sub_asset.py +12 -2
  31. adcp/types/generated_poc/creative/preview_creative_response.py +3 -14
  32. adcp/types/generated_poc/creative/preview_render.py +3 -11
  33. adcp/types/generated_poc/media_buy/create_media_buy_async_response_input_required.py +37 -0
  34. adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py +19 -0
  35. adcp/types/generated_poc/media_buy/create_media_buy_async_response_working.py +31 -0
  36. adcp/types/generated_poc/media_buy/create_media_buy_request.py +7 -26
  37. adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +4 -2
  38. adcp/types/generated_poc/media_buy/get_products_async_response_input_required.py +38 -0
  39. adcp/types/generated_poc/media_buy/get_products_async_response_submitted.py +24 -0
  40. adcp/types/generated_poc/media_buy/get_products_async_response_working.py +35 -0
  41. adcp/types/generated_poc/media_buy/get_products_request.py +5 -20
  42. adcp/types/generated_poc/media_buy/list_creatives_response.py +5 -7
  43. adcp/types/generated_poc/media_buy/sync_creatives_async_response_input_required.py +31 -0
  44. adcp/types/generated_poc/media_buy/sync_creatives_async_response_submitted.py +19 -0
  45. adcp/types/generated_poc/media_buy/sync_creatives_async_response_working.py +37 -0
  46. adcp/types/generated_poc/media_buy/update_media_buy_async_response_input_required.py +30 -0
  47. adcp/types/generated_poc/media_buy/update_media_buy_async_response_submitted.py +19 -0
  48. adcp/types/generated_poc/media_buy/update_media_buy_async_response_working.py +31 -0
  49. adcp/types/generated_poc/media_buy/update_media_buy_request.py +4 -14
  50. adcp/types/generated_poc/signals/activate_signal_request.py +2 -2
  51. adcp/types/generated_poc/signals/activate_signal_response.py +2 -2
  52. adcp/types/generated_poc/signals/get_signals_request.py +2 -2
  53. adcp/types/generated_poc/signals/get_signals_response.py +2 -3
  54. adcp/utils/preview_cache.py +6 -4
  55. adcp/webhooks.py +508 -0
  56. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/METADATA +2 -2
  57. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/RECORD +61 -45
  58. adcp/types/generated_poc/core/dimensions.py +0 -18
  59. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/WHEEL +0 -0
  60. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/entry_points.txt +0 -0
  61. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/licenses/LICENSE +0 -0
  62. {adcp-2.12.2.dist-info → adcp-2.14.0.dist-info}/top_level.txt +0 -0
adcp/types/base.py CHANGED
@@ -2,10 +2,189 @@ from __future__ import annotations
2
2
 
3
3
  """Base model for AdCP types with spec-compliant serialization."""
4
4
 
5
+ from collections.abc import Callable
5
6
  from typing import Any
6
7
 
7
8
  from pydantic import BaseModel
8
9
 
10
+ # Type alias to shorten long type annotations
11
+ MessageFormatter = Callable[[Any], str]
12
+
13
+
14
+ def _pluralize(count: int, singular: str, plural: str | None = None) -> str:
15
+ """Return singular or plural form based on count."""
16
+ if count == 1:
17
+ return singular
18
+ return plural if plural else f"{singular}s"
19
+
20
+
21
+ # Registry of human-readable message formatters for response types.
22
+ # Key is the class name, value is a callable that takes the instance and returns a message.
23
+ _RESPONSE_MESSAGE_REGISTRY: dict[str, MessageFormatter] = {}
24
+
25
+
26
+ def _register_response_message(cls_name: str) -> Callable[[MessageFormatter], MessageFormatter]:
27
+ """Decorator to register a message formatter for a response type."""
28
+
29
+ def decorator(func: MessageFormatter) -> MessageFormatter:
30
+ _RESPONSE_MESSAGE_REGISTRY[cls_name] = func
31
+ return func
32
+
33
+ return decorator
34
+
35
+
36
+ # Response message formatters
37
+ @_register_response_message("GetProductsResponse")
38
+ def _get_products_message(self: Any) -> str:
39
+ products = getattr(self, "products", None)
40
+ if products is None or len(products) == 0:
41
+ return "No products matched your requirements."
42
+ count = len(products)
43
+ return f"Found {count} {_pluralize(count, 'product')} matching your requirements."
44
+
45
+
46
+ @_register_response_message("ListCreativeFormatsResponse")
47
+ def _list_creative_formats_message(self: Any) -> str:
48
+ formats = getattr(self, "formats", None)
49
+ if formats is None:
50
+ return "No creative formats found."
51
+ count = len(formats)
52
+ return f"Found {count} supported creative {_pluralize(count, 'format')}."
53
+
54
+
55
+ @_register_response_message("GetSignalsResponse")
56
+ def _get_signals_message(self: Any) -> str:
57
+ signals = getattr(self, "signals", None)
58
+ if signals is None:
59
+ return "No signals found."
60
+ count = len(signals)
61
+ return f"Found {count} {_pluralize(count, 'signal')} available for targeting."
62
+
63
+
64
+ @_register_response_message("ListAuthorizedPropertiesResponse")
65
+ def _list_authorized_properties_message(self: Any) -> str:
66
+ domains = getattr(self, "publisher_domains", None)
67
+ if domains is None:
68
+ return "No authorized properties found."
69
+ count = len(domains)
70
+ return f"Authorized to represent {count} publisher {_pluralize(count, 'domain')}."
71
+
72
+
73
+ @_register_response_message("ListCreativesResponse")
74
+ def _list_creatives_message(self: Any) -> str:
75
+ creatives = getattr(self, "creatives", None)
76
+ if creatives is None:
77
+ return "No creatives found."
78
+ count = len(creatives)
79
+ return f"Found {count} {_pluralize(count, 'creative')} in the system."
80
+
81
+
82
+ @_register_response_message("CreateMediaBuyResponse1")
83
+ def _create_media_buy_success_message(self: Any) -> str:
84
+ media_buy_id = getattr(self, "media_buy_id", None)
85
+ packages = getattr(self, "packages", None)
86
+ package_count = len(packages) if packages else 0
87
+ return (
88
+ f"Media buy {media_buy_id} created with "
89
+ f"{package_count} {_pluralize(package_count, 'package')}."
90
+ )
91
+
92
+
93
+ @_register_response_message("CreateMediaBuyResponse2")
94
+ def _create_media_buy_error_message(self: Any) -> str:
95
+ errors = getattr(self, "errors", None)
96
+ error_count = len(errors) if errors else 0
97
+ return f"Media buy creation failed with {error_count} {_pluralize(error_count, 'error')}."
98
+
99
+
100
+ @_register_response_message("UpdateMediaBuyResponse1")
101
+ def _update_media_buy_success_message(self: Any) -> str:
102
+ media_buy_id = getattr(self, "media_buy_id", None)
103
+ return f"Media buy {media_buy_id} updated successfully."
104
+
105
+
106
+ @_register_response_message("UpdateMediaBuyResponse2")
107
+ def _update_media_buy_error_message(self: Any) -> str:
108
+ errors = getattr(self, "errors", None)
109
+ error_count = len(errors) if errors else 0
110
+ return f"Media buy update failed with {error_count} {_pluralize(error_count, 'error')}."
111
+
112
+
113
+ @_register_response_message("SyncCreativesResponse1")
114
+ def _sync_creatives_success_message(self: Any) -> str:
115
+ creatives = getattr(self, "creatives", None)
116
+ creative_count = len(creatives) if creatives else 0
117
+ return f"Synced {creative_count} {_pluralize(creative_count, 'creative')} successfully."
118
+
119
+
120
+ @_register_response_message("SyncCreativesResponse2")
121
+ def _sync_creatives_error_message(self: Any) -> str:
122
+ errors = getattr(self, "errors", None)
123
+ error_count = len(errors) if errors else 0
124
+ return f"Creative sync failed with {error_count} {_pluralize(error_count, 'error')}."
125
+
126
+
127
+ @_register_response_message("ActivateSignalResponse1")
128
+ def _activate_signal_success_message(self: Any) -> str:
129
+ return "Signal activated successfully."
130
+
131
+
132
+ @_register_response_message("ActivateSignalResponse2")
133
+ def _activate_signal_error_message(self: Any) -> str:
134
+ errors = getattr(self, "errors", None)
135
+ error_count = len(errors) if errors else 0
136
+ return f"Signal activation failed with {error_count} {_pluralize(error_count, 'error')}."
137
+
138
+
139
+ @_register_response_message("PreviewCreativeResponse1")
140
+ def _preview_creative_single_message(self: Any) -> str:
141
+ previews = getattr(self, "previews", None)
142
+ preview_count = len(previews) if previews else 0
143
+ return f"Generated {preview_count} {_pluralize(preview_count, 'preview')}."
144
+
145
+
146
+ @_register_response_message("PreviewCreativeResponse2")
147
+ def _preview_creative_batch_message(self: Any) -> str:
148
+ results = getattr(self, "results", None)
149
+ result_count = len(results) if results else 0
150
+ return f"Generated previews for {result_count} {_pluralize(result_count, 'manifest')}."
151
+
152
+
153
+ @_register_response_message("BuildCreativeResponse1")
154
+ def _build_creative_success_message(self: Any) -> str:
155
+ return "Creative built successfully."
156
+
157
+
158
+ @_register_response_message("BuildCreativeResponse2")
159
+ def _build_creative_error_message(self: Any) -> str:
160
+ errors = getattr(self, "errors", None)
161
+ error_count = len(errors) if errors else 0
162
+ return f"Creative build failed with {error_count} {_pluralize(error_count, 'error')}."
163
+
164
+
165
+ @_register_response_message("GetMediaBuyDeliveryResponse")
166
+ def _get_media_buy_delivery_message(self: Any) -> str:
167
+ deliveries = getattr(self, "media_buy_deliveries", None)
168
+ if deliveries is None:
169
+ return "No delivery data available."
170
+ count = len(deliveries)
171
+ return f"Retrieved delivery data for {count} media {_pluralize(count, 'buy', 'buys')}."
172
+
173
+
174
+ @_register_response_message("ProvidePerformanceFeedbackResponse1")
175
+ def _provide_performance_feedback_success_message(self: Any) -> str:
176
+ return "Performance feedback recorded successfully."
177
+
178
+
179
+ @_register_response_message("ProvidePerformanceFeedbackResponse2")
180
+ def _provide_performance_feedback_error_message(self: Any) -> str:
181
+ errors = getattr(self, "errors", None)
182
+ error_count = len(errors) if errors else 0
183
+ return (
184
+ f"Performance feedback recording failed with "
185
+ f"{error_count} {_pluralize(error_count, 'error')}."
186
+ )
187
+
9
188
 
10
189
  class AdCPBaseModel(BaseModel):
11
190
  """Base model for AdCP types with spec-compliant serialization.
@@ -24,3 +203,17 @@ class AdCPBaseModel(BaseModel):
24
203
  if "exclude_none" not in kwargs:
25
204
  kwargs["exclude_none"] = True
26
205
  return super().model_dump_json(**kwargs)
206
+
207
+ def summary(self) -> str:
208
+ """Human-readable summary for protocol responses.
209
+
210
+ Returns a standardized human-readable message suitable for MCP tool
211
+ results, A2A task communications, and REST API responses.
212
+
213
+ For types without a registered formatter, returns a generic message
214
+ with the class name.
215
+ """
216
+ formatter = _RESPONSE_MESSAGE_REGISTRY.get(self.__class__.__name__)
217
+ if formatter:
218
+ return formatter(self)
219
+ return f"{self.__class__.__name__} response"
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: adagents.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -19,7 +19,7 @@ class AuthorizedSalesAgents1(AdCPBaseModel):
19
19
  field_schema: Annotated[
20
20
  str | None,
21
21
  Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
22
- ] = 'https://adcontextprotocol.org/schemas/adagents.json'
22
+ ] = None
23
23
  authoritative_location: Annotated[
24
24
  AnyUrl,
25
25
  Field(
@@ -156,11 +156,7 @@ class AuthorizedAgents3(AdCPBaseModel):
156
156
  ),
157
157
  ]
158
158
  publisher_properties: Annotated[
159
- list[
160
- publisher_property_selector.PublisherPropertySelector1
161
- | publisher_property_selector.PublisherPropertySelector2
162
- | publisher_property_selector.PublisherPropertySelector3
163
- ],
159
+ list[publisher_property_selector.PublisherPropertySelector],
164
160
  Field(
165
161
  description='Properties from other publisher domains this agent is authorized for. Each entry specifies a publisher domain and which of their properties this agent can sell',
166
162
  min_length=1,
@@ -202,7 +198,7 @@ class AuthorizedSalesAgents2(AdCPBaseModel):
202
198
  field_schema: Annotated[
203
199
  str | None,
204
200
  Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
205
- ] = 'https://adcontextprotocol.org/schemas/adagents.json'
201
+ ] = None
206
202
  authorized_agents: Annotated[
207
203
  list[AuthorizedAgents | AuthorizedAgents1 | AuthorizedAgents2 | AuthorizedAgents3],
208
204
  Field(
@@ -242,12 +238,12 @@ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAg
242
238
  description='Declaration of authorized sales agents for advertising inventory. Hosted at /.well-known/adagents.json on publisher domains. Can either contain the full structure inline or reference an authoritative URL.',
243
239
  examples=[
244
240
  {
245
- '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
241
+ '$schema': '/schemas/latest/adagents.json',
246
242
  'authoritative_location': 'https://cdn.example.com/adagents/v2/adagents.json',
247
243
  'last_updated': '2025-01-15T10:00:00Z',
248
244
  },
249
245
  {
250
- '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
246
+ '$schema': '/schemas/latest/adagents.json',
251
247
  'authorized_agents': [
252
248
  {
253
249
  'authorization_type': 'property_tags',
@@ -273,7 +269,7 @@ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAg
273
269
  },
274
270
  },
275
271
  {
276
- '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
272
+ '$schema': '/schemas/latest/adagents.json',
277
273
  'authorized_agents': [
278
274
  {
279
275
  'authorization_type': 'property_tags',
@@ -338,7 +334,7 @@ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAg
338
334
  },
339
335
  },
340
336
  {
341
- '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
337
+ '$schema': '/schemas/latest/adagents.json',
342
338
  'authorized_agents': [
343
339
  {
344
340
  'authorization_type': 'property_tags',
@@ -366,7 +362,7 @@ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAg
366
362
  },
367
363
  },
368
364
  {
369
- '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
365
+ '$schema': '/schemas/latest/adagents.json',
370
366
  'authorized_agents': [
371
367
  {
372
368
  'authorization_type': 'publisher_properties',
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/activation_key.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import ConfigDict, Field
10
+ from pydantic import ConfigDict, Field, RootModel
11
11
 
12
12
 
13
13
  class ActivationKey1(AdCPBaseModel):
@@ -28,3 +28,13 @@ class ActivationKey2(AdCPBaseModel):
28
28
  key: Annotated[str, Field(description='The targeting parameter key')]
29
29
  type: Annotated[Literal['key_value'], Field(description='Key-value pair based targeting')]
30
30
  value: Annotated[str, Field(description='The targeting parameter value')]
31
+
32
+
33
+ class ActivationKey(RootModel[ActivationKey1 | ActivationKey2]):
34
+ root: Annotated[
35
+ ActivationKey1 | ActivationKey2,
36
+ Field(
37
+ description="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.",
38
+ title='Activation Key',
39
+ ),
40
+ ]
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/daast_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, ConfigDict, Field
10
+ from pydantic import AnyUrl, ConfigDict, Field, RootModel
11
11
 
12
12
  from ...enums import daast_tracking_event
13
13
  from ...enums import daast_version as daast_version_1
@@ -59,3 +59,13 @@ class DaastAsset2(AdCPBaseModel):
59
59
  list[daast_tracking_event.DaastTrackingEvent] | None,
60
60
  Field(description='Tracking events supported by this DAAST tag'),
61
61
  ] = None
62
+
63
+
64
+ class DaastAsset(RootModel[DaastAsset1 | DaastAsset2]):
65
+ root: Annotated[
66
+ DaastAsset1 | DaastAsset2,
67
+ Field(
68
+ description='DAAST (Digital Audio Ad Serving Template) tag for third-party audio ad serving',
69
+ title='DAAST Asset',
70
+ ),
71
+ ]
@@ -1,19 +1,23 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/image_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated
8
8
 
9
- from pydantic import AnyUrl, Field
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import AnyUrl, ConfigDict, Field
10
11
 
11
- from ..dimensions import Dimensions
12
12
 
13
-
14
- class ImageAsset(Dimensions):
13
+ class ImageAsset(AdCPBaseModel):
14
+ model_config = ConfigDict(
15
+ extra='forbid',
16
+ )
15
17
  alt_text: Annotated[str | None, Field(description='Alternative text for accessibility')] = None
16
18
  format: Annotated[
17
19
  str | None, Field(description='Image file format (jpg, png, gif, webp, etc.)')
18
20
  ] = None
21
+ height: Annotated[int, Field(description='Height in pixels', ge=1)]
19
22
  url: Annotated[AnyUrl, Field(description='URL to the image asset')]
23
+ width: Annotated[int, Field(description='Width in pixels', ge=1)]
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/vast_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, ConfigDict, Field
10
+ from pydantic import AnyUrl, ConfigDict, Field, RootModel
11
11
 
12
12
  from ...enums import vast_tracking_event
13
13
  from ...enums import vast_version as vast_version_1
@@ -61,3 +61,13 @@ class VastAsset2(AdCPBaseModel):
61
61
  bool | None,
62
62
  Field(description='Whether VPAID (Video Player-Ad Interface Definition) is supported'),
63
63
  ] = None
64
+
65
+
66
+ class VastAsset(RootModel[VastAsset1 | VastAsset2]):
67
+ root: Annotated[
68
+ VastAsset1 | VastAsset2,
69
+ Field(
70
+ description='VAST (Video Ad Serving Template) tag for third-party video ad serving',
71
+ title='VAST Asset',
72
+ ),
73
+ ]
@@ -1,17 +1,19 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/video_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated
8
8
 
9
- from pydantic import AnyUrl, Field
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import AnyUrl, ConfigDict, Field
10
11
 
11
- from ..dimensions import Dimensions
12
12
 
13
-
14
- class VideoAsset(Dimensions):
13
+ class VideoAsset(AdCPBaseModel):
14
+ model_config = ConfigDict(
15
+ extra='forbid',
16
+ )
15
17
  bitrate_kbps: Annotated[
16
18
  int | None, Field(description='Video bitrate in kilobits per second', ge=1)
17
19
  ] = None
@@ -21,4 +23,6 @@ class VideoAsset(Dimensions):
21
23
  format: Annotated[str | None, Field(description='Video file format (mp4, webm, mov, etc.)')] = (
22
24
  None
23
25
  )
26
+ height: Annotated[int, Field(description='Height in pixels', ge=1)]
24
27
  url: Annotated[AnyUrl, Field(description='URL to the video asset')]
28
+ width: Annotated[int, Field(description='Width in pixels', ge=1)]
@@ -0,0 +1,72 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/async_response_data.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from pydantic import Field, RootModel
10
+
11
+ from ..media_buy import (
12
+ create_media_buy_async_response_input_required,
13
+ create_media_buy_async_response_submitted,
14
+ create_media_buy_async_response_working,
15
+ create_media_buy_response,
16
+ get_products_async_response_input_required,
17
+ get_products_async_response_submitted,
18
+ get_products_async_response_working,
19
+ get_products_response,
20
+ sync_creatives_async_response_input_required,
21
+ sync_creatives_async_response_submitted,
22
+ sync_creatives_async_response_working,
23
+ sync_creatives_response,
24
+ update_media_buy_async_response_input_required,
25
+ update_media_buy_async_response_submitted,
26
+ update_media_buy_async_response_working,
27
+ update_media_buy_response,
28
+ )
29
+
30
+
31
+ class AdcpAsyncResponseData(
32
+ RootModel[
33
+ get_products_response.GetProductsResponse
34
+ | get_products_async_response_working.GetProductsWorking
35
+ | get_products_async_response_input_required.GetProductsInputRequired
36
+ | get_products_async_response_submitted.GetProductsSubmitted
37
+ | create_media_buy_response.CreateMediaBuyResponse
38
+ | create_media_buy_async_response_working.CreateMediaBuyWorking
39
+ | create_media_buy_async_response_input_required.CreateMediaBuyInputRequired
40
+ | create_media_buy_async_response_submitted.CreateMediaBuySubmitted
41
+ | update_media_buy_response.UpdateMediaBuyResponse
42
+ | update_media_buy_async_response_working.UpdateMediaBuyWorking
43
+ | update_media_buy_async_response_input_required.UpdateMediaBuyInputRequired
44
+ | update_media_buy_async_response_submitted.UpdateMediaBuySubmitted
45
+ | sync_creatives_response.SyncCreativesResponse
46
+ | sync_creatives_async_response_working.SyncCreativesWorking
47
+ | sync_creatives_async_response_input_required.SyncCreativesInputRequired
48
+ | sync_creatives_async_response_submitted.SyncCreativesSubmitted
49
+ ]
50
+ ):
51
+ root: Annotated[
52
+ get_products_response.GetProductsResponse
53
+ | get_products_async_response_working.GetProductsWorking
54
+ | get_products_async_response_input_required.GetProductsInputRequired
55
+ | get_products_async_response_submitted.GetProductsSubmitted
56
+ | create_media_buy_response.CreateMediaBuyResponse
57
+ | create_media_buy_async_response_working.CreateMediaBuyWorking
58
+ | create_media_buy_async_response_input_required.CreateMediaBuyInputRequired
59
+ | create_media_buy_async_response_submitted.CreateMediaBuySubmitted
60
+ | update_media_buy_response.UpdateMediaBuyResponse
61
+ | update_media_buy_async_response_working.UpdateMediaBuyWorking
62
+ | update_media_buy_async_response_input_required.UpdateMediaBuyInputRequired
63
+ | update_media_buy_async_response_submitted.UpdateMediaBuySubmitted
64
+ | sync_creatives_response.SyncCreativesResponse
65
+ | sync_creatives_async_response_working.SyncCreativesWorking
66
+ | sync_creatives_async_response_input_required.SyncCreativesInputRequired
67
+ | sync_creatives_async_response_submitted.SyncCreativesSubmitted,
68
+ Field(
69
+ description='Union of all possible data payloads for async task webhook responses. For completed/failed statuses, use the main task response schema. For working/input-required/submitted, use the status-specific schemas.',
70
+ title='AdCP Async Response Data',
71
+ ),
72
+ ]
@@ -0,0 +1,35 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/brand_manifest_ref.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from pydantic import AnyUrl, Field, RootModel
10
+
11
+ from . import brand_manifest
12
+
13
+
14
+ class BrandManifestReference(RootModel[brand_manifest.BrandManifest | AnyUrl]):
15
+ root: Annotated[
16
+ brand_manifest.BrandManifest | AnyUrl,
17
+ Field(
18
+ description='Brand manifest provided either as an inline object or a URL string pointing to a hosted manifest',
19
+ examples=[
20
+ {
21
+ 'data': {
22
+ 'colors': {'primary': '#FF6B35'},
23
+ 'name': 'ACME Corporation',
24
+ 'url': 'https://acmecorp.com',
25
+ },
26
+ 'description': 'Inline brand manifest',
27
+ },
28
+ {
29
+ 'data': 'https://cdn.acmecorp.com/brand-manifest.json',
30
+ 'description': 'URL string reference to hosted manifest',
31
+ },
32
+ ],
33
+ title='Brand Manifest Reference',
34
+ ),
35
+ ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/creative_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -59,12 +59,10 @@ class CreativeAsset(AdCPBaseModel):
59
59
  | html_asset.HtmlAsset
60
60
  | css_asset.CssAsset
61
61
  | javascript_asset.JavascriptAsset
62
+ | vast_asset.VastAsset
63
+ | daast_asset.DaastAsset
62
64
  | promoted_offerings.PromotedOfferings
63
- | url_asset.UrlAsset
64
- | vast_asset.VastAsset1
65
- | vast_asset.VastAsset2
66
- | daast_asset.DaastAsset1
67
- | daast_asset.DaastAsset2,
65
+ | url_asset.UrlAsset,
68
66
  ],
69
67
  Field(description='Assets required by the format, keyed by asset_role'),
70
68
  ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/creative_manifest.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -37,17 +37,15 @@ class CreativeManifest(AdCPBaseModel):
37
37
  image_asset.ImageAsset
38
38
  | video_asset.VideoAsset
39
39
  | audio_asset.AudioAsset
40
+ | vast_asset.VastAsset
40
41
  | text_asset.TextAsset
41
42
  | url_asset.UrlAsset
42
43
  | html_asset.HtmlAsset
43
44
  | javascript_asset.JavascriptAsset
44
45
  | webhook_asset.WebhookAsset
45
46
  | css_asset.CssAsset
46
- | promoted_offerings.PromotedOfferings
47
- | vast_asset.VastAsset1
48
- | vast_asset.VastAsset2
49
- | daast_asset.DaastAsset1
50
- | daast_asset.DaastAsset2,
47
+ | daast_asset.DaastAsset
48
+ | promoted_offerings.PromotedOfferings,
51
49
  ],
52
50
  Field(
53
51
  description="Map of asset IDs to actual asset content. Each key MUST match an asset_id from the format's assets_required array (e.g., 'banner_image', 'clickthrough_url', 'video_file', 'vast_tag'). The asset_id is the technical identifier used to match assets to format requirements.\n\nIMPORTANT: Creative manifest validation MUST be performed in the context of the format specification. The format defines what type each asset_id should be, which eliminates any validation ambiguity."
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/deployment.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field
10
+ from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel
11
11
 
12
12
  from . import activation_key as activation_key_1
13
13
 
@@ -18,10 +18,9 @@ class Deployment1(AdCPBaseModel):
18
18
  )
19
19
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
20
20
  activation_key: Annotated[
21
- activation_key_1.ActivationKey1 | activation_key_1.ActivationKey2 | None,
21
+ activation_key_1.ActivationKey | None,
22
22
  Field(
23
- description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.',
24
- title='Activation Key',
23
+ description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.'
25
24
  ),
26
25
  ] = None
27
26
  deployed_at: Annotated[
@@ -51,10 +50,9 @@ class Deployment2(AdCPBaseModel):
51
50
  )
52
51
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
53
52
  activation_key: Annotated[
54
- activation_key_1.ActivationKey1 | activation_key_1.ActivationKey2 | None,
53
+ activation_key_1.ActivationKey | None,
55
54
  Field(
56
- description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.',
57
- title='Activation Key',
55
+ description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.'
58
56
  ),
59
57
  ] = None
60
58
  agent_url: Annotated[AnyUrl, Field(description='URL identifying the deployment agent')]
@@ -76,3 +74,13 @@ class Deployment2(AdCPBaseModel):
76
74
  Literal['agent'],
77
75
  Field(description='Discriminator indicating this is an agent URL-based deployment'),
78
76
  ]
77
+
78
+
79
+ class Deployment(RootModel[Deployment1 | Deployment2]):
80
+ root: Annotated[
81
+ Deployment1 | Deployment2,
82
+ Field(
83
+ description='A signal deployment to a specific deployment target with activation status and key',
84
+ title='Deployment',
85
+ ),
86
+ ]