adcp 2.17.0__py3-none-any.whl → 2.18.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 (110) hide show
  1. adcp/ADCP_VERSION +1 -1
  2. adcp/__init__.py +22 -1
  3. adcp/__main__.py +72 -0
  4. adcp/types/_generated.py +7 -1
  5. adcp/types/generated_poc/adagents.py +14 -14
  6. adcp/types/generated_poc/core/activation_key.py +3 -3
  7. adcp/types/generated_poc/core/assets/audio_asset.py +2 -2
  8. adcp/types/generated_poc/core/assets/css_asset.py +2 -2
  9. adcp/types/generated_poc/core/assets/daast_asset.py +3 -3
  10. adcp/types/generated_poc/core/assets/html_asset.py +2 -2
  11. adcp/types/generated_poc/core/assets/image_asset.py +2 -2
  12. adcp/types/generated_poc/core/assets/javascript_asset.py +2 -2
  13. adcp/types/generated_poc/core/assets/text_asset.py +2 -2
  14. adcp/types/generated_poc/core/assets/url_asset.py +2 -2
  15. adcp/types/generated_poc/core/assets/vast_asset.py +3 -3
  16. adcp/types/generated_poc/core/assets/video_asset.py +2 -2
  17. adcp/types/generated_poc/core/assets/webhook_asset.py +2 -2
  18. adcp/types/generated_poc/core/brand_manifest.py +4 -4
  19. adcp/types/generated_poc/core/context.py +1 -2
  20. adcp/types/generated_poc/core/creative_asset.py +3 -3
  21. adcp/types/generated_poc/core/creative_assignment.py +2 -2
  22. adcp/types/generated_poc/core/creative_filters.py +2 -2
  23. adcp/types/generated_poc/core/creative_manifest.py +2 -2
  24. adcp/types/generated_poc/core/creative_policy.py +2 -2
  25. adcp/types/generated_poc/core/delivery_metrics.py +3 -3
  26. adcp/types/generated_poc/core/deployment.py +3 -3
  27. adcp/types/generated_poc/core/destination.py +3 -3
  28. adcp/types/generated_poc/core/error.py +5 -5
  29. adcp/types/generated_poc/core/ext.py +1 -2
  30. adcp/types/generated_poc/core/format.py +94 -7
  31. adcp/types/generated_poc/core/format_id.py +2 -2
  32. adcp/types/generated_poc/core/frequency_cap.py +2 -2
  33. adcp/types/generated_poc/core/measurement.py +2 -2
  34. adcp/types/generated_poc/core/media_buy.py +2 -2
  35. adcp/types/generated_poc/core/package.py +2 -2
  36. adcp/types/generated_poc/core/performance_feedback.py +3 -3
  37. adcp/types/generated_poc/core/placement.py +2 -2
  38. adcp/types/generated_poc/core/product.py +4 -4
  39. adcp/types/generated_poc/core/product_filters.py +4 -4
  40. adcp/types/generated_poc/core/promoted_offerings.py +4 -4
  41. adcp/types/generated_poc/core/promoted_products.py +2 -2
  42. adcp/types/generated_poc/core/property.py +3 -3
  43. adcp/types/generated_poc/core/protocol_envelope.py +2 -2
  44. adcp/types/generated_poc/core/publisher_property_selector.py +4 -4
  45. adcp/types/generated_poc/core/reporting_capabilities.py +2 -2
  46. adcp/types/generated_poc/core/response.py +2 -2
  47. adcp/types/generated_poc/core/signal_filters.py +2 -2
  48. adcp/types/generated_poc/core/sub_asset.py +3 -3
  49. adcp/types/generated_poc/core/targeting.py +2 -2
  50. adcp/types/generated_poc/creative/list_creative_formats_request.py +2 -2
  51. adcp/types/generated_poc/creative/list_creative_formats_response.py +2 -2
  52. adcp/types/generated_poc/creative/preview_creative_request.py +7 -7
  53. adcp/types/generated_poc/creative/preview_creative_response.py +3 -3
  54. adcp/types/generated_poc/creative/preview_render.py +4 -4
  55. adcp/types/generated_poc/media_buy/build_creative_request.py +2 -2
  56. adcp/types/generated_poc/media_buy/build_creative_response.py +3 -3
  57. adcp/types/generated_poc/media_buy/create_media_buy_async_response_input_required.py +2 -2
  58. adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py +2 -2
  59. adcp/types/generated_poc/media_buy/create_media_buy_async_response_working.py +2 -2
  60. adcp/types/generated_poc/media_buy/create_media_buy_request.py +47 -6
  61. adcp/types/generated_poc/media_buy/create_media_buy_response.py +3 -3
  62. adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py +2 -2
  63. adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +6 -6
  64. adcp/types/generated_poc/media_buy/get_products_async_response_input_required.py +2 -2
  65. adcp/types/generated_poc/media_buy/get_products_async_response_submitted.py +2 -2
  66. adcp/types/generated_poc/media_buy/get_products_async_response_working.py +2 -2
  67. adcp/types/generated_poc/media_buy/get_products_request.py +2 -2
  68. adcp/types/generated_poc/media_buy/get_products_response.py +2 -2
  69. adcp/types/generated_poc/media_buy/list_authorized_properties_request.py +2 -2
  70. adcp/types/generated_poc/media_buy/list_authorized_properties_response.py +2 -2
  71. adcp/types/generated_poc/media_buy/list_creative_formats_request.py +2 -2
  72. adcp/types/generated_poc/media_buy/list_creative_formats_response.py +2 -2
  73. adcp/types/generated_poc/media_buy/list_creatives_request.py +4 -4
  74. adcp/types/generated_poc/media_buy/list_creatives_response.py +9 -9
  75. adcp/types/generated_poc/media_buy/package_request.py +2 -2
  76. adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py +4 -4
  77. adcp/types/generated_poc/media_buy/provide_performance_feedback_response.py +3 -3
  78. adcp/types/generated_poc/media_buy/sync_creatives_async_response_input_required.py +2 -2
  79. adcp/types/generated_poc/media_buy/sync_creatives_async_response_submitted.py +2 -2
  80. adcp/types/generated_poc/media_buy/sync_creatives_async_response_working.py +2 -2
  81. adcp/types/generated_poc/media_buy/sync_creatives_request.py +2 -2
  82. adcp/types/generated_poc/media_buy/sync_creatives_response.py +4 -4
  83. adcp/types/generated_poc/media_buy/update_media_buy_async_response_input_required.py +2 -2
  84. adcp/types/generated_poc/media_buy/update_media_buy_async_response_submitted.py +2 -2
  85. adcp/types/generated_poc/media_buy/update_media_buy_async_response_working.py +2 -2
  86. adcp/types/generated_poc/media_buy/update_media_buy_request.py +5 -5
  87. adcp/types/generated_poc/media_buy/update_media_buy_response.py +3 -3
  88. adcp/types/generated_poc/pricing_options/cpc_option.py +2 -2
  89. adcp/types/generated_poc/pricing_options/cpcv_option.py +2 -2
  90. adcp/types/generated_poc/pricing_options/cpm_auction_option.py +2 -2
  91. adcp/types/generated_poc/pricing_options/cpm_fixed_option.py +2 -2
  92. adcp/types/generated_poc/pricing_options/cpp_option.py +3 -3
  93. adcp/types/generated_poc/pricing_options/cpv_option.py +4 -4
  94. adcp/types/generated_poc/pricing_options/flat_rate_option.py +3 -3
  95. adcp/types/generated_poc/pricing_options/vcpm_auction_option.py +2 -2
  96. adcp/types/generated_poc/pricing_options/vcpm_fixed_option.py +2 -2
  97. adcp/types/generated_poc/protocols/adcp_extension.py +2 -2
  98. adcp/types/generated_poc/signals/activate_signal_request.py +2 -2
  99. adcp/types/generated_poc/signals/activate_signal_response.py +3 -3
  100. adcp/types/generated_poc/signals/get_signals_request.py +3 -3
  101. adcp/types/generated_poc/signals/get_signals_response.py +4 -4
  102. adcp/utils/__init__.py +24 -1
  103. adcp/utils/format_assets.py +224 -0
  104. adcp/utils/preview_cache.py +29 -7
  105. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/METADATA +1 -1
  106. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/RECORD +110 -109
  107. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/WHEEL +0 -0
  108. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/entry_points.txt +0 -0
  109. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/licenses/LICENSE +0 -0
  110. {adcp-2.17.0.dist-info → adcp-2.18.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/delivery_metrics.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class VenueBreakdownItem(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  impressions: Annotated[int, Field(description='Impressions delivered at this venue', ge=0)]
18
18
  loop_plays: Annotated[int | None, Field(description='Loop plays at this venue', ge=0)] = None
@@ -29,7 +29,7 @@ class VenueBreakdownItem(AdCPBaseModel):
29
29
 
30
30
  class DoohMetrics(AdCPBaseModel):
31
31
  model_config = ConfigDict(
32
- extra='forbid',
32
+ extra='allow',
33
33
  )
34
34
  calculation_notes: Annotated[
35
35
  str | None, Field(description='Explanation of how DOOH impressions were calculated')
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/deployment.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,7 +14,7 @@ from . import activation_key as activation_key_1
14
14
 
15
15
  class Deployment1(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra='allow',
18
18
  )
19
19
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
20
20
  activation_key: Annotated[
@@ -46,7 +46,7 @@ class Deployment1(AdCPBaseModel):
46
46
 
47
47
  class Deployment2(AdCPBaseModel):
48
48
  model_config = ConfigDict(
49
- extra='forbid',
49
+ extra='allow',
50
50
  )
51
51
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
52
52
  activation_key: Annotated[
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/destination.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import AnyUrl, ConfigDict, Field, RootModel
12
12
 
13
13
  class Destination1(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  account: Annotated[
18
18
  str | None, Field(description='Optional account identifier on the platform')
@@ -29,7 +29,7 @@ class Destination1(AdCPBaseModel):
29
29
 
30
30
  class Destination2(AdCPBaseModel):
31
31
  model_config = ConfigDict(
32
- extra='forbid',
32
+ extra='allow',
33
33
  )
34
34
  account: Annotated[
35
35
  str | None, Field(description='Optional account identifier on the agent')
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/error.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,12 +12,12 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class Error(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  code: Annotated[str, Field(description='Error code for programmatic handling')]
18
- details: Annotated[Any | None, Field(description='Additional task-specific error details')] = (
19
- None
20
- )
18
+ details: Annotated[
19
+ dict[str, Any] | None, Field(description='Additional task-specific error details')
20
+ ] = None
21
21
  field: Annotated[
22
22
  str | None,
23
23
  Field(description="Field path associated with the error (e.g., 'packages[0].targeting')"),
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/ext.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -9,7 +9,6 @@ from pydantic import ConfigDict
9
9
 
10
10
 
11
11
  class ExtensionObject(AdCPBaseModel):
12
- pass
13
12
  model_config = ConfigDict(
14
13
  extra='allow',
15
14
  )
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/format.json
3
- # timestamp: 2025-12-18T20:00:24+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -13,6 +13,84 @@ from ..enums import asset_content_type, format_category, format_id_parameter
13
13
  from . import format_id as format_id_1
14
14
 
15
15
 
16
+ class Assets(AdCPBaseModel):
17
+ asset_id: Annotated[
18
+ str,
19
+ Field(
20
+ description='Unique identifier for this asset. Creative manifests MUST use this exact value as the key in the assets object.'
21
+ ),
22
+ ]
23
+ asset_role: Annotated[
24
+ str | None,
25
+ Field(
26
+ description="Optional descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). Not used for referencing assets in manifests—use asset_id instead. This field is for human-readable documentation and UI display only."
27
+ ),
28
+ ] = None
29
+ asset_type: Annotated[asset_content_type.AssetContentType, Field(description='Type of asset')]
30
+ item_type: Annotated[
31
+ Literal['individual'],
32
+ Field(description='Discriminator indicating this is an individual asset'),
33
+ ]
34
+ required: Annotated[
35
+ bool,
36
+ Field(
37
+ description='Whether this asset is required (true) or optional (false). Required assets must be provided for a valid creative. Optional assets enhance the creative but are not mandatory.'
38
+ ),
39
+ ]
40
+ requirements: Annotated[
41
+ dict[str, Any] | None,
42
+ Field(
43
+ description='Technical requirements for this asset (dimensions, file size, duration, etc.). For template formats, use parameters_from_format_id: true to indicate asset parameters must match the format_id parameters (width/height/unit and/or duration_ms).'
44
+ ),
45
+ ] = None
46
+
47
+
48
+ class Asset(AdCPBaseModel):
49
+ asset_id: Annotated[str, Field(description='Identifier for this asset within the group')]
50
+ asset_role: Annotated[
51
+ str | None,
52
+ Field(
53
+ description="Optional descriptive label for this asset's purpose. Not used for referencing assets in manifests—use asset_id instead. This field is for human-readable documentation and UI display only."
54
+ ),
55
+ ] = None
56
+ asset_type: Annotated[asset_content_type.AssetContentType, Field(description='Type of asset')]
57
+ required: Annotated[
58
+ bool,
59
+ Field(description='Whether this asset is required within each repetition of the group'),
60
+ ]
61
+ requirements: Annotated[
62
+ dict[str, Any] | None,
63
+ Field(
64
+ description='Technical requirements for this asset. For template formats, use parameters_from_format_id: true to indicate asset parameters must match the format_id parameters (width/height/unit and/or duration_ms).'
65
+ ),
66
+ ] = None
67
+
68
+
69
+ class Assets1(AdCPBaseModel):
70
+ asset_group_id: Annotated[
71
+ str, Field(description="Identifier for this asset group (e.g., 'product', 'slide', 'card')")
72
+ ]
73
+ assets: Annotated[list[Asset], Field(description='Assets within each repetition of this group')]
74
+ item_type: Annotated[
75
+ Literal['repeatable_group'],
76
+ Field(description='Discriminator indicating this is a repeatable asset group'),
77
+ ]
78
+ max_count: Annotated[int, Field(description='Maximum number of repetitions allowed', ge=1)]
79
+ min_count: Annotated[
80
+ int,
81
+ Field(
82
+ description='Minimum number of repetitions required (if group is required) or allowed (if optional)',
83
+ ge=0,
84
+ ),
85
+ ]
86
+ required: Annotated[
87
+ bool,
88
+ Field(
89
+ description='Whether this asset group is required. If true, at least min_count repetitions must be provided.'
90
+ ),
91
+ ]
92
+
93
+
16
94
  class AssetsRequired(AdCPBaseModel):
17
95
  asset_id: Annotated[
18
96
  str,
@@ -40,7 +118,7 @@ class AssetsRequired(AdCPBaseModel):
40
118
  ] = None
41
119
 
42
120
 
43
- class Asset(AdCPBaseModel):
121
+ class Asset2(AdCPBaseModel):
44
122
  asset_id: Annotated[str, Field(description='Identifier for this asset within the group')]
45
123
  asset_role: Annotated[
46
124
  str | None,
@@ -64,7 +142,9 @@ class AssetsRequired1(AdCPBaseModel):
64
142
  asset_group_id: Annotated[
65
143
  str, Field(description="Identifier for this asset group (e.g., 'product', 'slide', 'card')")
66
144
  ]
67
- assets: Annotated[list[Asset], Field(description='Assets within each repetition of this group')]
145
+ assets: Annotated[
146
+ list[Asset2], Field(description='Assets within each repetition of this group')
147
+ ]
68
148
  item_type: Annotated[
69
149
  Literal['repeatable_group'],
70
150
  Field(description='Discriminator indicating this is a repeatable asset group'),
@@ -146,7 +226,7 @@ class Renders1(AdCPBaseModel):
146
226
 
147
227
  class FormatCard(AdCPBaseModel):
148
228
  model_config = ConfigDict(
149
- extra='forbid',
229
+ extra='allow',
150
230
  )
151
231
  format_id: Annotated[
152
232
  format_id_1.FormatId,
@@ -162,7 +242,7 @@ class FormatCard(AdCPBaseModel):
162
242
 
163
243
  class FormatCardDetailed(AdCPBaseModel):
164
244
  model_config = ConfigDict(
165
- extra='forbid',
245
+ extra='allow',
166
246
  )
167
247
  format_id: Annotated[
168
248
  format_id_1.FormatId,
@@ -180,7 +260,7 @@ class FormatCardDetailed(AdCPBaseModel):
180
260
 
181
261
  class Format(AdCPBaseModel):
182
262
  model_config = ConfigDict(
183
- extra='forbid',
263
+ extra='allow',
184
264
  )
185
265
  accepts_parameters: Annotated[
186
266
  list[format_id_parameter.FormatIdParameter] | None,
@@ -188,10 +268,17 @@ class Format(AdCPBaseModel):
188
268
  description='List of parameters this format accepts in format_id. Template formats define which parameters (dimensions, duration, etc.) can be specified when instantiating the format. Empty or omitted means this is a concrete format with fixed parameters.'
189
269
  ),
190
270
  ] = None
271
+ assets: Annotated[
272
+ list[Assets | Assets1] | None,
273
+ Field(
274
+ description="Array of all assets supported for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Use the 'required' boolean on each asset to indicate whether it's mandatory. This field replaces the deprecated 'assets_required' and enables full asset discovery for buyers and AI agents."
275
+ ),
276
+ ] = None
191
277
  assets_required: Annotated[
192
278
  list[AssetsRequired | AssetsRequired1] | None,
193
279
  Field(
194
- description='Array of required assets or asset groups for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Can contain individual assets or repeatable asset sequences (e.g., carousel products, slideshow frames).'
280
+ deprecated=True,
281
+ description="DEPRECATED: Use 'assets' instead. Array of required assets or asset groups for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Can contain individual assets or repeatable asset sequences (e.g., carousel products, slideshow frames). This field is maintained for backward compatibility; new implementations should use 'assets' with the 'required' boolean on each asset."
195
282
  ),
196
283
  ] = None
197
284
  delivery: Annotated[
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/format_id.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import AnyUrl, ConfigDict, Field
12
12
 
13
13
  class FormatId(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  agent_url: Annotated[
18
18
  AnyUrl,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/frequency_cap.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class FrequencyCap(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  suppress_minutes: Annotated[
18
18
  float, Field(description='Minutes to suppress after impression', ge=0.0)
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/measurement.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class Measurement(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  attribution: Annotated[
18
18
  str,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/media_buy.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -16,7 +16,7 @@ from . import package
16
16
 
17
17
  class MediaBuy(AdCPBaseModel):
18
18
  model_config = ConfigDict(
19
- extra='forbid',
19
+ extra='allow',
20
20
  )
21
21
  buyer_ref: Annotated[
22
22
  str | None, Field(description="Buyer's reference identifier for this media buy")
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/package.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -17,7 +17,7 @@ from . import format_id, targeting
17
17
 
18
18
  class Package(AdCPBaseModel):
19
19
  model_config = ConfigDict(
20
- extra='forbid',
20
+ extra='allow',
21
21
  )
22
22
  bid_price: Annotated[
23
23
  float | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/performance_feedback.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -16,7 +16,7 @@ from ..enums import metric_type as metric_type_1
16
16
 
17
17
  class MeasurementPeriod(AdCPBaseModel):
18
18
  model_config = ConfigDict(
19
- extra='forbid',
19
+ extra='allow',
20
20
  )
21
21
  end: Annotated[
22
22
  AwareDatetime, Field(description='ISO 8601 end timestamp for measurement period')
@@ -35,7 +35,7 @@ class Status(Enum):
35
35
 
36
36
  class PerformanceFeedback(AdCPBaseModel):
37
37
  model_config = ConfigDict(
38
- extra='forbid',
38
+ extra='allow',
39
39
  )
40
40
  applied_at: Annotated[
41
41
  AwareDatetime | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/placement.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,7 +14,7 @@ from . import format_id
14
14
 
15
15
  class Placement(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra='allow',
18
18
  )
19
19
  description: Annotated[
20
20
  str | None, Field(description='Detailed description of where and how the placement appears')
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/product.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -35,7 +35,7 @@ class DeliveryMeasurement(AdCPBaseModel):
35
35
 
36
36
  class ProductCard(AdCPBaseModel):
37
37
  model_config = ConfigDict(
38
- extra='forbid',
38
+ extra='allow',
39
39
  )
40
40
  format_id: Annotated[
41
41
  format_id_1.FormatId,
@@ -51,7 +51,7 @@ class ProductCard(AdCPBaseModel):
51
51
 
52
52
  class ProductCardDetailed(AdCPBaseModel):
53
53
  model_config = ConfigDict(
54
- extra='forbid',
54
+ extra='allow',
55
55
  )
56
56
  format_id: Annotated[
57
57
  format_id_1.FormatId,
@@ -69,7 +69,7 @@ class ProductCardDetailed(AdCPBaseModel):
69
69
 
70
70
  class Product(AdCPBaseModel):
71
71
  model_config = ConfigDict(
72
- extra='forbid',
72
+ extra='allow',
73
73
  )
74
74
  brief_relevance: Annotated[
75
75
  str | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/product_filters.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -18,7 +18,7 @@ from . import format_id
18
18
 
19
19
  class BudgetRange(AdCPBaseModel):
20
20
  model_config = ConfigDict(
21
- extra='forbid',
21
+ extra='allow',
22
22
  )
23
23
  currency: Annotated[
24
24
  str,
@@ -32,7 +32,7 @@ class BudgetRange(AdCPBaseModel):
32
32
 
33
33
  class BudgetRange1(AdCPBaseModel):
34
34
  model_config = ConfigDict(
35
- extra='forbid',
35
+ extra='allow',
36
36
  )
37
37
  currency: Annotated[
38
38
  str,
@@ -50,7 +50,7 @@ class Country(RootModel[str]):
50
50
 
51
51
  class ProductFilters(AdCPBaseModel):
52
52
  model_config = ConfigDict(
53
- extra='forbid',
53
+ extra='allow',
54
54
  )
55
55
  budget_range: Annotated[
56
56
  BudgetRange | BudgetRange1 | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/promoted_offerings.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -29,7 +29,7 @@ class AssetType(Enum):
29
29
 
30
30
  class AssetSelectors(AdCPBaseModel):
31
31
  model_config = ConfigDict(
32
- extra='forbid',
32
+ extra='allow',
33
33
  )
34
34
  asset_types: Annotated[
35
35
  list[AssetType] | None, Field(description="Filter by asset type (e.g., ['image', 'video'])")
@@ -45,7 +45,7 @@ class AssetSelectors(AdCPBaseModel):
45
45
 
46
46
  class Offering(AdCPBaseModel):
47
47
  model_config = ConfigDict(
48
- extra='forbid',
48
+ extra='allow',
49
49
  )
50
50
  assets: Annotated[
51
51
  list[dict[str, Any]] | None, Field(description='Assets specific to this offering')
@@ -60,7 +60,7 @@ class Offering(AdCPBaseModel):
60
60
 
61
61
  class PromotedOfferings(AdCPBaseModel):
62
62
  model_config = ConfigDict(
63
- extra='forbid',
63
+ extra='allow',
64
64
  )
65
65
  asset_selectors: Annotated[
66
66
  AssetSelectors | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/promoted_products.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class PromotedProducts(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  manifest_category: Annotated[
18
18
  str | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/property.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -17,7 +17,7 @@ from . import property_tag
17
17
 
18
18
  class Identifier(AdCPBaseModel):
19
19
  model_config = ConfigDict(
20
- extra='forbid',
20
+ extra='allow',
21
21
  )
22
22
  type: Annotated[
23
23
  identifier_types.PropertyIdentifierTypes,
@@ -33,7 +33,7 @@ class Identifier(AdCPBaseModel):
33
33
 
34
34
  class Property(AdCPBaseModel):
35
35
  model_config = ConfigDict(
36
- extra='forbid',
36
+ extra='allow',
37
37
  )
38
38
  identifiers: Annotated[
39
39
  list[Identifier], Field(description='Array of identifiers for this property', min_length=1)
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/protocol_envelope.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -15,7 +15,7 @@ from . import push_notification_config as push_notification_config_1
15
15
 
16
16
  class ProtocolEnvelope(AdCPBaseModel):
17
17
  model_config = ConfigDict(
18
- extra='forbid',
18
+ extra='allow',
19
19
  )
20
20
  context_id: Annotated[
21
21
  str | None,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/publisher_property_selector.json
3
- # timestamp: 2025-12-11T15:09:37+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,7 +14,7 @@ from . import property_id, property_tag
14
14
 
15
15
  class PublisherPropertySelector1(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra='allow',
18
18
  )
19
19
  publisher_domain: Annotated[
20
20
  str,
@@ -33,7 +33,7 @@ class PublisherPropertySelector1(AdCPBaseModel):
33
33
 
34
34
  class PublisherPropertySelector2(AdCPBaseModel):
35
35
  model_config = ConfigDict(
36
- extra='forbid',
36
+ extra='allow',
37
37
  )
38
38
  property_ids: Annotated[
39
39
  list[property_id.PropertyId],
@@ -54,7 +54,7 @@ class PublisherPropertySelector2(AdCPBaseModel):
54
54
 
55
55
  class PublisherPropertySelector3(AdCPBaseModel):
56
56
  model_config = ConfigDict(
57
- extra='forbid',
57
+ extra='allow',
58
58
  )
59
59
  property_tags: Annotated[
60
60
  list[property_tag.PropertyTag],
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/reporting_capabilities.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,7 +14,7 @@ from ..enums import available_metric, reporting_frequency
14
14
 
15
15
  class ReportingCapabilities(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra='allow',
18
18
  )
19
19
  available_metrics: Annotated[
20
20
  list[available_metric.AvailableMetric],
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/response.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -12,7 +12,7 @@ from pydantic import ConfigDict, Field
12
12
 
13
13
  class ProtocolResponse(AdCPBaseModel):
14
14
  model_config = ConfigDict(
15
- extra='forbid',
15
+ extra='allow',
16
16
  )
17
17
  context_id: Annotated[str | None, Field(description='Session continuity identifier')] = None
18
18
  data: Annotated[
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/signal_filters.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2026-01-08T19:25:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,7 +14,7 @@ from ..enums import signal_catalog_type
14
14
 
15
15
  class SignalFilters(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra='allow',
18
18
  )
19
19
  catalog_types: Annotated[
20
20
  list[signal_catalog_type.SignalCatalogType] | None,