Skip to content

feat(models): update Python SDK schemas for UCP release 2026-08-25 - #87

Merged
damaz91 merged 2 commits into
Universal-Commerce-Protocol:mainfrom
segiodongo:feat/v825-upgrade
Aug 27, 2026
Merged

feat(models): update Python SDK schemas for UCP release 2026-08-25#87
damaz91 merged 2 commits into
Universal-Commerce-Protocol:mainfrom
segiodongo:feat/v825-upgrade

Conversation

@segiodongo

Copy link
Copy Markdown
Contributor

Description

This PR updates the Python SDK to be fully compatible with the UCP 2026-08-25 specification release.

Key Changes

  1. Schema Preprocessing & Codegen Pipeline:

    • Added list/dict type-safety guards to preprocess_schemas.py for allOf, anyOf, and oneOf merging.
    • Updated normalize_metadata_schemas to dynamically detect and include all UCP metadata response envelopes (including response_catalog_schema and response_location_schema).
    • Extended per-file-ignores in pyproject.toml for generated schemas, preprocessing/postprocessing scripts, and test suites.
  2. Model Regeneration & Reorganization:

    • Regenerated all Pydantic v2 models using ./generate_models.sh 2026-08-25.
    • Common Vertical (ucp_sdk.models.schemas.common): Added models for Location Search & Lookup (location_search.py, location_lookup.py), Loyalty, Payment Terms, AP2 Mandates, Split Payments, and Request Constraints.
    • Shared Primitives (ucp_sdk.models.schemas.common.types): Relocated shared types (amounts, prices, totals, postal address, signals, error/warning messages, pagination, time interval, unit price) from shopping into the common namespace.
    • Shopping Vertical (ucp_sdk.models.schemas.shopping): Updated Cart, Checkout, Order, Catalog Search/Lookup, Buyer Consent, and Permalinks to reference the reorganized common types and new fulfillment destinations.
    • Transports (ucp_sdk.models.schemas.transports): Generated transport bindings for REST, MCP tool calls, JSON-RPC, and embedded/A2A messaging.
    • Service & Profile (ucp_sdk.models.schemas): Added profile.py and updated capability/service definitions.
  3. Documentation & Versioning:

    • Bumped package version to 0.5.0 in pyproject.toml and src/ucp_sdk/__init__.py.
    • Updated README.md package reference table and codegen instructions.
    • Configured .gitignore to ignore temporary codegen artifacts (ucp/, uv.lock).

Verification

  • Linting & Formatting: uv run ruff format and uv run ruff check passed cleanly (0 errors).
  • Unit Tests: uv run python -m unittest discover tests/ passed (88 tests passed, 0 failures).
  • Module Import Health: Dynamically imported all 226 generated model submodules with 0 import errors.

@carolinerg1

Copy link
Copy Markdown

Hi @segiodongo,

Thanks for submitting this PR! Looks like it's failing backward compatibility when regenerating models for 2026-04-08 and Lint - though I see in your description that you ran Lint check (potentially not across all updated files ?). Could you please take a look at these 2 issues. Thanks!

@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Aug 25, 2026
@segiodongo
segiodongo force-pushed the feat/v825-upgrade branch 5 times, most recently from 056e4e8 to 99e5979 Compare August 26, 2026 17:13
segiodongo and others added 2 commits August 27, 2026 13:58
- Update schema preprocessing pipeline for v2026-08-25 response definitions
- Regenerate Pydantic models from UCP release/2026-08-25 JSON schemas
- Restructure models into common, shopping, and transport verticals
- Add Location Search/Lookup, Loyalty, Request Constraints, Permalinks, and Actions models
- Move shared primitives and payment extensions to common namespace
- Bump package version to 0.5.0 in pyproject.toml and update README.md
@damaz91
damaz91 force-pushed the feat/v825-upgrade branch from 99e5979 to bdfb3f6 Compare August 27, 2026 14:09
@carolinerg1 carolinerg1 added devops status:under-review and removed status:needs-triage Signal that the PR is ready for human triage labels Aug 27, 2026
@damaz91
damaz91 merged commit b6f9b91 into Universal-Commerce-Protocol:main Aug 27, 2026
18 checks passed
carolinerg1 pushed a commit that referenced this pull request Sep 1, 2026
* test: fix stale shopping.types import paths in the codegen pipeline suite

The HAVE_SDK import gate at the top of test_codegen_pipeline.py still
imported Description/Totals from ucp_sdk.models.schemas.shopping.types,
paths that moved to ucp_sdk.models.schemas.common.types when #87
(the 2026-08-25 UCP release regen) restructured the schema tree. The
stale paths raise ModuleNotFoundError, which the surrounding
try/except catches and sets HAVE_SDK = False, so every test gated on
HAVE_SDK skips instead of running (unittest reports a skip as OK, so
the suite reads green while ~37% of it never executes).

Fix every stale shopping.types.* reference in the file: description,
totals (+ its request variants), signals (+ its request variants), and
error_response moved to common.types under the same class names, so
those tests now run and pass unmodified. Two targets did not survive
the schema restructuring at all -- card_payment_instrument.Constraints
(a uniqueItems brands field) and merchant_fulfillment_config's nested
additionalProperties:false object (now business_fulfillment_config,
reshaped) -- so their four tests become documented unittest.skip with
the reason recorded in the schema, not silently deleted.

Before: 89 tests, 33 skipped (26 on the HAVE_SDK gate, 7 on
'executing the module needs pydantic', which also reads HAVE_SDK).
After: 89 tests, 4 skipped, all four with a stated schema-shape
reason.

No production code changes; test-only.

* test: add failing coverage for JWK conditional rules and unit scale pin

profile.json's jwk_public_key def carries five if/then rules (two
conditional-required: an EC key needs crv/x/y, an OKP key needs
crv/x; three conditional const-pins matching curve to algorithm:
P-256/ES256, P-384/ES384, Ed25519/EdDSA), and unit.json pins scale to
0 when unit is C62. All six are dropped by the current generator, so
the committed JwkPublicKey and Unit models validate payloads the spec
rejects.

Two independent scanner bugs cause this, isolated here at both the
injector level (schema-scan unit tests against synthetic fixtures)
and the semantic level (against the real committed models):

  (a) find_conditional_required only looks at a branch's own
      properties; an allOf branch carrying just {if, then} with no
      properties of its own (every JWK rule) is invisible, with no
      warning. find_conditional_bounds already threads the enclosing
      object's properties into such branches; find_conditional_required
      never picked up that fix.

  (b) find_conditional_bounds only recognizes the four numeric bound
      keywords (minimum/maximum/exclusiveMinimum/exclusiveMaximum) in
      a then.properties.<field> constraint. unit.json's scale pin and
      all three JWK algorithm pins use const, which is rejected as an
      unsupported shape and dropped (with a warning, unlike (a)).

  (c) Shared: in both scanners, a rule's own documentation title
      (JWK gives each branch a human-readable title, e.g. "EC keys
      carry crv, x, y") overwrites current_class_name via the same
      code path used for real class-defining titles, misattributing
      the rule to a nonexistent class instead of JwkPublicKey.

Kill-rate note: test_schema_scan_skips_else_branches and
test_schema_scan_skips_rules_whose_fields_were_stripped (both
pre-existing) stay green, confirming the new scope-threading and
const-recognition do not loosen the existing else-branch and
stripped-field guards.

RED: 109 tests, 9 failures + 1 error, 4 documented skips (unchanged
from the prior commit). Generator fix and regen follow in separate
commits per repo convention.

* fix(codegen): thread conditional-rule scope and recognize const pins

Three bugs in the postprocessing scanners were dropping all five of
profile.json's jwk_public_key if/then rules and unit.json's C62 scale
pin, letting the generated JwkPublicKey and Unit models validate
payloads the spec rejects:

(a) find_conditional_required only looked at a branch's own
    properties. An allOf branch carrying just {if, then} with no
    properties of its own (every JWK rule is exactly this shape) was
    invisible, silently, with no warning. find_conditional_bounds
    already threads the enclosing object's properties into such
    branches via an enclosing_properties parameter;
    find_conditional_required now does the same.

(b) find_conditional_bounds only recognized the four numeric bound
    keywords (minimum/maximum/exclusiveMinimum/exclusiveMaximum) in a
    then.properties.<field> constraint, so a bare {"const": ...}
    constraint fell outside _BOUND_KEYWORDS and the whole rule
    returned None (unit.json's scale pin, and all three JWK
    curve/algorithm pairings, are const-shaped). _BOUND_KEYWORDS gains
    a "const" entry mapped to not-equal, reusing the existing
    "value <op> limit -> violation" template unchanged; the describe()
    type check is split so const may be a string (JWK's algorithm
    names) while the numeric keywords keep their existing int/float
    requirement.

(c) Shared by both scanners: a rule's own documentation title (JWK
    gives each branch a human-readable one, e.g. "EC keys carry crv,
    x, y") was adopted as current_class_name via the same code path
    used for real class-defining titles, misattributing the rule to a
    nonexistent class instead of the enclosing JwkPublicKey. A new
    _is_bare_conditional_branch() helper recognizes an if/then node
    with no properties of its own as rule documentation, not a type,
    and both scanners now skip title adoption for it.

Fixed at the generator level only (postprocess_models.py); no
generated model files touched in this commit. Regeneration against
the pinned 2026-08-25 UCP schema follows in a separate commit, which
is what turns the six still-red semantic tests green (the four
injector-level unit tests added in the prior commit -- which exercise
the scanners directly against synthetic fixtures, not the committed
models -- already pass).

109 tests, 6 failures (JwkConditionalRulesSemanticTest x5,
UnitScaleSemanticTest x1), 4 documented skips.

* chore(models): regenerate against the pinned 2026-08-25 UCP schema

Regenerates via ./generate_models.sh 2026-08-25 (the same command the
model-drift CI job runs) to pick up the postprocessing fix in the
prior commit. Six files change:

- profile.py: JwkPublicKey gains both a conditional-required validator
  (EC needs crv/x/y, OKP needs crv/x) and a conditional-bounds
  validator (P-256/ES256, P-384/ES384, Ed25519/EdDSA pairing).
- common/types/unit.py (+ its create/update request variants): Unit
  gains a conditional-bounds validator pinning scale to 0 when unit
  is C62.
- common/types/total.py, common/types/totals.py: unchanged behavior,
  picked up only because the conditional-bounds checks dict (embedded
  verbatim in every class using this validator family) now also
  carries the const entry.

Verified:
- Full suite: 109 tests, 0 failures, 4 documented skips (all six new
  tests from the RED commit now pass).
- Double-regen: ran generate_models.sh 2026-08-25 twice; diff -rq
  between both outputs (excluding __pycache__) is empty.
- Kill-test: reverted postprocess_models.py to its pre-fix state,
  regenerated, reinstalled -- the same 9 failures + 1 error from the
  RED commit reappeared verbatim. Restored the fix and regenerated
  again to confirm the suite returns to green.
- pre-commit run on the changed files: clean (ruff, ruff-format,
  codespell, trailing-whitespace, end-of-file-fixer all pass).

Not committed: README.md, which ruff format also reformats (a
pre-existing docstring-code-block spacing drift in main, unrelated to
this fix -- the model-drift CI job only diffs
src/ucp_sdk/models/schemas, so this was never caught there). Left
untouched to keep this diff scoped to the constraint fix.

---------

Co-authored-by: Vishal Katyal <vishal@katyal.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants