notex-python 0.1.0__tar.gz → 0.2.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.
Files changed (29) hide show
  1. {notex_python-0.1.0/src/notex_python.egg-info → notex_python-0.2.0}/PKG-INFO +371 -323
  2. {notex_python-0.1.0 → notex_python-0.2.0}/README.md +56 -10
  3. {notex_python-0.1.0 → notex_python-0.2.0}/pyproject.toml +3 -1
  4. {notex_python-0.1.0 → notex_python-0.2.0}/setup.cfg +4 -4
  5. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex/__init__.py +7 -1
  6. {notex_python-0.1.0 → notex_python-0.2.0/src/notex_python.egg-info}/PKG-INFO +371 -323
  7. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/SOURCES.txt +2 -0
  8. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/__init__.py +5 -1
  9. notex_python-0.2.0/src/notex_sdk/facade.py +210 -0
  10. notex_python-0.2.0/tests/test_smoke_api.py +37 -0
  11. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_staging_integration.py +15 -1
  12. {notex_python-0.1.0 → notex_python-0.2.0}/LICENSE +0 -0
  13. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex/py.typed +0 -0
  14. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/dependency_links.txt +0 -0
  15. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/requires.txt +0 -0
  16. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_python.egg-info/top_level.txt +0 -0
  17. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/async_client.py +0 -0
  18. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/client.py +0 -0
  19. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/config.py +0 -0
  20. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/errors.py +0 -0
  21. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/models.py +0 -0
  22. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/py.typed +0 -0
  23. {notex_python-0.1.0 → notex_python-0.2.0}/src/notex_sdk/transport.py +0 -0
  24. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_client.py +0 -0
  25. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_content_api.py +0 -0
  26. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_external_consumer.py +0 -0
  27. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_models.py +0 -0
  28. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_public_api.py +0 -0
  29. {notex_python-0.1.0 → notex_python-0.2.0}/tests/test_reliability.py +0 -0
@@ -1,323 +1,371 @@
1
- Metadata-Version: 2.4
2
- Name: notex-python
3
- Version: 0.1.0
4
- Summary: Typed, dependency-free Python SDK for the NoteX API
5
- Author: NoteX
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://notexapp.com
8
- Project-URL: Documentation, https://be-docs.notexapp.com
9
- Keywords: notex,ai,notes,sdk,api-client
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Typing :: Typed
21
- Requires-Python: >=3.9
22
- Description-Content-Type: text/markdown
23
- License-File: LICENSE
24
- Provides-Extra: test
25
- Requires-Dist: pytest>=7; extra == "test"
26
- Provides-Extra: dev
27
- Requires-Dist: build>=1.2; extra == "dev"
28
- Requires-Dist: pytest>=7; extra == "dev"
29
- Requires-Dist: pyright>=1.1.400; extra == "dev"
30
- Requires-Dist: ruff>=0.12; extra == "dev"
31
- Requires-Dist: twine>=6; extra == "dev"
32
- Dynamic: license-file
33
-
34
- # NoteX Python SDK
35
-
36
- Typed, dependency-free Python SDK for API-key-enabled NoteX endpoints.
37
-
38
- ```bash
39
- pip install notex-python
40
- ```
41
-
42
- ```python
43
- from notex import NotexClient
44
-
45
- client = NotexClient(api_key="ntx_live_xxx")
46
- ```
47
-
48
- Requires Python 3.9 or newer.
49
-
50
- ## Naming convention
51
-
52
- The public SDK has one flat, explicit naming convention:
53
-
54
- | Operation | Convention | Examples |
55
- | --- | --- | --- |
56
- | Read one value | `get_<resource>()` | `get_profile()`, `get_task_result()` |
57
- | Read a collection | `list_<resources>()` | `list_notes()` |
58
- | Validate input | `validate_<resource>()` | `validate_source()` |
59
- | Create content | `create_<resource>()` | `create_note()`, `create_quiz()` |
60
- | Wait for an async operation | `wait_for_<resource>()` | `wait_for_task()` |
61
-
62
- Sync and async clients expose the same method names. The async variants are
63
- awaited.
64
-
65
- ## Multi-user integration
66
-
67
- Each client represents one user API key for one request or background job.
68
- Do not mutate a singleton client's key between users.
69
-
70
- ```python
71
- from notex import NotexClient
72
-
73
-
74
- def notex_for_connection(connection_id: str) -> NotexClient:
75
- api_key = credential_store.decrypt(connection_id)
76
- return NotexClient(api_key=api_key)
77
- ```
78
-
79
- The SDK does not create, list, delete, persist, or automatically load API
80
- keys. The integrating backend owns credential storage and rotation.
81
-
82
- ## Supported methods
83
-
84
- ### Account and notes
85
-
86
- ```python
87
- credits = client.get_credits()
88
- quota = client.get_quota()
89
- profile = client.get_profile()
90
- notes = client.list_notes(limit=20, sort_field="createdAt", sort_order=-1)
91
- ```
92
-
93
- Credit fields vary by account/plan. `Credits` exposes optional
94
- `total_credits`, `reward_credits`, `purchased_credits`, `user_type`, and
95
- `has_paid`, while `raw` preserves the complete backend payload. `balance` is
96
- an optional compatibility alias for responses that provide `balance` or
97
- `total_credits`.
98
-
99
- ### Validate and create notes
100
-
101
- Create a note from a web URL:
102
-
103
- ```python
104
- submission = client.create_note(
105
- web_url="https://youtu.be/example",
106
- language_hints=["en"],
107
- )
108
- ```
109
-
110
- Convenience equivalent:
111
-
112
- ```python
113
- submission = client.create_note_from_url(
114
- "https://youtu.be/example",
115
- language_hints=["en"],
116
- )
117
- ```
118
-
119
- Create a note from an existing NoteX file URL:
120
-
121
- ```python
122
- submission = client.create_note_from_file_url(
123
- "audio/user-1/lecture.mp3",
124
- language_hints=["en"],
125
- )
126
- ```
127
-
128
- Create a note from a local file:
129
-
130
- ```python
131
- submission = client.create_note_from_file(
132
- "./lecture.mp3",
133
- upload_file_name="lecture.mp3",
134
- language_hints=["en"],
135
- content_type="audio/mpeg",
136
- )
137
- ```
138
-
139
- The local-file method performs the complete internal flow:
140
-
141
- 1. Request a presigned upload contract.
142
- 2. Upload the file directly without the NoteX API key.
143
- 3. Submit note creation using the returned `file_url`.
144
-
145
- Presign and direct-upload helpers are intentionally private. Use
146
- `upload_file_name` when the local filename contains characters rejected by the
147
- target gateway or object storage.
148
-
149
- Validate a source before spending credits:
150
-
151
- ```python
152
- validation = client.validate_source(web_url="https://youtu.be/example")
153
- ```
154
-
155
- Exactly one of `web_url` or `file_url` is accepted by `create_note()` and
156
- `validate_source()`.
157
-
158
- ### Create content from a note
159
-
160
- All create methods return `TaskSubmission` with a `task_id`.
161
-
162
- ```python
163
- flashcards = client.create_flashcards(
164
- "note-id",
165
- num_cards=10,
166
- difficulty="medium",
167
- )
168
- quiz = client.create_quiz("note-id", num_questions=10)
169
- mindmap = client.create_mindmap("note-id")
170
- podcast = client.create_podcast("note-id", duration=180)
171
- shorts = client.create_shorts("note-id", voice_id="en-US-Standard-A")
172
- quiz_video = client.create_quiz_video("note-id", voice_id="en-US-Standard-A")
173
- slides = client.create_slide("note-id", template_id="default", language="en")
174
- translation = client.create_translation("note-id", language="vi")
175
- ```
176
-
177
- ### Poll task results
178
-
179
- Use `get_task_result()` when the integrating backend manages scheduling:
180
-
181
- ```python
182
- result = client.get_task_result(submission.task_id)
183
- ```
184
-
185
- Use `wait_for_task()` in a worker when a blocking helper is appropriate:
186
-
187
- ```python
188
- result = client.wait_for_task(
189
- submission.task_id,
190
- poll_interval=5,
191
- timeout=120,
192
- )
193
- ```
194
-
195
- Do not run file uploads or blocking task polling directly in a web request.
196
- Use a durable queue or background worker.
197
-
198
- Completed results can be parsed into feature-specific typed models:
199
-
200
- ```python
201
- from notex import FlashcardSet
202
-
203
- result = client.wait_for_task(flashcards.task_id)
204
- flashcard_set = FlashcardSet.from_task_result(result)
205
- print(flashcard_set.cards[0].front)
206
- ```
207
-
208
- Available result models include `GeneratedNote`, `FlashcardSet`, `QuizSet`,
209
- `Mindmap`, `Podcast`, `Video`, and `SlideDeck`.
210
-
211
- ## Async client
212
-
213
- `AsyncNotexClient` exposes the same names and runs blocking standard-library
214
- I/O in worker threads so the event loop remains responsive.
215
-
216
- ```python
217
- from notex import AsyncNotexClient
218
-
219
- client = AsyncNotexClient(api_key="ntx_live_xxx")
220
-
221
- submission = await client.create_quiz("note-id", num_questions=10)
222
- result = await client.wait_for_task(submission.task_id, timeout=120)
223
- ```
224
-
225
- ## Errors and retry behavior
226
-
227
- ```python
228
- from notex import NotexAuthenticationError, NotexRateLimitError
229
-
230
- try:
231
- client.get_credits()
232
- except NotexAuthenticationError:
233
- reconnect_notex_account()
234
- except NotexRateLimitError as error:
235
- reschedule_job(error.retry_after)
236
- ```
237
-
238
- Public errors:
239
-
240
- - `NotexAPIError`
241
- - `NotexAuthenticationError`
242
- - `NotexPermissionError`
243
- - `NotexRateLimitError`
244
- - `NotexUploadError`
245
- - `NotexTaskError`
246
-
247
- GET and HEAD requests retry transient network failures and HTTP 429/5xx
248
- responses, honoring `Retry-After`. Content-creating POST requests are not
249
- retried by default because repeating them can create duplicate work.
250
-
251
- ## API compatibility
252
-
253
- Every high-level method accepts `endpoint=` and `base_url=` overrides. This
254
- allows an integration to adopt a new backend version before the SDK releases
255
- an update.
256
-
257
- ```python
258
- client.get_credits(endpoint="/v3/credits/me")
259
- client.create_flashcards("note-id", endpoint="/v7/create/flashcards")
260
- ```
261
-
262
- For a documented endpoint that is not wrapped yet, use the low-level escape
263
- hatch:
264
-
265
- ```python
266
- response = client.request(
267
- "POST",
268
- "/v10/feature",
269
- form={"note_id": "note-id"},
270
- headers={"Idempotency-Key": "internal-job-id"},
271
- )
272
- ```
273
-
274
- ## Real API integration tests
275
-
276
- Copy the safe template and put the real key/base URL in the local file:
277
-
278
- ```powershell
279
- Copy-Item .notex-test.env.example .notex-test.env
280
- ```
281
-
282
- Edit `.notex-test.env`:
283
-
284
- ```dotenv
285
- NOTEX_TEST_API_KEY=ntx_test_your_real_key
286
- NOTEX_TEST_BASE_URL=https://api.notexapp.com
287
- ```
288
-
289
- The file is ignored by Git. Read-only tests cover profile, credits, quota, and
290
- note listing:
291
-
292
- ```bash
293
- python -m pytest tests/test_staging_integration.py -v
294
- ```
295
-
296
- Creation tests can upload files and spend credits, so they require explicit
297
- opt-in:
298
-
299
- ```dotenv
300
- NOTEX_RUN_WRITE_TESTS=1
301
- NOTEX_TEST_WEB_URL=https://youtu.be/example
302
- NOTEX_TEST_FILE=C:\path\to\lecture.mp3
303
- NOTEX_TEST_NOTE_ID=existing-note-id
304
- NOTEX_TEST_FEATURES=flashcards,quiz,mindmap,translation
305
- ```
306
-
307
- Optional feature configuration is documented in
308
- `.notex-test.env.example`. Test output is not persisted by the suite; local
309
- credentials and `.notex-test-results/` are both excluded from Git.
310
-
311
- ## Development and release checks
312
-
313
- ```bash
314
- python -m pip install -e ".[dev]"
315
- python -m ruff check .
316
- python -m pyright
317
- python -m pytest -q
318
- python -m build
319
- python -m twine check dist/*
320
- ```
321
-
322
- See [RELEASING.md](RELEASING.md) for the Trusted Publishing release flow.
323
- Never log, commit, or return API keys to frontend clients.
1
+ Metadata-Version: 2.4
2
+ Name: notex-python
3
+ Version: 0.2.0
4
+ Summary: Typed, dependency-free Python SDK for the NoteX API
5
+ Author: NoteX
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://notexapp.com
8
+ Project-URL: Documentation, https://be-docs.notexapp.com
9
+ Project-URL: Repository, https://github.com/sota-labs/notex-python-sdk
10
+ Project-URL: Issues, https://github.com/sota-labs/notex-python-sdk/issues
11
+ Keywords: notex,ai,notes,sdk,api-client
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.9
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: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=7; extra == "test"
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2; extra == "dev"
30
+ Requires-Dist: pytest>=7; extra == "dev"
31
+ Requires-Dist: pyright>=1.1.400; extra == "dev"
32
+ Requires-Dist: ruff>=0.12; extra == "dev"
33
+ Requires-Dist: twine>=6; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # NoteX Python SDK
37
+
38
+ [![PyPI version](https://img.shields.io/pypi/v/notex-python)](https://pypi.org/project/notex-python/)
39
+ [![Python versions](https://img.shields.io/pypi/pyversions/notex-python)](https://pypi.org/project/notex-python/)
40
+
41
+ Typed, dependency-free Python SDK for API-key-enabled NoteX endpoints.
42
+
43
+ ```bash
44
+ pip install notex-python
45
+ ```
46
+
47
+ ```python
48
+ from notex import Notex
49
+
50
+ client = Notex(api_key="ntx_live_xxx")
51
+ task = client.notes.create(text="Summarize this content.")
52
+ print(task.id)
53
+ ```
54
+
55
+ Requires Python 3.9 or newer.
56
+
57
+ `text` is uploaded internally as a temporary UTF-8 text file because the
58
+ NoteX create endpoint consumes URL or file sources. The temporary local file
59
+ is removed immediately after NoteX accepts the asynchronous task.
60
+
61
+ Use `task.result()` for one status check or `task.wait()` from a worker:
62
+
63
+ ```python
64
+ result = task.wait(timeout=120, poll_interval=5)
65
+ ```
66
+
67
+ `NotexClient` remains the typed low-level client for explicit endpoint access.
68
+
69
+ ## Naming convention
70
+
71
+ The public SDK has one flat, explicit naming convention:
72
+
73
+ | Operation | Convention | Examples |
74
+ | --- | --- | --- |
75
+ | Read one value | `get_<resource>()` | `get_profile()`, `get_task_result()` |
76
+ | Read a collection | `list_<resources>()` | `list_notes()` |
77
+ | Validate input | `validate_<resource>()` | `validate_source()` |
78
+ | Create content | `create_<resource>()` | `create_note()`, `create_quiz()` |
79
+ | Wait for an async operation | `wait_for_<resource>()` | `wait_for_task()` |
80
+
81
+ Sync and async clients expose the same method names. The async variants are
82
+ awaited.
83
+
84
+ ## Multi-user integration
85
+
86
+ Each `Notex` or `NotexClient` instance represents one user API key for one
87
+ request or background job. Do not mutate a singleton client's key between
88
+ users.
89
+
90
+ ```python
91
+ from notex import NotexClient
92
+
93
+
94
+ def notex_for_connection(connection_id: str) -> NotexClient:
95
+ api_key = credential_store.decrypt(connection_id)
96
+ return NotexClient(api_key=api_key)
97
+ ```
98
+
99
+ The SDK does not create, list, delete, persist, or automatically load API
100
+ keys. The integrating backend owns credential storage and rotation.
101
+
102
+ ## Supported methods
103
+
104
+ ### Account and notes
105
+
106
+ ```python
107
+ credits = client.get_credits()
108
+ quota = client.get_quota()
109
+ profile = client.get_profile()
110
+ notes = client.list_notes(limit=20, sort_field="createdAt", sort_order=-1)
111
+ ```
112
+
113
+ Credit fields vary by account/plan. `Credits` exposes optional
114
+ `total_credits`, `reward_credits`, `purchased_credits`, `user_type`, and
115
+ `has_paid`, while `raw` preserves the complete backend payload. `balance` is
116
+ an optional compatibility alias for responses that provide `balance` or
117
+ `total_credits`.
118
+
119
+ ### Validate and create notes
120
+
121
+ Create a note from a web URL:
122
+
123
+ ```python
124
+ submission = client.create_note(
125
+ web_url="https://youtu.be/example",
126
+ language_hints=["en"],
127
+ )
128
+ ```
129
+
130
+ Convenience equivalent:
131
+
132
+ ```python
133
+ submission = client.create_note_from_url(
134
+ "https://youtu.be/example",
135
+ language_hints=["en"],
136
+ )
137
+ ```
138
+
139
+ Create a note from an existing NoteX file URL:
140
+
141
+ ```python
142
+ submission = client.create_note_from_file_url(
143
+ "audio/user-1/lecture.mp3",
144
+ language_hints=["en"],
145
+ )
146
+ ```
147
+
148
+ Create a note from a local file:
149
+
150
+ ```python
151
+ submission = client.create_note_from_file(
152
+ "./lecture.mp3",
153
+ upload_file_name="lecture.mp3",
154
+ language_hints=["en"],
155
+ content_type="audio/mpeg",
156
+ )
157
+ ```
158
+
159
+ The local-file method performs the complete internal flow:
160
+
161
+ 1. Request a presigned upload contract.
162
+ 2. Upload the file directly without the NoteX API key.
163
+ 3. Submit note creation using the returned `file_url`.
164
+
165
+ Presign and direct-upload helpers are intentionally private. Use
166
+ `upload_file_name` when the local filename contains characters rejected by the
167
+ target gateway or object storage.
168
+
169
+ Validate a source before spending credits:
170
+
171
+ ```python
172
+ validation = client.validate_source(web_url="https://youtu.be/example")
173
+ ```
174
+
175
+ Exactly one of `web_url` or `file_url` is accepted by `create_note()` and
176
+ `validate_source()`.
177
+
178
+ ### Create content from a note
179
+
180
+ All create methods return `TaskSubmission` with a `task_id`.
181
+
182
+ ```python
183
+ flashcards = client.create_flashcards(
184
+ "note-id",
185
+ num_cards=10,
186
+ difficulty="medium",
187
+ )
188
+ quiz = client.create_quiz("note-id", num_questions=10)
189
+ mindmap = client.create_mindmap("note-id")
190
+ podcast = client.create_podcast("note-id", duration=180)
191
+ shorts = client.create_shorts("note-id", voice_id="en-US-Standard-A")
192
+ quiz_video = client.create_quiz_video("note-id", voice_id="en-US-Standard-A")
193
+ slides = client.create_slide("note-id", template_id="default", language="en")
194
+ translation = client.create_translation("note-id", language="vi")
195
+ ```
196
+
197
+ ### Poll task results
198
+
199
+ Use `get_task_result()` when the integrating backend manages scheduling:
200
+
201
+ ```python
202
+ result = client.get_task_result(submission.task_id)
203
+ ```
204
+
205
+ Use `wait_for_task()` in a worker when a blocking helper is appropriate:
206
+
207
+ ```python
208
+ result = client.wait_for_task(
209
+ submission.task_id,
210
+ poll_interval=5,
211
+ timeout=120,
212
+ )
213
+ ```
214
+
215
+ Do not run file uploads or blocking task polling directly in a web request.
216
+ Use a durable queue or background worker.
217
+
218
+ Completed results can be parsed into feature-specific typed models:
219
+
220
+ ```python
221
+ from notex import FlashcardSet
222
+
223
+ result = client.wait_for_task(flashcards.task_id)
224
+ flashcard_set = FlashcardSet.from_task_result(result)
225
+ print(flashcard_set.cards[0].front)
226
+ ```
227
+
228
+ Available result models include `GeneratedNote`, `FlashcardSet`, `QuizSet`,
229
+ `Mindmap`, `Podcast`, `Video`, and `SlideDeck`.
230
+
231
+ ## Async client
232
+
233
+ `AsyncNotexClient` exposes the same names and runs blocking standard-library
234
+ I/O in worker threads so the event loop remains responsive.
235
+
236
+ ```python
237
+ from notex import AsyncNotexClient
238
+
239
+ client = AsyncNotexClient(api_key="ntx_live_xxx")
240
+
241
+ submission = await client.create_quiz("note-id", num_questions=10)
242
+ result = await client.wait_for_task(submission.task_id, timeout=120)
243
+ ```
244
+
245
+ ## Errors and retry behavior
246
+
247
+ ```python
248
+ from notex import NotexAuthenticationError, NotexRateLimitError
249
+
250
+ try:
251
+ client.get_credits()
252
+ except NotexAuthenticationError:
253
+ reconnect_notex_account()
254
+ except NotexRateLimitError as error:
255
+ reschedule_job(error.retry_after)
256
+ ```
257
+
258
+ Public errors:
259
+
260
+ - `NotexAPIError`
261
+ - `NotexAuthenticationError`
262
+ - `NotexPermissionError`
263
+ - `NotexRateLimitError`
264
+ - `NotexUploadError`
265
+ - `NotexTaskError`
266
+
267
+ GET and HEAD requests retry transient network failures and HTTP 429/5xx
268
+ responses, honoring `Retry-After`. Content-creating POST requests are not
269
+ retried by default because repeating them can create duplicate work.
270
+
271
+ ## API compatibility
272
+
273
+ Every high-level method accepts `endpoint=` and `base_url=` overrides. This
274
+ allows an integration to adopt a new backend version before the SDK releases
275
+ an update.
276
+
277
+ ```python
278
+ client.get_credits(endpoint="/v3/credits/me")
279
+ client.create_flashcards("note-id", endpoint="/v7/create/flashcards")
280
+ ```
281
+
282
+ For a documented endpoint that is not wrapped yet, use the low-level escape
283
+ hatch:
284
+
285
+ ```python
286
+ response = client.request(
287
+ "POST",
288
+ "/v10/feature",
289
+ form={"note_id": "note-id"},
290
+ headers={"Idempotency-Key": "internal-job-id"},
291
+ )
292
+ ```
293
+
294
+ ## Real API integration tests
295
+
296
+ Copy the safe template and put the real key/base URL in the local file:
297
+
298
+ ```powershell
299
+ Copy-Item .notex-test.env.example .notex-test.env
300
+ ```
301
+
302
+ Edit `.notex-test.env`:
303
+
304
+ ```dotenv
305
+ NOTEX_TEST_API_KEY=ntx_test_your_real_key
306
+ NOTEX_TEST_BASE_URL=https://api.notexapp.com
307
+ ```
308
+
309
+ The file is ignored by Git. Read-only tests cover profile, credits, quota, and
310
+ note listing:
311
+
312
+ ```bash
313
+ python -m pytest tests/test_staging_integration.py -v
314
+ ```
315
+
316
+ Creation tests can upload files and spend credits, so they require explicit
317
+ opt-in:
318
+
319
+ ```dotenv
320
+ NOTEX_RUN_WRITE_TESTS=1
321
+ NOTEX_TEST_WEB_URL=https://youtu.be/example
322
+ NOTEX_TEST_FILE=C:\path\to\lecture.mp3
323
+ NOTEX_TEST_NOTE_ID=existing-note-id
324
+ NOTEX_TEST_FEATURES=flashcards,quiz,mindmap,translation
325
+ ```
326
+
327
+ Optional feature configuration is documented in
328
+ `.notex-test.env.example`. Test output is not persisted by the suite; local
329
+ credentials and `.notex-test-results/` are both excluded from Git.
330
+
331
+ The exact README smoke flow is also covered by an opt-in real integration
332
+ test:
333
+
334
+ ```powershell
335
+ python -m pytest tests/test_staging_integration.py::test_real_readme_smoke_create_note_from_text -v
336
+ ```
337
+
338
+ ## Development and release checks
339
+
340
+ Install development tools once:
341
+
342
+ ```bash
343
+ python -m pip install -e ".[dev]"
344
+ ```
345
+
346
+ Run the complete local release gate on Windows:
347
+
348
+ ```powershell
349
+ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\check-local.ps1
350
+ ```
351
+
352
+ This runs linting, type checking, non-network unit tests, package build,
353
+ `twine check`, clean-wheel installation, and a public import/version smoke
354
+ test. It never reads `PYPI_API_KEY` and never publishes.
355
+
356
+ On GitHub, changing `project.version` in `pyproject.toml` on `main`
357
+ automatically triggers the publish workflow. It checks PyPI first and skips
358
+ the upload when that exact version already exists. Pull requests never
359
+ receive the PyPI secret.
360
+
361
+ Verify the package published on PyPI in a clean Windows environment:
362
+
363
+ ```powershell
364
+ py -m venv .verify
365
+ .\.verify\Scripts\python.exe -m pip install --upgrade notex-python
366
+ .\.verify\Scripts\python.exe -c "from notex import Notex; print(Notex)"
367
+ .\.verify\Scripts\python.exe -c "import importlib.metadata; print(importlib.metadata.version('notex-python'))"
368
+ ```
369
+
370
+ See [RELEASING.md](RELEASING.md) for the repository-secret release flow.
371
+ Never log, commit, or return API keys to frontend clients.