Skip to content

Commit 74671d0

Browse files
bokelleyclaude
andcommitted
feat(decisioning): re-export SignalsFeatures + ContentStandards capability sub-models
Closes the re-export gap flagged by the code-reviewer in adcontextprotocol#484 (post-merge polish issue adcontextprotocol#485 P1). Two capability sub-models were reachable via Pydantic nested construction but not surfaced through :mod:`adcp.decisioning.capabilities`, forcing adopters to dig into ``generated_poc`` for deep declarations. * ``SignalsFeatures`` — codegen name ``Features2`` (numbered because ``Features`` already names the media_buy features block). Surfaced under a stable adopter-facing name so ``Signals(features=...)`` declarations read cleanly. * ``ContentStandards`` — type of ``MediaBuy.content_standards``. Collides with ``adcp.types.ContentStandards`` (unrelated wire model on the content-standards protocol), so disambiguated via ``CapabilitiesContentStandards`` in :mod:`adcp.types.capabilities` and re-aliased back to the wire-spec name within :mod:`adcp.decisioning.capabilities`. Same pattern as ``Account`` / ``MediaBuy`` / ``Creative``. One new regression test guards both re-exports against future drift. Refs adcontextprotocol#485 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f5cd8cf commit 74671d0

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

src/adcp/decisioning/capabilities.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class HelloSeller(DecisioningPlatform):
7777
RequestSigning,
7878
SiCapabilities,
7979
Signals,
80+
SignalsFeatures,
8081
Specialism,
8182
SponsoredIntelligence,
8283
SupportedProtocol,
@@ -90,6 +91,9 @@ class HelloSeller(DecisioningPlatform):
9091
from adcp.types.capabilities import (
9192
CapabilitiesAccount as Account,
9293
)
94+
from adcp.types.capabilities import (
95+
CapabilitiesContentStandards as ContentStandards,
96+
)
9397
from adcp.types.capabilities import (
9498
CapabilitiesCreative as Creative,
9599
)
@@ -110,6 +114,7 @@ class HelloSeller(DecisioningPlatform):
110114
"ComplianceTesting",
111115
"Components",
112116
"CompromiseNotification",
117+
"ContentStandards",
113118
"ConversionTracking",
114119
"Creative",
115120
"CreativeSpecs",
@@ -134,6 +139,7 @@ class HelloSeller(DecisioningPlatform):
134139
"RequestSigning",
135140
"SiCapabilities",
136141
"Signals",
142+
"SignalsFeatures",
137143
"Specialism",
138144
"SponsoredIntelligence",
139145
"SupportedProtocol",

src/adcp/types/capabilities.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,27 @@
9797
from adcp.types.generated_poc.bundled.protocol.get_adcp_capabilities_response import (
9898
Capabilities as SiCapabilities,
9999
)
100+
101+
# ``ContentStandards`` collides with ``adcp.types.ContentStandards`` (the
102+
# unrelated wire model on the content-standards protocol). Same
103+
# disambiguation pattern as ``Account`` / ``MediaBuy`` / ``Creative`` —
104+
# imported here under a ``Capabilities*`` alias and re-aliased back to
105+
# the wire-spec name within :mod:`adcp.decisioning.capabilities`.
106+
from adcp.types.generated_poc.bundled.protocol.get_adcp_capabilities_response import (
107+
ContentStandards as CapabilitiesContentStandards,
108+
)
100109
from adcp.types.generated_poc.bundled.protocol.get_adcp_capabilities_response import (
101110
Creative as CapabilitiesCreative,
102111
)
103112

113+
# ``Features2`` is the codegen name for the ``Signals.features`` type
114+
# (numbered because ``Features`` already names the media_buy features
115+
# block at line 142 of the generated module). Surface under a stable
116+
# adopter-facing name so signals declarations read cleanly.
117+
from adcp.types.generated_poc.bundled.protocol.get_adcp_capabilities_response import (
118+
Features2 as SignalsFeatures,
119+
)
120+
104121
# ``Idempotency`` ships as a ``oneOf`` on the wire (``IdempotencySupported``
105122
# vs ``IdempotencyUnsupported``) — the codegen names them ``Idempotency``
106123
# and ``Idempotency3`` (with the numbered variant covering the
@@ -126,6 +143,7 @@
126143
"Avatar",
127144
"Brand",
128145
"CapabilitiesAccount",
146+
"CapabilitiesContentStandards",
129147
"CapabilitiesCreative",
130148
"CapabilitiesMediaBuy",
131149
"Commerce",
@@ -152,8 +170,9 @@
152170
"NegativeKeywords",
153171
"Portfolio",
154172
"RequestSigning",
155-
"Signals",
156173
"SiCapabilities",
174+
"Signals",
175+
"SignalsFeatures",
157176
"Specialism",
158177
"SponsoredIntelligence",
159178
"SupportedProtocol",

tests/test_decisioning_capabilities_submodule.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,37 @@ def test_submodule_all_matches_imports() -> None:
134134
assert hasattr(caps, name), f"__all__ lists {name!r} but it is not importable"
135135

136136

137+
def test_signals_features_and_content_standards_re_exported() -> None:
138+
"""``SignalsFeatures`` (codegen ``Features2`` for ``Signals.features``)
139+
and ``ContentStandards`` (the ``MediaBuy.content_standards`` type, which
140+
collides with the unrelated wire ``adcp.types.ContentStandards``) are
141+
surfaced through :mod:`adcp.decisioning.capabilities` so adopters
142+
declaring deep Signals / MediaBuy blocks don't have to dig into
143+
``generated_poc``.
144+
"""
145+
from adcp.decisioning.capabilities import (
146+
ContentStandards,
147+
MediaBuy,
148+
Signals,
149+
SignalsFeatures,
150+
)
151+
from adcp.types import ContentStandards as WireContentStandards
152+
153+
# Content-standards collision guard — same pattern as Account / MediaBuy / Creative.
154+
assert ContentStandards is not WireContentStandards
155+
assert ContentStandards.__name__ == "ContentStandards"
156+
157+
# SignalsFeatures usable on a Signals declaration.
158+
sig = Signals(features=SignalsFeatures(catalog_signals=True))
159+
assert sig.features is not None
160+
assert sig.features.catalog_signals is True
161+
162+
# ContentStandards usable on a MediaBuy declaration.
163+
mb = MediaBuy(content_standards=ContentStandards(supports_local_evaluation=True))
164+
assert mb.content_standards is not None
165+
assert mb.content_standards.supports_local_evaluation is True
166+
167+
137168
def test_decisioning_capabilities_accepts_structured_fields() -> None:
138169
"""``DecisioningCapabilities`` carries instances of the wire-spec
139170
capability sub-models. Validates the dataclass widening (commit 2 of

0 commit comments

Comments
 (0)