adcp 1.2.0__py3-none-any.whl → 1.3.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.
adcp/__init__.py CHANGED
@@ -18,35 +18,135 @@ from adcp.exceptions import (
18
18
  ADCPWebhookError,
19
19
  ADCPWebhookSignatureError,
20
20
  )
21
+
22
+ # Test helpers
23
+ from adcp.testing import (
24
+ CREATIVE_AGENT_CONFIG,
25
+ TEST_AGENT_A2A_CONFIG,
26
+ TEST_AGENT_MCP_CONFIG,
27
+ TEST_AGENT_TOKEN,
28
+ create_test_agent,
29
+ creative_agent,
30
+ test_agent,
31
+ test_agent_a2a,
32
+ test_agent_client,
33
+ )
21
34
  from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
22
35
  from adcp.types.generated import (
36
+ ActivateSignalError,
37
+ # Request/Response types
23
38
  ActivateSignalRequest,
24
39
  ActivateSignalResponse,
40
+ ActivateSignalSuccess,
41
+ ActivationKey,
42
+ AgentDeployment,
43
+ AgentDestination,
44
+ BothPreviewRender,
45
+ # Brand types
46
+ BrandManifest,
47
+ BrandManifestRef,
48
+ BuildCreativeRequest,
49
+ BuildCreativeResponse,
50
+ # Channel types
51
+ Channels,
52
+ CreateMediaBuyError,
25
53
  CreateMediaBuyRequest,
26
54
  CreateMediaBuyResponse,
55
+ CreateMediaBuySuccess,
56
+ # Creative types
57
+ CreativeAsset,
58
+ CreativeAssignment,
59
+ CreativeManifest,
60
+ CreativePolicy,
61
+ DaastAsset,
62
+ # Metrics types
63
+ DeliveryMetrics,
64
+ # Delivery types
65
+ DeliveryType,
66
+ Deployment,
67
+ # Deployment types
68
+ Destination,
69
+ Error,
70
+ Format,
71
+ FormatId,
72
+ FrequencyCap,
27
73
  GetMediaBuyDeliveryRequest,
28
74
  GetMediaBuyDeliveryResponse,
29
75
  GetProductsRequest,
30
76
  GetProductsResponse,
31
77
  GetSignalsRequest,
32
78
  GetSignalsResponse,
79
+ HtmlPreviewRender,
80
+ InlineDaastAsset,
81
+ InlineVastAsset,
82
+ Key_valueActivationKey,
33
83
  ListAuthorizedPropertiesRequest,
34
84
  ListAuthorizedPropertiesResponse,
35
85
  ListCreativeFormatsRequest,
36
86
  ListCreativeFormatsResponse,
37
87
  ListCreativesRequest,
38
88
  ListCreativesResponse,
89
+ Measurement,
90
+ # Core domain types
39
91
  MediaBuy,
92
+ # Status enums
93
+ MediaBuyStatus,
94
+ # Sub-asset types
95
+ MediaSubAsset,
96
+ Pacing,
97
+ Package,
98
+ PackageStatus,
99
+ PerformanceFeedback,
100
+ Placement,
101
+ PlatformDeployment,
102
+ PlatformDestination,
103
+ PreviewCreativeRequest,
104
+ PreviewCreativeResponse,
105
+ # Preview render types
106
+ PreviewRender,
107
+ PricingModel,
108
+ # Pricing types
109
+ PricingOption,
40
110
  Product,
111
+ PromotedProducts,
112
+ # Property and placement types
113
+ Property,
114
+ ProtocolEnvelope,
41
115
  ProvidePerformanceFeedbackRequest,
42
116
  ProvidePerformanceFeedbackResponse,
117
+ PushNotificationConfig,
118
+ ReportingCapabilities,
119
+ Response,
120
+ Segment_idActivationKey,
121
+ StandardFormatIds,
122
+ StartTiming,
123
+ SubAsset,
124
+ SyncCreativesError,
43
125
  SyncCreativesRequest,
44
126
  SyncCreativesResponse,
127
+ SyncCreativesSuccess,
128
+ # Targeting types
129
+ Targeting,
130
+ # Task types
131
+ TaskType,
132
+ TextSubAsset,
133
+ UpdateMediaBuyError,
45
134
  UpdateMediaBuyRequest,
46
135
  UpdateMediaBuyResponse,
136
+ UpdateMediaBuySuccess,
137
+ UrlDaastAsset,
138
+ UrlPreviewRender,
139
+ UrlVastAsset,
140
+ # Asset delivery types (VAST/DAAST)
141
+ VastAsset,
142
+ # Protocol types
143
+ WebhookPayload,
144
+ )
145
+ from adcp.types.generated import (
146
+ TaskStatus as GeneratedTaskStatus,
47
147
  )
48
148
 
49
- __version__ = "1.2.0"
149
+ __version__ = "1.3.0"
50
150
 
51
151
  __all__ = [
52
152
  # Client classes
@@ -58,6 +158,16 @@ __all__ = [
58
158
  "TaskResult",
59
159
  "TaskStatus",
60
160
  "WebhookMetadata",
161
+ # Test helpers
162
+ "test_agent",
163
+ "test_agent_a2a",
164
+ "creative_agent",
165
+ "test_agent_client",
166
+ "create_test_agent",
167
+ "TEST_AGENT_TOKEN",
168
+ "TEST_AGENT_MCP_CONFIG",
169
+ "TEST_AGENT_A2A_CONFIG",
170
+ "CREATIVE_AGENT_CONFIG",
61
171
  # Exceptions
62
172
  "ADCPError",
63
173
  "ADCPConnectionError",
@@ -67,30 +177,113 @@ __all__ = [
67
177
  "ADCPToolNotFoundError",
68
178
  "ADCPWebhookError",
69
179
  "ADCPWebhookSignatureError",
70
- # Generated request/response types
71
- "GetProductsRequest",
72
- "GetProductsResponse",
180
+ # Request/Response types
181
+ "ActivateSignalRequest",
182
+ "ActivateSignalResponse",
183
+ "ActivateSignalSuccess",
184
+ "ActivateSignalError",
185
+ "ActivationKey",
186
+ "Segment_idActivationKey",
187
+ "Key_valueActivationKey",
188
+ "BuildCreativeRequest",
189
+ "BuildCreativeResponse",
73
190
  "CreateMediaBuyRequest",
74
191
  "CreateMediaBuyResponse",
75
- "UpdateMediaBuyRequest",
76
- "UpdateMediaBuyResponse",
77
- "SyncCreativesRequest",
78
- "SyncCreativesResponse",
79
- "ListCreativesRequest",
80
- "ListCreativesResponse",
81
- "ListCreativeFormatsRequest",
82
- "ListCreativeFormatsResponse",
192
+ "CreateMediaBuySuccess",
193
+ "CreateMediaBuyError",
83
194
  "GetMediaBuyDeliveryRequest",
84
195
  "GetMediaBuyDeliveryResponse",
85
- "ListAuthorizedPropertiesRequest",
86
- "ListAuthorizedPropertiesResponse",
196
+ "GetProductsRequest",
197
+ "GetProductsResponse",
87
198
  "GetSignalsRequest",
88
199
  "GetSignalsResponse",
89
- "ActivateSignalRequest",
90
- "ActivateSignalResponse",
200
+ "ListAuthorizedPropertiesRequest",
201
+ "ListAuthorizedPropertiesResponse",
202
+ "ListCreativeFormatsRequest",
203
+ "ListCreativeFormatsResponse",
204
+ "ListCreativesRequest",
205
+ "ListCreativesResponse",
206
+ "PreviewCreativeRequest",
207
+ "PreviewCreativeResponse",
91
208
  "ProvidePerformanceFeedbackRequest",
92
209
  "ProvidePerformanceFeedbackResponse",
210
+ "SyncCreativesRequest",
211
+ "SyncCreativesResponse",
212
+ "SyncCreativesSuccess",
213
+ "SyncCreativesError",
214
+ "UpdateMediaBuyRequest",
215
+ "UpdateMediaBuyResponse",
216
+ "UpdateMediaBuySuccess",
217
+ "UpdateMediaBuyError",
93
218
  # Core domain types
94
- "Product",
95
219
  "MediaBuy",
220
+ "Product",
221
+ "Package",
222
+ "Error",
223
+ # Creative types
224
+ "CreativeAsset",
225
+ "CreativeManifest",
226
+ "CreativeAssignment",
227
+ "CreativePolicy",
228
+ "Format",
229
+ "FormatId",
230
+ # Property and placement types
231
+ "Property",
232
+ "Placement",
233
+ # Targeting types
234
+ "Targeting",
235
+ "FrequencyCap",
236
+ "Pacing",
237
+ # Brand types
238
+ "BrandManifest",
239
+ "BrandManifestRef",
240
+ # Metrics types
241
+ "DeliveryMetrics",
242
+ "Measurement",
243
+ "PerformanceFeedback",
244
+ # Status enums
245
+ "MediaBuyStatus",
246
+ "PackageStatus",
247
+ # Pricing types
248
+ "PricingOption",
249
+ "PricingModel",
250
+ # Delivery types
251
+ "DeliveryType",
252
+ "StartTiming",
253
+ # Channel types
254
+ "Channels",
255
+ "StandardFormatIds",
256
+ # Protocol types
257
+ "WebhookPayload",
258
+ "ProtocolEnvelope",
259
+ "Response",
260
+ "PromotedProducts",
261
+ "PushNotificationConfig",
262
+ "ReportingCapabilities",
263
+ # Deployment types
264
+ "Destination",
265
+ "Deployment",
266
+ "PlatformDestination",
267
+ "AgentDestination",
268
+ "PlatformDeployment",
269
+ "AgentDeployment",
270
+ # Sub-asset types
271
+ "MediaSubAsset",
272
+ "SubAsset",
273
+ "TextSubAsset",
274
+ # Asset delivery types (VAST/DAAST)
275
+ "VastAsset",
276
+ "UrlVastAsset",
277
+ "InlineVastAsset",
278
+ "DaastAsset",
279
+ "UrlDaastAsset",
280
+ "InlineDaastAsset",
281
+ # Preview render types
282
+ "PreviewRender",
283
+ "UrlPreviewRender",
284
+ "HtmlPreviewRender",
285
+ "BothPreviewRender",
286
+ # Task types
287
+ "TaskType",
288
+ "GeneratedTaskStatus",
96
289
  ]
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(raw_result, GetProductsResponse)
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(raw_result, ListCreativeFormatsResponse)
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
- response_type_map: dict[str, type] = {
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(self, raw_result: TaskResult[Any], response_type: type[T]) -> TaskResult[T]:
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
@@ -0,0 +1,38 @@
1
+ """Test helpers for AdCP client library.
2
+
3
+ Provides pre-configured test agents for examples and quick testing.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from adcp.testing.test_helpers import (
9
+ CREATIVE_AGENT_CONFIG,
10
+ TEST_AGENT_A2A_CONFIG,
11
+ TEST_AGENT_A2A_NO_AUTH_CONFIG,
12
+ TEST_AGENT_MCP_CONFIG,
13
+ TEST_AGENT_MCP_NO_AUTH_CONFIG,
14
+ TEST_AGENT_TOKEN,
15
+ create_test_agent,
16
+ creative_agent,
17
+ test_agent,
18
+ test_agent_a2a,
19
+ test_agent_a2a_no_auth,
20
+ test_agent_client,
21
+ test_agent_no_auth,
22
+ )
23
+
24
+ __all__ = [
25
+ "test_agent",
26
+ "test_agent_a2a",
27
+ "test_agent_no_auth",
28
+ "test_agent_a2a_no_auth",
29
+ "creative_agent",
30
+ "test_agent_client",
31
+ "create_test_agent",
32
+ "TEST_AGENT_TOKEN",
33
+ "TEST_AGENT_MCP_CONFIG",
34
+ "TEST_AGENT_A2A_CONFIG",
35
+ "TEST_AGENT_MCP_NO_AUTH_CONFIG",
36
+ "TEST_AGENT_A2A_NO_AUTH_CONFIG",
37
+ "CREATIVE_AGENT_CONFIG",
38
+ ]
@@ -0,0 +1,311 @@
1
+ """Test agent helpers for easy examples and quick testing.
2
+
3
+ These provide pre-configured access to AdCP's public test agent.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any
9
+
10
+ from adcp.client import ADCPClient, ADCPMultiAgentClient
11
+ from adcp.types.core import AgentConfig, Protocol
12
+
13
+ # Public test agent auth token
14
+ # This token is public and rate-limited, for testing/examples only.
15
+ TEST_AGENT_TOKEN = "1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ"
16
+
17
+ # Public test agent configuration - MCP protocol
18
+ TEST_AGENT_MCP_CONFIG = AgentConfig(
19
+ id="test-agent-mcp",
20
+ agent_uri="https://test-agent.adcontextprotocol.org/mcp/",
21
+ protocol=Protocol.MCP,
22
+ auth_token=TEST_AGENT_TOKEN,
23
+ )
24
+
25
+ # Public test agent configuration - A2A protocol
26
+ TEST_AGENT_A2A_CONFIG = AgentConfig(
27
+ id="test-agent-a2a",
28
+ agent_uri="https://test-agent.adcontextprotocol.org",
29
+ protocol=Protocol.A2A,
30
+ auth_token=TEST_AGENT_TOKEN,
31
+ )
32
+
33
+ # Public test agent configuration (no auth) - MCP protocol
34
+ TEST_AGENT_MCP_NO_AUTH_CONFIG = AgentConfig(
35
+ id="test-agent-mcp-no-auth",
36
+ agent_uri="https://test-agent.adcontextprotocol.org/mcp/",
37
+ protocol=Protocol.MCP,
38
+ )
39
+
40
+ # Public test agent configuration (no auth) - A2A protocol
41
+ TEST_AGENT_A2A_NO_AUTH_CONFIG = AgentConfig(
42
+ id="test-agent-a2a-no-auth",
43
+ agent_uri="https://test-agent.adcontextprotocol.org",
44
+ protocol=Protocol.A2A,
45
+ )
46
+
47
+ # Reference creative agent configuration - MCP protocol
48
+ # No authentication required for the reference creative agent
49
+ CREATIVE_AGENT_CONFIG = AgentConfig(
50
+ id="creative-agent",
51
+ agent_uri="https://creative.adcontextprotocol.org/mcp",
52
+ protocol=Protocol.MCP,
53
+ )
54
+
55
+
56
+ def _create_test_agent_client() -> ADCPClient:
57
+ """Create pre-configured test agent client using MCP protocol.
58
+
59
+ Returns:
60
+ ADCPClient instance configured for the public test agent
61
+
62
+ Note:
63
+ This agent is rate-limited and intended for testing/examples only.
64
+ The auth token is public and may be rotated without notice.
65
+ DO NOT use in production applications.
66
+ """
67
+ return ADCPClient(TEST_AGENT_MCP_CONFIG)
68
+
69
+
70
+ def _create_test_agent_a2a_client() -> ADCPClient:
71
+ """Create pre-configured test agent client using A2A protocol.
72
+
73
+ Returns:
74
+ ADCPClient instance configured for the public test agent
75
+
76
+ Note:
77
+ This agent is rate-limited and intended for testing/examples only.
78
+ The auth token is public and may be rotated without notice.
79
+ DO NOT use in production applications.
80
+ """
81
+ return ADCPClient(TEST_AGENT_A2A_CONFIG)
82
+
83
+
84
+ def _create_test_agent_no_auth_client() -> ADCPClient:
85
+ """Create pre-configured test agent client (no auth) using MCP protocol.
86
+
87
+ Returns:
88
+ ADCPClient instance configured for the public test agent without authentication
89
+
90
+ Note:
91
+ This agent is rate-limited and intended for testing scenarios where no auth is provided.
92
+ Useful for testing behavior differences between authenticated and unauthenticated requests.
93
+ DO NOT use in production applications.
94
+ """
95
+ return ADCPClient(TEST_AGENT_MCP_NO_AUTH_CONFIG)
96
+
97
+
98
+ def _create_test_agent_a2a_no_auth_client() -> ADCPClient:
99
+ """Create pre-configured test agent client (no auth) using A2A protocol.
100
+
101
+ Returns:
102
+ ADCPClient instance configured for the public test agent without authentication
103
+
104
+ Note:
105
+ This agent is rate-limited and intended for testing scenarios where no auth is provided.
106
+ Useful for testing behavior differences between authenticated and unauthenticated requests.
107
+ DO NOT use in production applications.
108
+ """
109
+ return ADCPClient(TEST_AGENT_A2A_NO_AUTH_CONFIG)
110
+
111
+
112
+ def _create_creative_agent_client() -> ADCPClient:
113
+ """Create pre-configured creative agent client.
114
+
115
+ Returns:
116
+ ADCPClient instance configured for the reference creative agent
117
+
118
+ Note:
119
+ The reference creative agent is public and requires no authentication.
120
+ It provides creative preview functionality for testing and examples.
121
+ """
122
+ return ADCPClient(CREATIVE_AGENT_CONFIG)
123
+
124
+
125
+ def _create_test_multi_agent_client() -> ADCPMultiAgentClient:
126
+ """Create multi-agent client with both test agents configured.
127
+
128
+ Returns:
129
+ ADCPMultiAgentClient with both MCP and A2A test agents
130
+
131
+ Note:
132
+ This client is rate-limited and intended for testing/examples only.
133
+ DO NOT use in production applications.
134
+ """
135
+ return ADCPMultiAgentClient([TEST_AGENT_MCP_CONFIG, TEST_AGENT_A2A_CONFIG])
136
+
137
+
138
+ # Pre-configured test agent client using MCP protocol.
139
+ # Ready to use for examples, documentation, and quick testing.
140
+ #
141
+ # Example:
142
+ # ```python
143
+ # from adcp.testing import test_agent
144
+ #
145
+ # # Simple get_products call
146
+ # result = await test_agent.get_products(
147
+ # GetProductsRequest(
148
+ # brief="Coffee subscription service for busy professionals",
149
+ # promoted_offering="Premium monthly coffee deliveries"
150
+ # )
151
+ # )
152
+ #
153
+ # if result.success:
154
+ # print(f"Found {len(result.data.products)} products")
155
+ # ```
156
+ #
157
+ # Note:
158
+ # This agent is rate-limited and intended for testing/examples only.
159
+ # The auth token is public and may be rotated without notice.
160
+ # DO NOT use in production applications.
161
+ test_agent: ADCPClient = _create_test_agent_client()
162
+
163
+ # Pre-configured test agent client using A2A protocol.
164
+ # Identical functionality to test_agent but uses A2A instead of MCP.
165
+ #
166
+ # Example:
167
+ # ```python
168
+ # from adcp.testing import test_agent_a2a
169
+ #
170
+ # result = await test_agent_a2a.get_products(
171
+ # GetProductsRequest(
172
+ # brief="Sustainable fashion brands",
173
+ # promoted_offering="Eco-friendly clothing"
174
+ # )
175
+ # )
176
+ # ```
177
+ #
178
+ # Note:
179
+ # This agent is rate-limited and intended for testing/examples only.
180
+ # The auth token is public and may be rotated without notice.
181
+ # DO NOT use in production applications.
182
+ test_agent_a2a: ADCPClient = _create_test_agent_a2a_client()
183
+
184
+ # Pre-configured test agent client (no auth) using MCP protocol.
185
+ # Useful for testing scenarios where authentication is not provided,
186
+ # such as testing how agents handle unauthenticated requests or
187
+ # comparing behavior between authenticated and unauthenticated calls.
188
+ #
189
+ # Example:
190
+ # ```python
191
+ # from adcp.testing import test_agent_no_auth
192
+ #
193
+ # # Test behavior without authentication
194
+ # result = await test_agent_no_auth.get_products(
195
+ # GetProductsRequest(
196
+ # brief="Coffee subscription service",
197
+ # promoted_offering="Premium monthly coffee"
198
+ # )
199
+ # )
200
+ # ```
201
+ #
202
+ # Note:
203
+ # This agent is rate-limited and intended for testing/examples only.
204
+ # DO NOT use in production applications.
205
+ test_agent_no_auth: ADCPClient = _create_test_agent_no_auth_client()
206
+
207
+ # Pre-configured test agent client (no auth) using A2A protocol.
208
+ # Identical functionality to test_agent_no_auth but uses A2A instead of MCP.
209
+ #
210
+ # Example:
211
+ # ```python
212
+ # from adcp.testing import test_agent_a2a_no_auth
213
+ #
214
+ # # Test A2A behavior without authentication
215
+ # result = await test_agent_a2a_no_auth.get_products(
216
+ # GetProductsRequest(
217
+ # brief="Sustainable fashion brands",
218
+ # promoted_offering="Eco-friendly clothing"
219
+ # )
220
+ # )
221
+ # ```
222
+ #
223
+ # Note:
224
+ # This agent is rate-limited and intended for testing/examples only.
225
+ # DO NOT use in production applications.
226
+ test_agent_a2a_no_auth: ADCPClient = _create_test_agent_a2a_no_auth_client()
227
+
228
+ # Pre-configured reference creative agent.
229
+ # Provides creative preview functionality without authentication.
230
+ #
231
+ # Example:
232
+ # ```python
233
+ # from adcp.testing import creative_agent
234
+ # from adcp.types.generated import PreviewCreativeRequest
235
+ #
236
+ # result = await creative_agent.preview_creative(
237
+ # PreviewCreativeRequest(
238
+ # manifest={
239
+ # "format_id": "banner_300x250",
240
+ # "assets": {...}
241
+ # }
242
+ # )
243
+ # )
244
+ # ```
245
+ #
246
+ # Note:
247
+ # The reference creative agent is public and requires no authentication.
248
+ # Perfect for testing creative rendering and preview functionality.
249
+ creative_agent: ADCPClient = _create_creative_agent_client()
250
+
251
+ # Multi-agent client with both test agents configured.
252
+ # Useful for testing multi-agent patterns and protocol comparisons.
253
+ #
254
+ # Example:
255
+ # ```python
256
+ # from adcp.testing import test_agent_client
257
+ #
258
+ # # Access individual agents
259
+ # mcp_agent = test_agent_client.agent("test-agent-mcp")
260
+ # a2a_agent = test_agent_client.agent("test-agent-a2a")
261
+ #
262
+ # # Use for parallel operations
263
+ # results = await test_agent_client.get_products(
264
+ # GetProductsRequest(
265
+ # brief="Premium coffee brands",
266
+ # promoted_offering="Artisan coffee"
267
+ # )
268
+ # )
269
+ # ```
270
+ #
271
+ # Note:
272
+ # This client is rate-limited and intended for testing/examples only.
273
+ # DO NOT use in production applications.
274
+ test_agent_client: ADCPMultiAgentClient = _create_test_multi_agent_client()
275
+
276
+
277
+ def create_test_agent(**overrides: Any) -> AgentConfig:
278
+ """Create a custom test agent configuration.
279
+
280
+ Useful when you need to modify the default test agent setup.
281
+
282
+ Args:
283
+ **overrides: Keyword arguments to override default config values
284
+
285
+ Returns:
286
+ Complete agent configuration
287
+
288
+ Example:
289
+ ```python
290
+ from adcp.testing import create_test_agent
291
+ from adcp.client import ADCPClient
292
+
293
+ # Use default test agent with custom ID
294
+ config = create_test_agent(id="my-test-agent")
295
+ client = ADCPClient(config)
296
+ ```
297
+
298
+ Example:
299
+ ```python
300
+ # Use A2A protocol instead of MCP
301
+ from adcp.types.core import Protocol
302
+
303
+ config = create_test_agent(
304
+ protocol=Protocol.A2A,
305
+ agent_uri="https://test-agent.adcontextprotocol.org"
306
+ )
307
+ ```
308
+ """
309
+ base_config = TEST_AGENT_MCP_CONFIG.model_dump()
310
+ base_config.update(overrides)
311
+ return AgentConfig(**base_config)