feat(examples): dual-emit v1+v2 formats on reference seller (#741 polish)#847
Merged
Conversation
…nce seller Closes the conformance gap between the canonical-formats projection layer (#741) and the reference seller the AdCP storyboard runner targets. Previously, ``examples/seller_agent.py`` published ``Product.format_ids[]`` only (v1) — the SDK's v2 surface shipped but was never exercised end-to-end by the storyboard suite. ## What changed Every product in the reference seller's ``PRODUCTS`` catalog now carries both wire shapes: * **v1**: ``format_ids: [{agent_url, id}]`` (unchanged). * **v2**: ``format_options: [ProductFormatDeclaration]`` with ``format_kind="image"``, sized ``params`` (width/height + ``asset_source`` + ``image_formats``), and ``v1_format_ref`` pointing back at the v1 ``format_id``. A small ``_image_format_options(...)`` helper builds the v2 declaration so each product entry stays a one-keyword addition. The helper docstring documents the dual-emit pattern as the template adopters should follow during the 3.0 → 3.1 migration. ## Why * 3.0 buyers continue reading ``format_ids[]`` from the wire. * 3.1 buyers now see real ``format_options[]`` declarations and can route on ``format_kind`` plus typed ``params``. * The storyboard runner exercises this path end-to-end against the reference seller for the first time. ## Tests 5258 pass locally. ``tests/test_seller_agent_dual_emit.py`` pins 3 invariants over the 6 products: 1. Every product dual-emits. 2. Every v2 ``v1_format_ref`` matches the product's v1 ``format_ids`` (round-trip anchor). 3. SDK projection rebuilds v1 emit from v2 with zero advisories. Refs: #741
There was a problem hiding this comment.
LGTM. The right shape for the 3.0 → 3.1 migration window: v1 format_ids[] stays load-bearing on the wire, v2 format_options[] is purely additive, and v1_format_ref is what makes the SDK's projection round-trip.
Things I checked
- Dict literal produced by
_image_format_options(examples/seller_agent.py:172-208) validates againstProductFormatDeclaration—format_kind="image"is inCanonicalFormatKind(canonical_format_kind.py:11),paramskeys (sizes,asset_source,ssl_required,image_formats) miss every credential-shaped suffix the_reject_credential_shaped_extrasgate (canonical_decl.py:238) screens for. No fail-closed surprise at validation time. - All 6 products dual-emit with a v1↔v2 anchor: each
format_options[0].v1_format_ref[0]matches the product'sformat_ids[0]. The three test invariants (tests/test_seller_agent_dual_emit.py:55, 64, 81) pin exactly that, plus zero advisories from the projection. products_response(PRODUCTS, cache_scope="public")at examples/seller_agent.py:502 and :512 is the wire sink — the new field flows through the standard serializer, no bespoke path.- Test imports the seller via
importlib.util.spec_from_file_locationand cleans upsys.modulesinfinally(test file:46-48). Hermetic; doesn't leak theseller_agentname into other tests. - Happy path unchanged for 3.0 buyers —
format_optionsis additive, no existing field touched.
Minor nits (non-blocking)
- Import reshuffle hitched to the feature commit.
INSECURE_ALLOW_ALLmoved into the mainadcp.serverimport block (examples/seller_agent.py:23) — pure ordering, unrelated to dual-emit. Could have lived in its own commit, but trivial enough not to matter. _image_format_optionsis single-size by construction. Signature takeswidth, heightand emits onesizes[]entry. Fine for the reference catalog, but the name suggests broader applicability — adopters copying it as a template will need to widen it the first time they declare a multi-size declaration. Worth either renaming (_single_size_image_format_options) or accepting asizesiterable when this gets lifted out ofexamples/intoadcp.canonical_formats.fixtures(item D on the deferred backlog).
Approving.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the conformance gap between the canonical-formats projection layer (#741) and the AdCP storyboard runner's reference seller. Half 1 + half 2 of #741 shipped the projection layer; this PR makes
examples/seller_agent.pyactually exercise it.What changed
Every product in
examples/seller_agent.py'sPRODUCTScatalog now carries BOTH wire shapes:format_ids: [{agent_url, id}](unchanged).format_options: [ProductFormatDeclaration]withformat_kind="image", sizedparams(width/height +asset_source+image_formats), andv1_format_refpointing back at the v1format_id.A new
_image_format_options(...)helper builds the v2 declaration; the docstring documents the dual-emit pattern as the template adopters should follow during the 3.0 → 3.1 migration window.Why this matters
format_ids[]from the wire, unchanged.format_options[]declarations — the storyboard runner exercises that path end-to-end against the reference seller for the first time.project_product_to_v1on each product emits refs that round-trip back to the source declaration viafind_declaration_by_v1_format_id.Tests
tests/test_seller_agent_dual_emit.pypins 3 invariants over the 6 products:v1_format_refmatches the product's v1format_ids.Polish PR backlog (remaining)
Tracked as separate PRs after this one:
projection.py→v2_to_v1.pyfor symmetryDivergencediscriminated unionadcp.canonical_formats.fixturespublic modulegroup_declarations_by_producthelper_versions_overlapforward-compat (Argus feat(canonical-formats): public API + v2→v1 projection (#741, half 1 of 2) #841 deferred)Refs: #741