notex-python 0.1.0__py3-none-any.whl
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.
- notex/__init__.py +105 -0
- notex/py.typed +1 -0
- notex_python-0.1.0.dist-info/METADATA +323 -0
- notex_python-0.1.0.dist-info/RECORD +15 -0
- notex_python-0.1.0.dist-info/WHEEL +5 -0
- notex_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- notex_python-0.1.0.dist-info/top_level.txt +2 -0
- notex_sdk/__init__.py +108 -0
- notex_sdk/async_client.py +100 -0
- notex_sdk/client.py +496 -0
- notex_sdk/config.py +25 -0
- notex_sdk/errors.py +45 -0
- notex_sdk/models.py +429 -0
- notex_sdk/py.typed +1 -0
- notex_sdk/transport.py +140 -0
notex/__init__.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Public API for the NoteX Python SDK."""
|
|
2
|
+
|
|
3
|
+
from notex_sdk import (
|
|
4
|
+
API_V1,
|
|
5
|
+
API_V2,
|
|
6
|
+
CREDITS_ME_ENDPOINT,
|
|
7
|
+
CREDITS_QUOTA_ENDPOINT,
|
|
8
|
+
DEFAULT_BASE_URL,
|
|
9
|
+
FLASHCARDS_CREATE_ENDPOINT,
|
|
10
|
+
MINDMAP_CREATE_ENDPOINT,
|
|
11
|
+
NOTE_CREATE_ENDPOINT,
|
|
12
|
+
NOTE_VALIDATE_ENDPOINT,
|
|
13
|
+
PODCAST_CREATE_ENDPOINT,
|
|
14
|
+
PRESIGNED_URL_ENDPOINT,
|
|
15
|
+
QUIZ_CREATE_ENDPOINT,
|
|
16
|
+
QUIZ_VIDEO_CREATE_ENDPOINT,
|
|
17
|
+
SHORTS_CREATE_ENDPOINT,
|
|
18
|
+
SLIDE_CREATE_ENDPOINT,
|
|
19
|
+
TASK_RESULT_ENDPOINT,
|
|
20
|
+
TRANSLATE_CREATE_ENDPOINT,
|
|
21
|
+
USER_NOTES_ENDPOINT,
|
|
22
|
+
USER_PROFILE_ENDPOINT,
|
|
23
|
+
AsyncNotexClient,
|
|
24
|
+
Credits,
|
|
25
|
+
Flashcard,
|
|
26
|
+
FlashcardSet,
|
|
27
|
+
GeneratedNote,
|
|
28
|
+
Mindmap,
|
|
29
|
+
MindmapNode,
|
|
30
|
+
NoteItem,
|
|
31
|
+
NotesPage,
|
|
32
|
+
NotexAPIError,
|
|
33
|
+
NotexAuthenticationError,
|
|
34
|
+
NotexClient,
|
|
35
|
+
NotexPermissionError,
|
|
36
|
+
NotexRateLimitError,
|
|
37
|
+
NotexTaskError,
|
|
38
|
+
NotexUploadError,
|
|
39
|
+
Podcast,
|
|
40
|
+
QuizQuestion,
|
|
41
|
+
QuizSet,
|
|
42
|
+
Quota,
|
|
43
|
+
RetryPolicy,
|
|
44
|
+
SlideDeck,
|
|
45
|
+
SourceValidation,
|
|
46
|
+
TaskResult,
|
|
47
|
+
TaskStatus,
|
|
48
|
+
TaskSubmission,
|
|
49
|
+
UrllibTransport,
|
|
50
|
+
UserProfile,
|
|
51
|
+
Video,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"NotexClient",
|
|
56
|
+
"AsyncNotexClient",
|
|
57
|
+
"NotexAPIError",
|
|
58
|
+
"NotexAuthenticationError",
|
|
59
|
+
"NotexPermissionError",
|
|
60
|
+
"NotexRateLimitError",
|
|
61
|
+
"NotexUploadError",
|
|
62
|
+
"NotexTaskError",
|
|
63
|
+
"Credits",
|
|
64
|
+
"GeneratedNote",
|
|
65
|
+
"Flashcard",
|
|
66
|
+
"FlashcardSet",
|
|
67
|
+
"QuizQuestion",
|
|
68
|
+
"QuizSet",
|
|
69
|
+
"MindmapNode",
|
|
70
|
+
"Mindmap",
|
|
71
|
+
"Podcast",
|
|
72
|
+
"Video",
|
|
73
|
+
"SlideDeck",
|
|
74
|
+
"Quota",
|
|
75
|
+
"UserProfile",
|
|
76
|
+
"NoteItem",
|
|
77
|
+
"NotesPage",
|
|
78
|
+
"SourceValidation",
|
|
79
|
+
"TaskSubmission",
|
|
80
|
+
"TaskResult",
|
|
81
|
+
"TaskStatus",
|
|
82
|
+
"RetryPolicy",
|
|
83
|
+
"UrllibTransport",
|
|
84
|
+
"DEFAULT_BASE_URL",
|
|
85
|
+
"API_V1",
|
|
86
|
+
"API_V2",
|
|
87
|
+
"CREDITS_ME_ENDPOINT",
|
|
88
|
+
"CREDITS_QUOTA_ENDPOINT",
|
|
89
|
+
"USER_PROFILE_ENDPOINT",
|
|
90
|
+
"USER_NOTES_ENDPOINT",
|
|
91
|
+
"TASK_RESULT_ENDPOINT",
|
|
92
|
+
"PRESIGNED_URL_ENDPOINT",
|
|
93
|
+
"NOTE_CREATE_ENDPOINT",
|
|
94
|
+
"NOTE_VALIDATE_ENDPOINT",
|
|
95
|
+
"FLASHCARDS_CREATE_ENDPOINT",
|
|
96
|
+
"MINDMAP_CREATE_ENDPOINT",
|
|
97
|
+
"PODCAST_CREATE_ENDPOINT",
|
|
98
|
+
"QUIZ_CREATE_ENDPOINT",
|
|
99
|
+
"SHORTS_CREATE_ENDPOINT",
|
|
100
|
+
"QUIZ_VIDEO_CREATE_ENDPOINT",
|
|
101
|
+
"SLIDE_CREATE_ENDPOINT",
|
|
102
|
+
"TRANSLATE_CREATE_ENDPOINT",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
__version__ = "0.1.0"
|
notex/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,323 @@
|
|
|
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.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
notex/__init__.py,sha256=OBOztsFwlrmVvQ82zOyetq58UohSSsjuVcwu__dCugk,2220
|
|
2
|
+
notex/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
|
+
notex_python-0.1.0.dist-info/licenses/LICENSE,sha256=0r4I_CjDc3JvT4P7yEovJieS72PEtUVTtNbiiWEpQTg,1062
|
|
4
|
+
notex_sdk/__init__.py,sha256=8OGtE4hcOHrug2l8axHLFJOFHsYYgxzSa7tJMLtcq4U,2309
|
|
5
|
+
notex_sdk/async_client.py,sha256=scUJa3dCh8SRQ5DofNbSNkHlya7enOcsSXpeocW-eJI,4534
|
|
6
|
+
notex_sdk/client.py,sha256=LtJMlRydqecwo9gOaKOTx-nO9tGEbgUTqNYg085HrWw,21463
|
|
7
|
+
notex_sdk/config.py,sha256=g45ngEVeBkVgHUmEI_yC8ylntpVKkLScCR3kqG-LpY8,931
|
|
8
|
+
notex_sdk/errors.py,sha256=U2spHvwdDhEqzHeWmhmGEuVyW2u8e_MfGB4hB2euMpo,1260
|
|
9
|
+
notex_sdk/models.py,sha256=HrofQN5H3fs6OQN4nmYSIw_3yEFgq0BJ9hvCwfnrzHI,15794
|
|
10
|
+
notex_sdk/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
11
|
+
notex_sdk/transport.py,sha256=Fse4oU5jwKRqz_SCbq8u4Cm3H9V9VfovU_-GyMn813s,5148
|
|
12
|
+
notex_python-0.1.0.dist-info/METADATA,sha256=RvuaRYHtu391qrzvGqxaDK4HXElWJwQWGs2XaUU8liE,9111
|
|
13
|
+
notex_python-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
14
|
+
notex_python-0.1.0.dist-info/top_level.txt,sha256=grCQy8NrHm0nJ2DNQmu1uzZg2G56SRfPBbcHLSYjI7Q,16
|
|
15
|
+
notex_python-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NoteX
|
|
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.
|
notex_sdk/__init__.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Python SDK for the NoteX API."""
|
|
2
|
+
|
|
3
|
+
from .async_client import AsyncNotexClient
|
|
4
|
+
from .client import NotexClient
|
|
5
|
+
from .config import (
|
|
6
|
+
API_V1,
|
|
7
|
+
API_V2,
|
|
8
|
+
CREDITS_ME_ENDPOINT,
|
|
9
|
+
CREDITS_QUOTA_ENDPOINT,
|
|
10
|
+
DEFAULT_BASE_URL,
|
|
11
|
+
FLASHCARDS_CREATE_ENDPOINT,
|
|
12
|
+
MINDMAP_CREATE_ENDPOINT,
|
|
13
|
+
NOTE_CREATE_ENDPOINT,
|
|
14
|
+
NOTE_VALIDATE_ENDPOINT,
|
|
15
|
+
PODCAST_CREATE_ENDPOINT,
|
|
16
|
+
PRESIGNED_URL_ENDPOINT,
|
|
17
|
+
QUIZ_CREATE_ENDPOINT,
|
|
18
|
+
QUIZ_VIDEO_CREATE_ENDPOINT,
|
|
19
|
+
SHORTS_CREATE_ENDPOINT,
|
|
20
|
+
SLIDE_CREATE_ENDPOINT,
|
|
21
|
+
TASK_RESULT_ENDPOINT,
|
|
22
|
+
TRANSLATE_CREATE_ENDPOINT,
|
|
23
|
+
USER_NOTES_ENDPOINT,
|
|
24
|
+
USER_PROFILE_ENDPOINT,
|
|
25
|
+
)
|
|
26
|
+
from .errors import (
|
|
27
|
+
NotexAPIError,
|
|
28
|
+
NotexAuthenticationError,
|
|
29
|
+
NotexPermissionError,
|
|
30
|
+
NotexRateLimitError,
|
|
31
|
+
NotexTaskError,
|
|
32
|
+
NotexUploadError,
|
|
33
|
+
)
|
|
34
|
+
from .models import (
|
|
35
|
+
Credits,
|
|
36
|
+
Flashcard,
|
|
37
|
+
FlashcardSet,
|
|
38
|
+
GeneratedNote,
|
|
39
|
+
Mindmap,
|
|
40
|
+
MindmapNode,
|
|
41
|
+
NoteItem,
|
|
42
|
+
NotesPage,
|
|
43
|
+
Podcast,
|
|
44
|
+
QuizQuestion,
|
|
45
|
+
QuizSet,
|
|
46
|
+
Quota,
|
|
47
|
+
SlideDeck,
|
|
48
|
+
SourceValidation,
|
|
49
|
+
TaskResult,
|
|
50
|
+
TaskStatus,
|
|
51
|
+
TaskSubmission,
|
|
52
|
+
UserProfile,
|
|
53
|
+
Video,
|
|
54
|
+
)
|
|
55
|
+
from .transport import RetryPolicy, UrllibTransport
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
"NotexClient",
|
|
59
|
+
"AsyncNotexClient",
|
|
60
|
+
"NotexAPIError",
|
|
61
|
+
"NotexAuthenticationError",
|
|
62
|
+
"NotexPermissionError",
|
|
63
|
+
"NotexRateLimitError",
|
|
64
|
+
"NotexUploadError",
|
|
65
|
+
"NotexTaskError",
|
|
66
|
+
"Credits",
|
|
67
|
+
"GeneratedNote",
|
|
68
|
+
"Flashcard",
|
|
69
|
+
"FlashcardSet",
|
|
70
|
+
"QuizQuestion",
|
|
71
|
+
"QuizSet",
|
|
72
|
+
"MindmapNode",
|
|
73
|
+
"Mindmap",
|
|
74
|
+
"Podcast",
|
|
75
|
+
"Video",
|
|
76
|
+
"SlideDeck",
|
|
77
|
+
"Quota",
|
|
78
|
+
"UserProfile",
|
|
79
|
+
"NoteItem",
|
|
80
|
+
"NotesPage",
|
|
81
|
+
"SourceValidation",
|
|
82
|
+
"TaskSubmission",
|
|
83
|
+
"TaskResult",
|
|
84
|
+
"TaskStatus",
|
|
85
|
+
"RetryPolicy",
|
|
86
|
+
"UrllibTransport",
|
|
87
|
+
"DEFAULT_BASE_URL",
|
|
88
|
+
"API_V1",
|
|
89
|
+
"API_V2",
|
|
90
|
+
"CREDITS_ME_ENDPOINT",
|
|
91
|
+
"CREDITS_QUOTA_ENDPOINT",
|
|
92
|
+
"USER_PROFILE_ENDPOINT",
|
|
93
|
+
"USER_NOTES_ENDPOINT",
|
|
94
|
+
"TASK_RESULT_ENDPOINT",
|
|
95
|
+
"PRESIGNED_URL_ENDPOINT",
|
|
96
|
+
"NOTE_CREATE_ENDPOINT",
|
|
97
|
+
"NOTE_VALIDATE_ENDPOINT",
|
|
98
|
+
"FLASHCARDS_CREATE_ENDPOINT",
|
|
99
|
+
"MINDMAP_CREATE_ENDPOINT",
|
|
100
|
+
"PODCAST_CREATE_ENDPOINT",
|
|
101
|
+
"QUIZ_CREATE_ENDPOINT",
|
|
102
|
+
"SHORTS_CREATE_ENDPOINT",
|
|
103
|
+
"QUIZ_VIDEO_CREATE_ENDPOINT",
|
|
104
|
+
"SLIDE_CREATE_ENDPOINT",
|
|
105
|
+
"TRANSLATE_CREATE_ENDPOINT",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Async adapter for the dependency-free synchronous NoteX client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Mapping, Optional, Union
|
|
8
|
+
|
|
9
|
+
from .client import NotexClient
|
|
10
|
+
from .models import (
|
|
11
|
+
Credits,
|
|
12
|
+
NotesPage,
|
|
13
|
+
Quota,
|
|
14
|
+
SourceValidation,
|
|
15
|
+
TaskResult,
|
|
16
|
+
TaskSubmission,
|
|
17
|
+
UserProfile,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AsyncNotexClient:
|
|
22
|
+
"""Run a request-scoped :class:`NotexClient` without blocking an event loop.
|
|
23
|
+
|
|
24
|
+
This adapter delegates blocking I/O to a worker thread. It is intentionally
|
|
25
|
+
small; it keeps sync and async behavior identical without adding an HTTP
|
|
26
|
+
dependency to the SDK.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, client: Optional[NotexClient] = None, **client_options: Any) -> None:
|
|
30
|
+
if client is not None and client_options:
|
|
31
|
+
raise ValueError("pass either client or client options, not both")
|
|
32
|
+
self._client = client or NotexClient(**client_options)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def sync_client(self) -> NotexClient:
|
|
36
|
+
return self._client
|
|
37
|
+
|
|
38
|
+
async def get_credits(self, **options: Any) -> Credits:
|
|
39
|
+
return await asyncio.to_thread(self._client.get_credits, **options)
|
|
40
|
+
|
|
41
|
+
async def get_quota(self, **options: Any) -> Quota:
|
|
42
|
+
return await asyncio.to_thread(self._client.get_quota, **options)
|
|
43
|
+
|
|
44
|
+
async def get_profile(self, **options: Any) -> UserProfile:
|
|
45
|
+
return await asyncio.to_thread(self._client.get_profile, **options)
|
|
46
|
+
|
|
47
|
+
async def list_notes(self, **options: Any) -> NotesPage:
|
|
48
|
+
return await asyncio.to_thread(self._client.list_notes, **options)
|
|
49
|
+
|
|
50
|
+
async def validate_source(self, **options: Any) -> SourceValidation:
|
|
51
|
+
return await asyncio.to_thread(self._client.validate_source, **options)
|
|
52
|
+
|
|
53
|
+
async def create_note(self, **options: Any) -> TaskSubmission:
|
|
54
|
+
return await asyncio.to_thread(self._client.create_note, **options)
|
|
55
|
+
|
|
56
|
+
async def create_note_from_url(self, web_url: str, **options: Any) -> TaskSubmission:
|
|
57
|
+
return await asyncio.to_thread(self._client.create_note_from_url, web_url, **options)
|
|
58
|
+
|
|
59
|
+
async def create_note_from_file_url(self, file_url: str, **options: Any) -> TaskSubmission:
|
|
60
|
+
return await asyncio.to_thread(self._client.create_note_from_file_url, file_url, **options)
|
|
61
|
+
|
|
62
|
+
async def create_note_from_file(
|
|
63
|
+
self, file_path: Union[str, Path], **options: Any
|
|
64
|
+
) -> TaskSubmission:
|
|
65
|
+
return await asyncio.to_thread(self._client.create_note_from_file, file_path, **options)
|
|
66
|
+
|
|
67
|
+
async def create_flashcards(self, note_id: str, **options: Any) -> TaskSubmission:
|
|
68
|
+
return await asyncio.to_thread(self._client.create_flashcards, note_id, **options)
|
|
69
|
+
|
|
70
|
+
async def create_mindmap(self, note_id: str, **options: Any) -> TaskSubmission:
|
|
71
|
+
return await asyncio.to_thread(self._client.create_mindmap, note_id, **options)
|
|
72
|
+
|
|
73
|
+
async def create_podcast(self, note_id: str, duration: int, **options: Any) -> TaskSubmission:
|
|
74
|
+
return await asyncio.to_thread(self._client.create_podcast, note_id, duration, **options)
|
|
75
|
+
|
|
76
|
+
async def create_quiz(self, note_id: str, **options: Any) -> TaskSubmission:
|
|
77
|
+
return await asyncio.to_thread(self._client.create_quiz, note_id, **options)
|
|
78
|
+
|
|
79
|
+
async def create_shorts(self, note_id: str, voice_id: str, **options: Any) -> TaskSubmission:
|
|
80
|
+
return await asyncio.to_thread(self._client.create_shorts, note_id, voice_id, **options)
|
|
81
|
+
|
|
82
|
+
async def create_quiz_video(self, note_id: str, voice_id: str, **options: Any) -> TaskSubmission:
|
|
83
|
+
return await asyncio.to_thread(self._client.create_quiz_video, note_id, voice_id, **options)
|
|
84
|
+
|
|
85
|
+
async def create_slide(self, note_id: str, template_id: str, **options: Any) -> TaskSubmission:
|
|
86
|
+
return await asyncio.to_thread(self._client.create_slide, note_id, template_id, **options)
|
|
87
|
+
|
|
88
|
+
async def create_translation(self, note_id: str, language: str, **options: Any) -> TaskSubmission:
|
|
89
|
+
return await asyncio.to_thread(self._client.create_translation, note_id, language, **options)
|
|
90
|
+
|
|
91
|
+
async def get_task_result(self, task_id: str, **options: Any) -> TaskResult:
|
|
92
|
+
return await asyncio.to_thread(self._client.get_task_result, task_id, **options)
|
|
93
|
+
|
|
94
|
+
async def wait_for_task(self, task_id: str, **options: Any) -> TaskResult:
|
|
95
|
+
return await asyncio.to_thread(self._client.wait_for_task, task_id, **options)
|
|
96
|
+
|
|
97
|
+
async def request(
|
|
98
|
+
self, method: str, path: str, **options: Any
|
|
99
|
+
) -> Mapping[str, Any]:
|
|
100
|
+
return await asyncio.to_thread(self._client.request, method, path, **options)
|