Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ec9d0aa
feat(sdk): generate Python SDK + emit vocab constants for TS and Python
KonstantinMirin Jun 19, 2026
6afb052
docs(readme): document the Python SDK and cross-language vocab constants
KonstantinMirin Jun 19, 2026
4558026
refactor(vocab): drive the axis→package mapping from the proto, drop …
KonstantinMirin Jun 19, 2026
287516d
refactor(proto): discriminators as standard enum.not_in:[0], not cust…
KonstantinMirin Jun 19, 2026
5e48f80
refactor(proto): format rules as standard string.pattern, not custom CEL
KonstantinMirin Jun 19, 2026
51f03c9
chore(sdk): discard the protobuf-native TS/Python SDKs
KonstantinMirin Jun 19, 2026
cd86627
feat(sdk): types export — generate Pydantic + Zod from the proto via …
KonstantinMirin Jun 19, 2026
78adfbd
feat(sdk): descriptor-driven types export — WireModel base, clean nam…
KonstantinMirin Jun 19, 2026
8ccc7e6
fix(sdk): collapse double-special anyOf to number (drop Rate/UnitCost…
KonstantinMirin Jun 19, 2026
21e8602
fix(sdk): money is Decimal in Python, never float
KonstantinMirin Jun 19, 2026
2c43b2f
docs(sdk): drop name-rationale comments from the WireModel base + script
KonstantinMirin Jun 19, 2026
82ff392
feat(proto)!: money is an exact decimal string, never a float
KonstantinMirin Jun 19, 2026
e1a9a3c
docs: money values are decimal strings in examples
KonstantinMirin Jun 19, 2026
075898c
fix(sdk): clean int64 DX in the types export (int, not int|str)
KonstantinMirin Jun 19, 2026
1d07ac2
ci: pin tool versions so the regenerate-and-diff gates are reproducible
KonstantinMirin Jun 19, 2026
f777d22
fix(sdk): deterministic $defs order so the types-export drift gate is…
KonstantinMirin Jun 19, 2026
8433f87
Merge remote-tracking branch 'origin/feature/protocol-unification' in…
KonstantinMirin Jun 19, 2026
5b59e71
feat(sdk-types): cross-language validation parity + close client enfo…
KonstantinMirin Jun 19, 2026
c8344a7
test(conformance): canonical proto-JSON round-trip interop gate
KonstantinMirin Jun 20, 2026
f82ce09
Merge feature/protocol-unification into feature/sdk-libraries
KonstantinMirin Jun 22, 2026
10d707b
Merge branch 'main' into feature/sdk-libraries
KonstantinMirin Jul 1, 2026
6fffae0
fix(sdk-types): enforce money wire pattern in generated Pydantic
KonstantinMirin Jul 2, 2026
f754230
fix(sdk-types): restore forward-compatible unknown-field handling
KonstantinMirin Jul 2, 2026
fe8bc39
test(sdk-types): pin byte-exact money round-trip in Pydantic
KonstantinMirin Jul 2, 2026
f8219d1
build(sdk-types): pin the types-export toolchain for deterministic ou…
KonstantinMirin Jul 2, 2026
528b7a9
docs: correct the SDKs section to match the generated types export
KonstantinMirin Jul 2, 2026
31d1538
docs(changelog): record the breaking money double->string change
KonstantinMirin Jul 3, 2026
4656217
docs: fix generation claim in the Python types-export pyproject
KonstantinMirin Jul 3, 2026
9459f69
docs: show money as decimal strings in the walkthrough pseudocode
KonstantinMirin Jul 3, 2026
67c2016
docs: scope the closed-enum note to not_in:[0] discriminators only
KonstantinMirin Jul 3, 2026
c220de1
docs: cosmetic cleanup across the types export
KonstantinMirin Jul 3, 2026
5fbf5b3
build: make ci-local the true single local gate (add sdk-types)
KonstantinMirin Jul 3, 2026
9a06b79
fix(proto): bound money strings with max_len = 32
KonstantinMirin Jul 3, 2026
91cc051
fix(conformance): fail loudly on an unhandled numeric required-rule
KonstantinMirin Jul 3, 2026
6981115
build(sdk-types): harden the install steps
KonstantinMirin Jul 3, 2026
958b23f
test(conformance): add the missing parity corpus mutant classes
KonstantinMirin Jul 3, 2026
89c98ca
fix(sdk-types): default a bytes field to empty string, not null
KonstantinMirin Jul 3, 2026
61315ff
docs: remove dead poc/ links
KonstantinMirin Jul 3, 2026
b437197
build(sdk-types): hash-lock the pip installs with --require-hashes
KonstantinMirin Jul 3, 2026
cd6effe
feat(sdk-types)!: standardize the wire on snake_case proto-JSON
KonstantinMirin Jul 3, 2026
84c750c
docs: recase remaining camelCase proto fields to snake_case + lock th…
KonstantinMirin Jul 6, 2026
608db17
test(sdk-types): guard the generated client field names are snake_case
KonstantinMirin Jul 6, 2026
5aa186c
docs: fix proto reference in the wire-format note (Offer.signature)
KonstantinMirin Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/proto-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ jobs:
# (informational, pre-v1). The remark docs-guard step self-skips here because
# node_modules is absent; docs-ci.yml owns the docs build.
- name: gating sequence (scripts/ci-local.sh)
run: ./scripts/ci-local.sh
# sdk-types-ci.yml owns the Pydantic/Zod gate (path-filtered), so skip it here
# and keep this job the proto mirror. Locally ci-local.sh runs both.
run: RAMP_CI_SKIP_SDK_TYPES=1 ./scripts/ci-local.sh
75 changes: 75 additions & 0 deletions .github/workflows/sdk-types-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: sdk-types-ci

# Drift gate for the generated TYPES EXPORT (Pydantic + Zod). Regenerates from the
# proto via scripts/gen-sdk-types.sh and fails if the committed output differs — the
# same regenerate-and-diff contract as the gen/ drift gate, for the SDK types. Runs
# only when the proto, the descriptor, the generators, or the committed output change.
on:
push:
branches: [main]
paths: &paths
- 'proto/**'
- 'gen/descriptor.binpb'
- 'scripts/gen-sdk-types.sh'
- 'scripts/check-canonical.sh'
- 'scripts/sdk-types/**'
- 'conformance/**'
- 'gen/python/**'
- 'gen/ts/**'
- '.github/workflows/sdk-types-ci.yml'
pull_request:
paths: *paths

jobs:
sdk-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- uses: bufbuild/buf-setup-action@v1
with:
version: 1.66.1
github_token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: actions/setup-node@v4
with:
node-version: 22

- name: regenerate types export
run: ./scripts/gen-sdk-types.sh

# Only the two generated files; wire/base.py is hand-written (the seam) and the
# vocab constants are generated by buf, gated separately.
- name: assert no drift
run: |
if ! git diff --exit-code -- gen/python/wire/models.py gen/ts/wire/schemas.ts; then
echo "::error::Generated types export is out of sync with the proto. Run scripts/gen-sdk-types.sh and commit gen/python/wire/models.py + gen/ts/wire/schemas.ts."
exit 1
fi

# Cross-language validation parity: the generated Pydantic models and Zod schemas
# must reach the SAME verdict as Go protovalidate on every case in the generated
# corpus (conformance/corpus/cases.json) — proving the proto -> JSON Schema ->
# client pipeline carries every field-level rule. The corpus is pinned to
# protovalidate by the Go conformance suite (proto-ci) and gated for drift there.
- name: Python (Pydantic) parity
run: |
python -m pip install -q "pydantic>=2.0" pytest
PYTHONPATH=gen/python pytest gen/python/tests -q

- name: TypeScript (Zod) parity
working-directory: gen/ts
run: |
npm install
npm test

# Canonical proto-JSON interop: each client re-serializes every valid corpus
# instance and the emission, read back through Go protojson, must decode to the
# same proto message as the original (incl. Timestamp/Duration). Reuses the
# .sdk-types-work venv + zod provisioned by gen-sdk-types.sh above.
- name: Canonical proto-JSON round-trip
run: ./scripts/check-canonical.sh
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ research-*.md

# Local agent/review scratch — never ship AI-review artifacts in the protocol repo
.claude/
__pycache__/
.sdk-types-work/
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,44 @@ A working multi-language stack — Exchange (Go), Broker (Go), Edge (TypeScript)

## SDKs

All three languages are generated from `proto/`: Go is native protobuf + Connect via
`buf generate` (it is the server/runtime); the Python and TypeScript **types exports**
— Pydantic models and Zod schemas — are generated from the same proto via JSON Schema
by `scripts/gen-sdk-types.sh` (the two real consumers, the Python MCP shim and the
TypeScript edge worker, cannot use protobuf natively). All three carry **registered
vocabulary constants** per axis (`pricingunits`, `quotametrics`, `functiontokens`,
`geographytokens`, `usertypes`) so consumers use typed constants and an
`IsRegistered`/`isRegistered`/`is_registered` membership check instead of magic
strings. The vocab is emitted from the single `(ramp.v1.vocab)` source in one pass, so
the three languages cannot drift from each other.

### Go

```go
import (
rampv1 "github.com/RAMP-Protocol/protocol/gen/go/ramp/v1"
"github.com/RAMP-Protocol/protocol/gen/go/ramp/v1/rampv1connect"
"github.com/RAMP-Protocol/protocol/gen/go/vocab/pricingunits"
)
```

### TypeScript

TypeScript message types and a Connect client are generated under [`gen/ts/`](gen/ts) (Protobuf-ES + Connect-ES); the [reference implementation](https://github.com/RAMP-Protocol/reference-implementation) shows them in use.
Zod schemas for every message are generated under [`gen/ts/wire/schemas.ts`](gen/ts/wire/schemas.ts) (validated message types; the edge worker uses them for request validation), with vocabulary constants under [`gen/ts/vocab/`](gen/ts/vocab); the [reference implementation](https://github.com/RAMP-Protocol/reference-implementation) shows them in use.

```typescript
import { OfferSchema } from "@ramp-protocol/sdk/wire/schemas";
import { pricingunits } from "@ramp-protocol/sdk/vocab/pricingunits";
```

### Python

Pydantic v2 models for every message (extending the hand-written `wire.base.WireModel` seam) plus vocabulary constants are generated under [`gen/python/`](gen/python) (`pip install .` from that directory; see its [README](gen/python/README.md)).

```python
from wire.models import Offer, Pricing
from vocab import pricingunits
```

## License

Expand Down
Loading
Loading