opedd 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ unit-tests:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install package + dev deps
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Lint with ruff
30
+ run: ruff check src/ tests/
31
+
32
+ - name: Type-check with mypy
33
+ run: mypy src/
34
+
35
+ - name: Run unit tests
36
+ run: pytest tests/test_unit.py -v
37
+
38
+ # Integration tests intentionally NOT run in CI.
39
+ # Per Phase 11 M6 ratification 6: SDK live tests running autonomously in CI
40
+ # could pollute usage_records, distort metered-publisher-payouts aggregation,
41
+ # fire production API calls with founder identity at scale. Integration suite
42
+ # is documented in RELEASE.md as "run locally with your JWT before each SDK
43
+ # release."
opedd-0.1.0/.gitignore ADDED
@@ -0,0 +1,55 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+
25
+ # Virtual env
26
+ .venv/
27
+ venv/
28
+ ENV/
29
+ env/
30
+
31
+ # Type checker / linter caches
32
+ .mypy_cache/
33
+ .ruff_cache/
34
+ .pytest_cache/
35
+ .tox/
36
+ .nox/
37
+ htmlcov/
38
+ .coverage
39
+ .coverage.*
40
+ nosetests.xml
41
+ coverage.xml
42
+ *.cover
43
+ .hypothesis/
44
+
45
+ # IDE
46
+ .vscode/
47
+ .idea/
48
+ *.swp
49
+ *.swo
50
+ .DS_Store
51
+
52
+ # Secrets
53
+ .env
54
+ .env.*
55
+ !.env.example
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to opedd-python are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] — 2026-05-15
8
+
9
+ Initial public release. Phase 11 M6 ship.
10
+
11
+ ### Added
12
+
13
+ - `Opedd` client class with 5 namespaces:
14
+ - `client.content.get(article_id)` — fetch licensed article (`GET /content-delivery`)
15
+ - `client.feed.list(since, cursor, limit)` — paginated JSON catalog (`GET /enterprise-license?format=json`)
16
+ - `client.feed.stream_ndjson(since, cursor, limit)` — NDJSON streaming generator (`GET /enterprise-license?format=ndjson`)
17
+ - `client.audit.events(from_, to, event_type, cursor, limit)` — per-event audit browse (`GET /buyer-audit`)
18
+ - `client.compliance.report(from_, to, cursor)` — procurement-defense dossier (`GET /buyer-compliance-report`)
19
+ - `client.licenses.purchase(...)` — enterprise license purchase (`POST /enterprise-license`)
20
+ - `client.licenses.list()` — buyer profile + masked key list (`GET /buyer-account`)
21
+ - Three credential modes:
22
+ - `buyer_token` (bearer) for `/content-delivery`
23
+ - `access_key` (query param) for `/enterprise-license` GET feed
24
+ - `buyer_jwt` (Supabase session) for `/buyer-audit` + `/buyer-compliance-report` + `/buyer-account`
25
+ - Env-var fallbacks: `OPEDD_BUYER_TOKEN`, `OPEDD_BUYER_JWT`, `OPEDD_ACCESS_KEY`, `OPEDD_BASE_URL`
26
+ - `Opedd.from_access_key(access_key, buyer_email)` constructor that exchanges `eak_*` for a bearer token via `POST /enterprise-auth`
27
+ - Context manager support (`with Opedd(...) as client:` closes the underlying HTTP pool)
28
+ - Typed exception hierarchy: `OpeddError`, `OpeddAuthError`, `OpeddNotFoundError`, `OpeddValidationError`, `OpeddRateLimitError`, `OpeddServerError`. Each carries `status_code`, `request_id`, `body`. `OpeddRateLimitError` additionally carries `retry_after_seconds`.
29
+ - NDJSON streaming with auto-pagination across `_meta.next_cursor` until `_meta.truncated == false`.
30
+ - 29 unit tests with mocked HTTPX (`pytest tests/test_unit.py`).
31
+ - Integration test suite gated on `pytest --integration` flag (manual local execution before release; not run in CI per institutional risk discipline).
32
+ - Schema version pin: `phase-11-m4`. Surfaced as `opedd.__schema_version__`.
33
+
34
+ ### Wire-format contract
35
+
36
+ - Successful response body is flat (no nested `data.data` envelope on most endpoints).
37
+ - Error response: `{"success": false, "error": "..."}` or `{"success": false, "error": {"code": "...", "message": "...", "details": {...}}}` (Phase 8 nested envelope).
38
+ - `X-Opedd-Request-Id` propagated to all SDK exception `request_id` fields.
39
+
40
+ [0.1.0]: https://github.com/Opedd/opedd-python/releases/tag/v0.1.0
opedd-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Opedd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
opedd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.4
2
+ Name: opedd
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Opedd content licensing API (buyer-side)
5
+ Project-URL: Homepage, https://opedd.com
6
+ Project-URL: Documentation, https://docs.opedd.com
7
+ Project-URL: Repository, https://github.com/Opedd/opedd-python
8
+ Project-URL: Issues, https://github.com/Opedd/opedd-python/issues
9
+ Project-URL: Changelog, https://github.com/Opedd/opedd-python/blob/main/CHANGELOG.md
10
+ Author-email: Opedd <support@opedd.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai-licensing,ai-training,content-licensing,opedd,rag
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.27.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
28
+ Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
29
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # opedd
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/opedd.svg)](https://pypi.org/project/opedd/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/opedd.svg)](https://pypi.org/project/opedd/)
37
+ [![License](https://img.shields.io/pypi/l/opedd.svg)](https://github.com/Opedd/opedd-python/blob/main/LICENSE)
38
+
39
+ Official Python SDK for the [Opedd](https://opedd.com) content licensing API (buyer-side).
40
+
41
+ Opedd is programmatic licensing infrastructure between AI buyers and publishers — rights, usage tracking, and payment. "Stripe for content licensing."
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install opedd
47
+ ```
48
+
49
+ Requires Python 3.10+.
50
+
51
+ ## Quickstart
52
+
53
+ ```python
54
+ from opedd import Opedd
55
+
56
+ client = Opedd(buyer_token="opedd_buyer_live_...")
57
+
58
+ # Fetch a single licensed article
59
+ article = client.content.get("article-uuid")
60
+ print(article["title"], article["author"], article["word_count"])
61
+
62
+ # Stream the full licensed catalog as NDJSON
63
+ client = Opedd(access_key="eak_xxx", buyer_email="eng@yourlab.com")
64
+ for row in client.feed.stream_ndjson(limit=5000):
65
+ train_model.ingest(row["content"], metadata=row)
66
+
67
+ # Pull a procurement-defense compliance dossier
68
+ client = Opedd(buyer_jwt="eyJhbGc...")
69
+ dossier = client.compliance.report(from_="2026-04-01", to="2026-04-30")
70
+ print(f"Retrievals: {dossier['dossier_metadata']['summary']['total_retrievals']}")
71
+ ```
72
+
73
+ For end-to-end walkthroughs, see the [cookbook](https://docs.opedd.com/cookbook.md).
74
+
75
+ ## Credentials
76
+
77
+ The SDK supports three credential types depending on which endpoint you call:
78
+
79
+ | Endpoint | Credential | Construction |
80
+ |---|---|---|
81
+ | `client.content.get(...)` | Bearer buyer token | `Opedd(buyer_token="opedd_buyer_live_...")` |
82
+ | `client.feed.list(...)` / `client.feed.stream_ndjson(...)` | Access key (query param) | `Opedd(access_key="eak_...")` |
83
+ | `client.audit.events(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
84
+ | `client.compliance.report(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
85
+ | `client.licenses.purchase(...)` | None (returns Stripe `client_secret`) | `Opedd(buyer_token="...")` |
86
+ | `client.licenses.list()` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
87
+
88
+ Multiple credentials can be supplied at once and the SDK selects the correct one per endpoint.
89
+
90
+ ### Env-var fallbacks
91
+
92
+ The constructor reads from these env vars when arguments are omitted:
93
+
94
+ - `OPEDD_BUYER_TOKEN`
95
+ - `OPEDD_BUYER_JWT`
96
+ - `OPEDD_ACCESS_KEY`
97
+ - `OPEDD_BASE_URL` (default `https://api.opedd.com`)
98
+
99
+ ### Exchanging an access key for a bearer token
100
+
101
+ ```python
102
+ client = Opedd.from_access_key(
103
+ access_key="eak_xyz...",
104
+ buyer_email="eng@yourlab.com",
105
+ )
106
+ # client.buyer_token is now set; you can call /content-delivery
107
+ ```
108
+
109
+ ## API surface
110
+
111
+ ```python
112
+ client.content.get(article_id)
113
+
114
+ client.feed.list(since=None, cursor=None, limit=200)
115
+ client.feed.stream_ndjson(since=None, cursor=None, limit=5000) # generator
116
+
117
+ client.audit.events(from_=None, to=None, event_type=None, cursor=None, limit=100)
118
+
119
+ client.compliance.report(from_, to, cursor=None)
120
+
121
+ client.licenses.purchase(publisher_ids, buyer_email, buyer_org, ...)
122
+ client.licenses.list()
123
+ ```
124
+
125
+ All methods return dicts matching the backend wire format. See [docs.opedd.com](https://docs.opedd.com) for full response shapes.
126
+
127
+ ## Error handling
128
+
129
+ ```python
130
+ from opedd import (
131
+ OpeddError,
132
+ OpeddAuthError,
133
+ OpeddNotFoundError,
134
+ OpeddRateLimitError,
135
+ OpeddServerError,
136
+ OpeddValidationError,
137
+ )
138
+
139
+ try:
140
+ article = client.content.get(uuid)
141
+ except OpeddRateLimitError as e:
142
+ time.sleep(e.retry_after_seconds or 60)
143
+ retry()
144
+ except OpeddAuthError:
145
+ refresh_token()
146
+ except OpeddNotFoundError:
147
+ log.warning("article gone; skipping")
148
+ except OpeddError as e:
149
+ log.error(f"{e} request_id={e.request_id}")
150
+ ```
151
+
152
+ Every error carries `status_code`, `request_id`, and `body` for forensic correlation.
153
+
154
+ ## Schema version pinning
155
+
156
+ The SDK ships pinned to backend schema version **`phase-11-m4`** (`opedd.__schema_version__`).
157
+
158
+ When the backend bumps `X-Opedd-Schema-Version`, the SDK ships a follow-up release within 1 sprint per the [schema-pin invariant](https://github.com/Opedd/opedd-backend/blob/main/INVARIANTS.md#python-sdk--mcp-server-pin-to-backend-x-opedd-schema-version-phase-11-m6).
159
+
160
+ Additive field bumps are absorbed transparently (dict pass-through). Subtractive or rename bumps require an SDK release; ensure your `requirements.txt` pins to a tested version.
161
+
162
+ ## Tests
163
+
164
+ Two test suites:
165
+
166
+ ### Unit tests (run in CI, no live API calls)
167
+
168
+ ```bash
169
+ pip install -e ".[dev]"
170
+ pytest tests/test_unit.py
171
+ ```
172
+
173
+ 29+ unit tests covering: client construction, credential precedence, env-var fallback, auth-header building per credential type, HTTP error mapping (401/403/404/400/422/429/5xx), NDJSON streaming + cursor pagination, all 5 namespaces' request shapes.
174
+
175
+ ### Integration tests (manual, before each release)
176
+
177
+ ```bash
178
+ export OPEDD_BUYER_JWT="..." # for /buyer-audit + /buyer-compliance-report
179
+ export OPEDD_BUYER_TOKEN="..." # for /content-delivery
180
+ export OPEDD_ACCESS_KEY="..." # for /enterprise-license GET feed
181
+ pytest --integration
182
+ ```
183
+
184
+ Live tests against `api.opedd.com`. Read-only. Skipped by default (require `--integration` flag).
185
+
186
+ Per [release discipline](./RELEASE.md), integration tests must pass locally before any version tag is pushed. CI does NOT run integration tests — per institutional risk discipline, autonomous CI runs against production state can pollute `usage_records`, distort metered-publisher payouts, and fire production API calls at scale.
187
+
188
+ ## Development
189
+
190
+ ```bash
191
+ git clone https://github.com/Opedd/opedd-python.git
192
+ cd opedd-python
193
+ pip install -e ".[dev]"
194
+ pytest tests/test_unit.py # unit only (default)
195
+ pytest --integration # unit + integration (requires env vars)
196
+ ruff check src/ tests/ # lint
197
+ mypy src/ # type-check
198
+ ```
199
+
200
+ ## Contributing
201
+
202
+ Issues and PRs welcome at [github.com/Opedd/opedd-python](https://github.com/Opedd/opedd-python). For broader questions about Opedd as a platform, email [support@opedd.com](mailto:support@opedd.com).
203
+
204
+ ## License
205
+
206
+ [MIT](./LICENSE)
207
+
208
+ ## See also
209
+
210
+ - [docs.opedd.com](https://docs.opedd.com) — full API reference + cookbook
211
+ - [opedd-mcp](https://github.com/Opedd/opedd-mcp) — Model Context Protocol server for Claude Desktop / Cursor
212
+ - [opedd-backend](https://github.com/Opedd/opedd-backend) — backend implementation (private)
opedd-0.1.0/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # opedd
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/opedd.svg)](https://pypi.org/project/opedd/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/opedd.svg)](https://pypi.org/project/opedd/)
5
+ [![License](https://img.shields.io/pypi/l/opedd.svg)](https://github.com/Opedd/opedd-python/blob/main/LICENSE)
6
+
7
+ Official Python SDK for the [Opedd](https://opedd.com) content licensing API (buyer-side).
8
+
9
+ Opedd is programmatic licensing infrastructure between AI buyers and publishers — rights, usage tracking, and payment. "Stripe for content licensing."
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install opedd
15
+ ```
16
+
17
+ Requires Python 3.10+.
18
+
19
+ ## Quickstart
20
+
21
+ ```python
22
+ from opedd import Opedd
23
+
24
+ client = Opedd(buyer_token="opedd_buyer_live_...")
25
+
26
+ # Fetch a single licensed article
27
+ article = client.content.get("article-uuid")
28
+ print(article["title"], article["author"], article["word_count"])
29
+
30
+ # Stream the full licensed catalog as NDJSON
31
+ client = Opedd(access_key="eak_xxx", buyer_email="eng@yourlab.com")
32
+ for row in client.feed.stream_ndjson(limit=5000):
33
+ train_model.ingest(row["content"], metadata=row)
34
+
35
+ # Pull a procurement-defense compliance dossier
36
+ client = Opedd(buyer_jwt="eyJhbGc...")
37
+ dossier = client.compliance.report(from_="2026-04-01", to="2026-04-30")
38
+ print(f"Retrievals: {dossier['dossier_metadata']['summary']['total_retrievals']}")
39
+ ```
40
+
41
+ For end-to-end walkthroughs, see the [cookbook](https://docs.opedd.com/cookbook.md).
42
+
43
+ ## Credentials
44
+
45
+ The SDK supports three credential types depending on which endpoint you call:
46
+
47
+ | Endpoint | Credential | Construction |
48
+ |---|---|---|
49
+ | `client.content.get(...)` | Bearer buyer token | `Opedd(buyer_token="opedd_buyer_live_...")` |
50
+ | `client.feed.list(...)` / `client.feed.stream_ndjson(...)` | Access key (query param) | `Opedd(access_key="eak_...")` |
51
+ | `client.audit.events(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
52
+ | `client.compliance.report(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
53
+ | `client.licenses.purchase(...)` | None (returns Stripe `client_secret`) | `Opedd(buyer_token="...")` |
54
+ | `client.licenses.list()` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
55
+
56
+ Multiple credentials can be supplied at once and the SDK selects the correct one per endpoint.
57
+
58
+ ### Env-var fallbacks
59
+
60
+ The constructor reads from these env vars when arguments are omitted:
61
+
62
+ - `OPEDD_BUYER_TOKEN`
63
+ - `OPEDD_BUYER_JWT`
64
+ - `OPEDD_ACCESS_KEY`
65
+ - `OPEDD_BASE_URL` (default `https://api.opedd.com`)
66
+
67
+ ### Exchanging an access key for a bearer token
68
+
69
+ ```python
70
+ client = Opedd.from_access_key(
71
+ access_key="eak_xyz...",
72
+ buyer_email="eng@yourlab.com",
73
+ )
74
+ # client.buyer_token is now set; you can call /content-delivery
75
+ ```
76
+
77
+ ## API surface
78
+
79
+ ```python
80
+ client.content.get(article_id)
81
+
82
+ client.feed.list(since=None, cursor=None, limit=200)
83
+ client.feed.stream_ndjson(since=None, cursor=None, limit=5000) # generator
84
+
85
+ client.audit.events(from_=None, to=None, event_type=None, cursor=None, limit=100)
86
+
87
+ client.compliance.report(from_, to, cursor=None)
88
+
89
+ client.licenses.purchase(publisher_ids, buyer_email, buyer_org, ...)
90
+ client.licenses.list()
91
+ ```
92
+
93
+ All methods return dicts matching the backend wire format. See [docs.opedd.com](https://docs.opedd.com) for full response shapes.
94
+
95
+ ## Error handling
96
+
97
+ ```python
98
+ from opedd import (
99
+ OpeddError,
100
+ OpeddAuthError,
101
+ OpeddNotFoundError,
102
+ OpeddRateLimitError,
103
+ OpeddServerError,
104
+ OpeddValidationError,
105
+ )
106
+
107
+ try:
108
+ article = client.content.get(uuid)
109
+ except OpeddRateLimitError as e:
110
+ time.sleep(e.retry_after_seconds or 60)
111
+ retry()
112
+ except OpeddAuthError:
113
+ refresh_token()
114
+ except OpeddNotFoundError:
115
+ log.warning("article gone; skipping")
116
+ except OpeddError as e:
117
+ log.error(f"{e} request_id={e.request_id}")
118
+ ```
119
+
120
+ Every error carries `status_code`, `request_id`, and `body` for forensic correlation.
121
+
122
+ ## Schema version pinning
123
+
124
+ The SDK ships pinned to backend schema version **`phase-11-m4`** (`opedd.__schema_version__`).
125
+
126
+ When the backend bumps `X-Opedd-Schema-Version`, the SDK ships a follow-up release within 1 sprint per the [schema-pin invariant](https://github.com/Opedd/opedd-backend/blob/main/INVARIANTS.md#python-sdk--mcp-server-pin-to-backend-x-opedd-schema-version-phase-11-m6).
127
+
128
+ Additive field bumps are absorbed transparently (dict pass-through). Subtractive or rename bumps require an SDK release; ensure your `requirements.txt` pins to a tested version.
129
+
130
+ ## Tests
131
+
132
+ Two test suites:
133
+
134
+ ### Unit tests (run in CI, no live API calls)
135
+
136
+ ```bash
137
+ pip install -e ".[dev]"
138
+ pytest tests/test_unit.py
139
+ ```
140
+
141
+ 29+ unit tests covering: client construction, credential precedence, env-var fallback, auth-header building per credential type, HTTP error mapping (401/403/404/400/422/429/5xx), NDJSON streaming + cursor pagination, all 5 namespaces' request shapes.
142
+
143
+ ### Integration tests (manual, before each release)
144
+
145
+ ```bash
146
+ export OPEDD_BUYER_JWT="..." # for /buyer-audit + /buyer-compliance-report
147
+ export OPEDD_BUYER_TOKEN="..." # for /content-delivery
148
+ export OPEDD_ACCESS_KEY="..." # for /enterprise-license GET feed
149
+ pytest --integration
150
+ ```
151
+
152
+ Live tests against `api.opedd.com`. Read-only. Skipped by default (require `--integration` flag).
153
+
154
+ Per [release discipline](./RELEASE.md), integration tests must pass locally before any version tag is pushed. CI does NOT run integration tests — per institutional risk discipline, autonomous CI runs against production state can pollute `usage_records`, distort metered-publisher payouts, and fire production API calls at scale.
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ git clone https://github.com/Opedd/opedd-python.git
160
+ cd opedd-python
161
+ pip install -e ".[dev]"
162
+ pytest tests/test_unit.py # unit only (default)
163
+ pytest --integration # unit + integration (requires env vars)
164
+ ruff check src/ tests/ # lint
165
+ mypy src/ # type-check
166
+ ```
167
+
168
+ ## Contributing
169
+
170
+ Issues and PRs welcome at [github.com/Opedd/opedd-python](https://github.com/Opedd/opedd-python). For broader questions about Opedd as a platform, email [support@opedd.com](mailto:support@opedd.com).
171
+
172
+ ## License
173
+
174
+ [MIT](./LICENSE)
175
+
176
+ ## See also
177
+
178
+ - [docs.opedd.com](https://docs.opedd.com) — full API reference + cookbook
179
+ - [opedd-mcp](https://github.com/Opedd/opedd-mcp) — Model Context Protocol server for Claude Desktop / Cursor
180
+ - [opedd-backend](https://github.com/Opedd/opedd-backend) — backend implementation (private)
opedd-0.1.0/RELEASE.md ADDED
@@ -0,0 +1,106 @@
1
+ # Release process for opedd-python
2
+
3
+ This document codifies the SDK release discipline established in Phase 11 M6.
4
+
5
+ ## Trigger
6
+
7
+ A release ships when:
8
+
9
+ 1. The backend bumps `X-Opedd-Schema-Version` on any buyer-facing endpoint, AND the bump requires a wrapper change (additive bumps are absorbed transparently; subtractive/rename bumps require code).
10
+ 2. A new buyer-side endpoint ships in the backend (e.g., Phase 12 onwards).
11
+ 3. A bug is fixed in the SDK itself.
12
+ 4. A new minor feature lands in the SDK (e.g., new error type, retry helper).
13
+
14
+ Per the [schema-pin invariant](https://github.com/Opedd/opedd-backend/blob/main/INVARIANTS.md#python-sdk--mcp-server-pin-to-backend-x-opedd-schema-version-phase-11-m6), backend schema bumps trigger a follow-up SDK release within 1 sprint.
15
+
16
+ ## Version-bump rules
17
+
18
+ - **MAJOR** (1.0.0 → 2.0.0): breaking SDK API change — method removed/renamed, parameter signature change.
19
+ - **MINOR** (0.1.0 → 0.2.0): new endpoint surface, new namespace, new optional parameter, backend schema-pin bump.
20
+ - **PATCH** (0.1.0 → 0.1.1): bug fix, doc update, dependency-version bump that doesn't change behavior.
21
+
22
+ While the SDK is pre-1.0, MINOR bumps are also acceptable for behavior-breaking changes; the README + CHANGELOG must call them out explicitly.
23
+
24
+ ## Pre-release checklist
25
+
26
+ 1. **Update `__version__`** in `src/opedd/__init__.py` AND `version` in `pyproject.toml`.
27
+ 2. **Update `__schema_version__`** if the backend bumped any buyer-facing endpoint schema version.
28
+ 3. **Update `CHANGELOG.md`** with a new section dated today's UTC date.
29
+ 4. **Run unit tests**: `pytest tests/test_unit.py` — must be all-green.
30
+ 5. **Run integration tests locally** with founder credentials:
31
+ ```bash
32
+ export OPEDD_BUYER_JWT="..."
33
+ export OPEDD_BUYER_TOKEN="..."
34
+ export OPEDD_ACCESS_KEY="..."
35
+ pytest --integration
36
+ ```
37
+ Per founder M6 ratification 6, integration tests do NOT run in CI — they run locally before each release. Verify all-green.
38
+ 6. **Lint + type-check**: `ruff check src/ tests/` AND `mypy src/`. Must be clean.
39
+ 7. **Build the package**: `pip install --upgrade build && python -m build`. Confirm `dist/opedd-<version>-py3-none-any.whl` and `dist/opedd-<version>.tar.gz` produced.
40
+ 8. **Verify install from local wheel**: `pip install --force-reinstall dist/opedd-<version>-py3-none-any.whl && python -c "import opedd; print(opedd.__version__)"`.
41
+
42
+ ## PyPI publish
43
+
44
+ ```bash
45
+ pip install --upgrade twine
46
+ twine check dist/*
47
+ twine upload dist/*
48
+ ```
49
+
50
+ You'll need a PyPI API token (`pypi-AgEIcHlw...`) stored either in `~/.pypirc`:
51
+
52
+ ```ini
53
+ [pypi]
54
+ username = __token__
55
+ password = pypi-AgEIcHlw...
56
+ ```
57
+
58
+ Or passed via `TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-...`.
59
+
60
+ Verify install from PyPI after upload (give the CDN ~30s to propagate):
61
+
62
+ ```bash
63
+ pip install --upgrade opedd
64
+ python -c "import opedd; print(opedd.__version__, opedd.__schema_version__)"
65
+ ```
66
+
67
+ ## Git tag + GitHub release
68
+
69
+ ```bash
70
+ git tag -a v0.1.0 -m "v0.1.0 — initial buyer-side SDK"
71
+ git push origin v0.1.0
72
+ gh release create v0.1.0 \
73
+ --title "v0.1.0 — initial buyer-side SDK" \
74
+ --notes-file CHANGELOG.md
75
+ ```
76
+
77
+ ## Post-release
78
+
79
+ - Cross-link from opedd-docs `python-sdk.md` if the README example block changed.
80
+ - If schema pin changed: update `opedd-backend/INVARIANTS.md` "Python SDK + MCP server pin to backend X-Opedd-Schema-Version" section with the new version.
81
+ - Email `support@opedd.com` (or whatever the buyer-facing channel is) if the bump is breaking.
82
+
83
+ ## Yank / unpublish
84
+
85
+ If a release ships with a critical bug:
86
+
87
+ ```bash
88
+ # Yank (preferred — removes from default index but keeps for pinned installs)
89
+ twine upload --skip-existing dist/opedd-<patched-version>-* # ship the patch first
90
+ pip install --upgrade twine
91
+ # Then yank the broken version via PyPI web UI: https://pypi.org/manage/project/opedd/release/<version>/
92
+ ```
93
+
94
+ NEVER fully delete a PyPI release; PyPI does not allow re-publishing the same version number, so deletion creates a permanent gap. Yank-then-patch is the canonical recovery path.
95
+
96
+ ## CI
97
+
98
+ CI runs unit tests on every push (see `.github/workflows/ci.yml`). Integration tests run locally only (per Phase 11 M6 ratification 6: "SDK live tests running autonomously in CI could pollute usage_records, distort metered-publisher-payouts aggregation, fire production API calls with founder identity at scale").
99
+
100
+ ## Maintenance schedule
101
+
102
+ - **Backend X-Opedd-Schema-Version bump**: ship follow-up SDK within 1 sprint per the schema-pin invariant.
103
+ - **Dependency security advisory (httpx, pytest)**: ship patch release within 1 week.
104
+ - **Python EOL versions (3.10 EOL October 2026)**: drop EOL'd versions in a MINOR bump, document in CHANGELOG.
105
+
106
+ See [opedd-backend/docs/cleanup/active-deferrals.md](https://github.com/Opedd/opedd-backend/blob/main/docs/cleanup/active-deferrals.md) for the institutional schema-pin maintenance discipline entry.