Metadata-Version: 2.4
Name: notex-python
Version: 0.2.0
Summary: Typed, dependency-free Python SDK for the NoteX API
Author: NoteX
License-Expression: MIT
Project-URL: Homepage, https://notexapp.com
Project-URL: Documentation, https://be-docs.notexapp.com
Project-URL: Repository, https://github.com/sota-labs/notex-python-sdk
Project-URL: Issues, https://github.com/sota-labs/notex-python-sdk/issues
Keywords: notex,ai,notes,sdk,api-client
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pyright>=1.1.400; extra == "dev"
Requires-Dist: ruff>=0.12; extra == "dev"
Requires-Dist: twine>=6; extra == "dev"
Dynamic: license-file

# NoteX Python SDK

[![PyPI version](https://img.shields.io/pypi/v/notex-python)](https://pypi.org/project/notex-python/)
[![Python versions](https://img.shields.io/pypi/pyversions/notex-python)](https://pypi.org/project/notex-python/)

Typed, dependency-free Python SDK for API-key-enabled NoteX endpoints.

```bash
pip install notex-python
```

```python
from notex import Notex

client = Notex(api_key="ntx_live_xxx")
task = client.notes.create(text="Summarize this content.")
print(task.id)
```

Requires Python 3.9 or newer.

`text` is uploaded internally as a temporary UTF-8 text file because the
NoteX create endpoint consumes URL or file sources. The temporary local file
is removed immediately after NoteX accepts the asynchronous task.

Use `task.result()` for one status check or `task.wait()` from a worker:

```python
result = task.wait(timeout=120, poll_interval=5)
```

`NotexClient` remains the typed low-level client for explicit endpoint access.

## Naming convention

The public SDK has one flat, explicit naming convention:

| Operation | Convention | Examples |
| --- | --- | --- |
| Read one value | `get_<resource>()` | `get_profile()`, `get_task_result()` |
| Read a collection | `list_<resources>()` | `list_notes()` |
| Validate input | `validate_<resource>()` | `validate_source()` |
| Create content | `create_<resource>()` | `create_note()`, `create_quiz()` |
| Wait for an async operation | `wait_for_<resource>()` | `wait_for_task()` |

Sync and async clients expose the same method names. The async variants are
awaited.

## Multi-user integration

Each `Notex` or `NotexClient` instance represents one user API key for one
request or background job. Do not mutate a singleton client's key between
users.

```python
from notex import NotexClient


def notex_for_connection(connection_id: str) -> NotexClient:
    api_key = credential_store.decrypt(connection_id)
    return NotexClient(api_key=api_key)
```

The SDK does not create, list, delete, persist, or automatically load API
keys. The integrating backend owns credential storage and rotation.

## Supported methods

### Account and notes

```python
credits = client.get_credits()
quota = client.get_quota()
profile = client.get_profile()
notes = client.list_notes(limit=20, sort_field="createdAt", sort_order=-1)
```

Credit fields vary by account/plan. `Credits` exposes optional
`total_credits`, `reward_credits`, `purchased_credits`, `user_type`, and
`has_paid`, while `raw` preserves the complete backend payload. `balance` is
an optional compatibility alias for responses that provide `balance` or
`total_credits`.

### Validate and create notes

Create a note from a web URL:

```python
submission = client.create_note(
    web_url="https://youtu.be/example",
    language_hints=["en"],
)
```

Convenience equivalent:

```python
submission = client.create_note_from_url(
    "https://youtu.be/example",
    language_hints=["en"],
)
```

Create a note from an existing NoteX file URL:

```python
submission = client.create_note_from_file_url(
    "audio/user-1/lecture.mp3",
    language_hints=["en"],
)
```

Create a note from a local file:

```python
submission = client.create_note_from_file(
    "./lecture.mp3",
    upload_file_name="lecture.mp3",
    language_hints=["en"],
    content_type="audio/mpeg",
)
```

The local-file method performs the complete internal flow:

1. Request a presigned upload contract.
2. Upload the file directly without the NoteX API key.
3. Submit note creation using the returned `file_url`.

Presign and direct-upload helpers are intentionally private. Use
`upload_file_name` when the local filename contains characters rejected by the
target gateway or object storage.

Validate a source before spending credits:

```python
validation = client.validate_source(web_url="https://youtu.be/example")
```

Exactly one of `web_url` or `file_url` is accepted by `create_note()` and
`validate_source()`.

### Create content from a note

All create methods return `TaskSubmission` with a `task_id`.

```python
flashcards = client.create_flashcards(
    "note-id",
    num_cards=10,
    difficulty="medium",
)
quiz = client.create_quiz("note-id", num_questions=10)
mindmap = client.create_mindmap("note-id")
podcast = client.create_podcast("note-id", duration=180)
shorts = client.create_shorts("note-id", voice_id="en-US-Standard-A")
quiz_video = client.create_quiz_video("note-id", voice_id="en-US-Standard-A")
slides = client.create_slide("note-id", template_id="default", language="en")
translation = client.create_translation("note-id", language="vi")
```

### Poll task results

Use `get_task_result()` when the integrating backend manages scheduling:

```python
result = client.get_task_result(submission.task_id)
```

Use `wait_for_task()` in a worker when a blocking helper is appropriate:

```python
result = client.wait_for_task(
    submission.task_id,
    poll_interval=5,
    timeout=120,
)
```

Do not run file uploads or blocking task polling directly in a web request.
Use a durable queue or background worker.

Completed results can be parsed into feature-specific typed models:

```python
from notex import FlashcardSet

result = client.wait_for_task(flashcards.task_id)
flashcard_set = FlashcardSet.from_task_result(result)
print(flashcard_set.cards[0].front)
```

Available result models include `GeneratedNote`, `FlashcardSet`, `QuizSet`,
`Mindmap`, `Podcast`, `Video`, and `SlideDeck`.

## Async client

`AsyncNotexClient` exposes the same names and runs blocking standard-library
I/O in worker threads so the event loop remains responsive.

```python
from notex import AsyncNotexClient

client = AsyncNotexClient(api_key="ntx_live_xxx")

submission = await client.create_quiz("note-id", num_questions=10)
result = await client.wait_for_task(submission.task_id, timeout=120)
```

## Errors and retry behavior

```python
from notex import NotexAuthenticationError, NotexRateLimitError

try:
    client.get_credits()
except NotexAuthenticationError:
    reconnect_notex_account()
except NotexRateLimitError as error:
    reschedule_job(error.retry_after)
```

Public errors:

- `NotexAPIError`
- `NotexAuthenticationError`
- `NotexPermissionError`
- `NotexRateLimitError`
- `NotexUploadError`
- `NotexTaskError`

GET and HEAD requests retry transient network failures and HTTP 429/5xx
responses, honoring `Retry-After`. Content-creating POST requests are not
retried by default because repeating them can create duplicate work.

## API compatibility

Every high-level method accepts `endpoint=` and `base_url=` overrides. This
allows an integration to adopt a new backend version before the SDK releases
an update.

```python
client.get_credits(endpoint="/v3/credits/me")
client.create_flashcards("note-id", endpoint="/v7/create/flashcards")
```

For a documented endpoint that is not wrapped yet, use the low-level escape
hatch:

```python
response = client.request(
    "POST",
    "/v10/feature",
    form={"note_id": "note-id"},
    headers={"Idempotency-Key": "internal-job-id"},
)
```

## Real API integration tests

Copy the safe template and put the real key/base URL in the local file:

```powershell
Copy-Item .notex-test.env.example .notex-test.env
```

Edit `.notex-test.env`:

```dotenv
NOTEX_TEST_API_KEY=ntx_test_your_real_key
NOTEX_TEST_BASE_URL=https://api.notexapp.com
```

The file is ignored by Git. Read-only tests cover profile, credits, quota, and
note listing:

```bash
python -m pytest tests/test_staging_integration.py -v
```

Creation tests can upload files and spend credits, so they require explicit
opt-in:

```dotenv
NOTEX_RUN_WRITE_TESTS=1
NOTEX_TEST_WEB_URL=https://youtu.be/example
NOTEX_TEST_FILE=C:\path\to\lecture.mp3
NOTEX_TEST_NOTE_ID=existing-note-id
NOTEX_TEST_FEATURES=flashcards,quiz,mindmap,translation
```

Optional feature configuration is documented in
`.notex-test.env.example`. Test output is not persisted by the suite; local
credentials and `.notex-test-results/` are both excluded from Git.

The exact README smoke flow is also covered by an opt-in real integration
test:

```powershell
python -m pytest tests/test_staging_integration.py::test_real_readme_smoke_create_note_from_text -v
```

## Development and release checks

Install development tools once:

```bash
python -m pip install -e ".[dev]"
```

Run the complete local release gate on Windows:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\check-local.ps1
```

This runs linting, type checking, non-network unit tests, package build,
`twine check`, clean-wheel installation, and a public import/version smoke
test. It never reads `PYPI_API_KEY` and never publishes.

On GitHub, changing `project.version` in `pyproject.toml` on `main`
automatically triggers the publish workflow. It checks PyPI first and skips
the upload when that exact version already exists. Pull requests never
receive the PyPI secret.

Verify the package published on PyPI in a clean Windows environment:

```powershell
py -m venv .verify
.\.verify\Scripts\python.exe -m pip install --upgrade notex-python
.\.verify\Scripts\python.exe -c "from notex import Notex; print(Notex)"
.\.verify\Scripts\python.exe -c "import importlib.metadata; print(importlib.metadata.version('notex-python'))"
```

See [RELEASING.md](RELEASING.md) for the repository-secret release flow.
Never log, commit, or return API keys to frontend clients.
