You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(types): lazy, typed public surface with curated partial modules (#963)
Make `import adcp` and `import adcp.types` lightweight (PEP 562 lazy surface),
add curated partial type modules (adcp.types.media_buy/creative/signals/
protocol/buyer/seller), and keep full mypy/IDE typing — with zero wire-shape
change and back-compat preserved (from adcp.types import Product still works).
- import adcp ~3.25s -> ~2ms; generated schema graph + client/server/a2a load
lazily, on first access to a type symbol; importlib.metadata version lookup
deferred too.
- Adopter typing preserved and improved: typos like `from adcp import Prodct`
are flagged (runtime __getattr__ under `if not TYPE_CHECKING`); unknown names
fail fast without building the graph.
- Fixed a latent webhooks <-> webhook_sender import cycle the lazy facade
exposed; docs/examples no longer instruct importing the internal generated
layer; README/llms.txt/CONTRIBUTING updated.
5839 tests, ruff, and mypy --strict (incl. adopter fixtures) all green.
Official Python SDK for the **Ad Context Protocol (AdCP)**. Build and connect to advertising agents that work synchronously OR asynchronously with the same code.
8
8
9
+
## Choose your path
10
+
11
+
This README serves both sides of an AdCP integration. Jump to what you're doing:
12
+
13
+
-**Connect as a buyer** → [Quick Start: Test Helpers](#quick-start-test-helpers) and [Quick Start: Distributed Operations](#quick-start-distributed-operations). Entry point: `from adcp import ADCPClient, AgentConfig`; start with the `client.simple.*` API.
14
+
-**Build a seller / agent** → [Building an AdCP Agent](#building-an-adcp-agent). Entry point: `from adcp.server import ADCPHandler, serve`.
15
+
-**Understand the type system & imports** → [Type Safety](#type-safety) (import surface, partial modules, cold-start note).
16
+
-**Test against reference agents** → [Quick Start: Test Helpers](#quick-start-test-helpers) and [Test Helpers](#test-helpers). Entry point: `from adcp.testing import test_agent, creative_agent`.
17
+
18
+
## Table of Contents
19
+
20
+
-[Building an AdCP Agent](#building-an-adcp-agent)
The fastest path to a working agent: subclass `ADCPHandler`, use response builders, call `serve()`.
@@ -154,7 +221,7 @@ Pre-configured agents (all include `.simple` accessor):
154
221
155
222
See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.
156
223
157
-
> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.
224
+
> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`), from `adcp.types`, or from a curated partial module (`adcp.types.media_buy`, `.creative`, `.signals`, `.protocol`, `.buyer`, `.seller`) — never from the internal `adcp.types.generated_poc.*` layer. `import adcp` is lightweight; the generated type graph is built only when you import a type.
158
225
159
226
## Quick Start: Distributed Operations
160
227
@@ -209,13 +276,20 @@ async with ADCPMultiAgentClient(
209
276
210
277
## AdCP version support
211
278
212
-
The 5.x line targets AdCP 3.0 stable. v3.1 support lands in SDK 6.0 against
213
-
the 3.1 stable spec — there is no opt-in preview surface in 5.x. If you talk
214
-
to a v3.1+ agent from 5.x, the SDK parses the response through v3.0 types
215
-
(unknown fields are preserved on the model but not surfaced as typed
216
-
attributes) and schema validation is skipped for that version. Track
The 6.x line is built against **AdCP 3.1.0 stable** and natively validates
280
+
both AdCP 3.0 and 3.1 wire shapes. Check the versions at runtime:
281
+
282
+
```python
283
+
import adcp
284
+
285
+
adcp.get_adcp_sdk_version() # SDK package version, e.g. "6.4.1"
286
+
adcp.get_adcp_spec_version() # AdCP spec this build targets, e.g. "3.1.0"
287
+
```
288
+
289
+
If you talk to an agent on a newer spec than this SDK validates, the response
290
+
still parses — unknown fields are preserved on the model (but not surfaced as
291
+
typed attributes) and schema validation is skipped for that version, so
292
+
forward traffic degrades gracefully rather than failing.
219
293
220
294
## Documentation
221
295
@@ -341,7 +415,31 @@ if media_buy.status == MediaBuyStatus.active:
341
415
-**All 9 pricing options**: `CpcPricingOption`, `CpmFixedRatePricingOption`, `VcpmAuctionPricingOption`, etc.
342
416
-**Request/Response types**: All 16 operations with full request/response types
343
417
344
-
For types not on the top-level surface, import from `adcp.types` (e.g., `from adcp.types import AssetStatus`). If a type you need isn't in `adcp.types`, open an issue — we'll add an alias. The `adcp.types.generated_poc.*` modules are internal; class names and module paths shift on every schema regeneration and are not a supported API.
418
+
For types not on the top-level surface, import from `adcp.types` (e.g., `from adcp.types import AssetStatus`), or from one of the curated partial modules that group the types by domain:
419
+
420
+
```python
421
+
from adcp.types.media_buy import CreateMediaBuyRequest, MediaBuyStatus
422
+
from adcp.types.creative import Format, SyncCreativesRequest
423
+
from adcp.types.signals import GetSignalsRequest, SignalTargeting
424
+
from adcp.types.protocol import Error, Pagination, GetTaskStatusRequest
425
+
from adcp.types.buyer import GetProductsRequest, CpmPricingOption
426
+
from adcp.types.seller import Offering, PropertyList, ContentStandards
427
+
```
428
+
429
+
If a type you need isn't in `adcp.types`, open an issue — we'll add an alias. The `adcp.types.generated_poc.*` modules are internal; class names and module paths shift on every schema regeneration and are not a supported API.
430
+
431
+
The six partial modules (`media_buy`, `creative`, `signals`, `protocol`, `buyer`, `seller`) are for curation and discoverability — they group types by domain and give you a smaller import surface. They are **not** a per-domain performance tier: the first AdCP type you touch through any of them builds the same single Pydantic graph.
432
+
433
+
#### Cold start / import performance
434
+
435
+
`import adcp` is lightweight (~2ms) and builds nothing — it does not import pydantic, the A2A SDK, or the client, and it does not construct the type graph. The first time you access *any* AdCP type — through `adcp`, `adcp.types`, or a partial module — the full generated Pydantic graph builds once per process (~1s). There is only one graph; subsequent type access is free.
436
+
437
+
For latency-sensitive cold starts (AWS Lambda, agent tool invocations), warm the graph at startup so the cost lands before your first request:
438
+
439
+
```python
440
+
import adcp.types
441
+
adcp.types.Product # forces the one-time graph build now, not on the hot path
0 commit comments