Module adcp.canonical_formats.dialect

Negotiated creative dialect selection for AdCP 3.x.

Functions

def canonical_creatives_capability(capabilities: Any) ‑> bool | None
Expand source code
def canonical_creatives_capability(capabilities: Any) -> bool | None:
    """Read ``media_buy.features.canonical_creatives`` without guessing."""

    root = _as_mapping(capabilities)
    if root is None:
        return None
    media_buy = _as_mapping(root.get("media_buy"))
    features = _as_mapping(media_buy.get("features")) if media_buy else None
    value = features.get("canonical_creatives") if features else None
    return value if isinstance(value, bool) else None

Read media_buy.features.canonical_creatives without guessing.

def resolve_creative_dialect(adcp_version: str,
*,
capabilities: Any = None,
request: Any = None,
legacy_projection_available: bool = False) ‑> CreativeDialect
Expand source code
def resolve_creative_dialect(
    adcp_version: str,
    *,
    capabilities: Any = None,
    request: Any = None,
    legacy_projection_available: bool = False,
) -> CreativeDialect:
    """Apply the normative 3.0/3.1/3.2 canonical-creatives matrix.

    AdCP 3.1 is deliberately evidence-driven. Contradictory or absent evidence
    fails closed, except when the caller has already established an unambiguous
    legacy projection route.
    """

    normalized = normalize_to_release_precision(adcp_version)
    release = normalized.split("-", 1)[0]
    major, minor = (int(part) for part in release.split(".", 1))
    if major != 3:
        raise CreativeDialectError(
            f"canonical creative negotiation only supports AdCP 3.x, got {adcp_version!r}"
        )

    capability = canonical_creatives_capability(capabilities)
    if minor == 0:
        return CreativeDialect.LEGACY
    if minor >= 2:
        if capability is False:
            raise CreativeDialectError(
                "AdCP 3.2+ requires canonical creatives, but the seller advertised "
                "canonical_creatives=false"
            )
        return CreativeDialect.CANONICAL

    if capability is True:
        return CreativeDialect.CANONICAL
    if capability is False:
        return CreativeDialect.LEGACY

    canonical, legacy = _schema_evidence(request)
    # During the 3.1 transition a sender may dual-emit legacy format IDs next
    # to canonical option references. Canonical evidence is authoritative;
    # the legacy fields are compatibility data rather than a contradiction.
    if canonical:
        return CreativeDialect.CANONICAL
    if legacy and not canonical:
        return CreativeDialect.LEGACY
    if legacy_projection_available:
        return CreativeDialect.LEGACY
    raise CreativeDialectError(
        "AdCP 3.1 does not establish a creative dialect: advertise "
        "media_buy.features.canonical_creatives or provide unambiguous "
        "request-local schema evidence"
    )

Apply the normative 3.0/3.1/3.2 canonical-creatives matrix.

AdCP 3.1 is deliberately evidence-driven. Contradictory or absent evidence fails closed, except when the caller has already established an unambiguous legacy projection route.

Classes

class CreativeDialect (*args, **kwds)
Expand source code
class CreativeDialect(str, Enum):
    """Creative identity shape expected at a protocol boundary."""

    LEGACY = "legacy"
    CANONICAL = "canonical"

Creative identity shape expected at a protocol boundary.

Ancestors

  • builtins.str
  • enum.Enum

Class variables

var CANONICAL
var LEGACY
class CreativeDialectError (*args, **kwargs)
Expand source code
class CreativeDialectError(ValueError):
    """Raised when negotiation cannot select a safe creative dialect."""

Raised when negotiation cannot select a safe creative dialect.

Ancestors

  • builtins.ValueError
  • builtins.Exception
  • builtins.BaseException