Metadata-Version: 2.5
Name: yousleep_common
Version: 12.10.3
Summary: YouSleep Common
Project-URL: Homepage, https://yousleep.ai
Project-URL: Source, https://github.com/yousleep-ai/common
Project-URL: Issues, https://github.com/yousleep-ai/common/issues
Author-email: Mathias Perslev <mp@yousleep.ai>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: api-client,edf,eeg,polysomnography,sdk,sleep
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Healthcare Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: gitpython>=3.1.47
Requires-Dist: httpx>=0.28.1
Requires-Dist: mkdocs-gen-files>=0.6.0
Requires-Dist: numpy>=2.0.0
Requires-Dist: pydantic[email]>=2.12.5
Requires-Dist: pyyaml>=6.0.3
Provides-Extra: system
Requires-Dist: nvidia-ml-py>=13.590.44; extra == 'system'
Description-Content-Type: text/markdown

# SDK & shared models

Type-safe async Python SDK and the shared Pydantic models for the
[youSleep](https://yousleep.ai) sleep-analysis platform. Published to PyPI as
`yousleep-common`.

Analyze a sleep recording in three lines:

```python
from yousleep_common.client import AsyncClient

async with AsyncClient(base_url="https://api.yousleep.ai", token="...") as client:
    result = await client.workflows.analyze_file(
        file="night.edf", analysis_config_id="u-sleep-research-v1"
    )
    print(result.biomarkers.biomarkers.tst_min, len(result.events))
```

## What it covers

| Area | Detail |
|---|---|
| Workflows | `client.workflows` runs upload → submit → poll → fetch in one call; the `_temporary` variants delete what they created on block exit, including on exception |
| Multi-file batches | `analyze_files` submits and polls through the server's batch endpoints; per-file outcomes are returned rather than raised, so one bad file does not abort the batch. The server caps a batch at 500 items |
| Endpoint namespaces | `admin`, `analyses`, `auth`, `batch`, `billing`, `legal`, `projects`, `recordings`, `reports`, `status`, `studies`, `user`, `workflows` — checked against the server's OpenAPI spec in CI (`make verify-routes`) |
| Uploads | Presigned S3 PUT, streamed in 64 KiB chunks with an optional progress callback, so the file is not held in memory |
| Typing | Pydantic v2 request/response models, `py.typed`, mypy strict |
| Auth and errors | JWT with automatic refresh, retry with exponential backoff on rate limits, and one exception per failure mode (see below) |

## Installation

```bash
pip install yousleep-common
```

Requires Python 3.12+.

## Quick start

### Authenticate

```python
from yousleep_common.client import AsyncClient
from yousleep_common.models import UserAuthentication

client = await AsyncClient.from_credentials(
    base_url="https://api.yousleep.ai",
    credentials=UserAuthentication(email="you@example.com", password="..."),
)
```

Or pass a JWT directly: `AsyncClient(base_url=..., token=...)`.

### Analyze a file end-to-end

```python
result = await client.workflows.analyze_file(
    file="night.edf",
    analysis_config_id="u-sleep-research-v1",
    study_name="Subject 001",   # optional; inferred from filename if omitted
    age=35, sex="male",         # optional subject metadata
)
result.events                 # list[Event] | None
result.biomarkers             # BiomarkerResult | None
result.biomarkers.biomarkers  # Biomarkers: tst_min, tib_min, sleep_efficiency_pct, …
```

`analyze_file_temporary` is the ephemeral variant: it deletes everything it
created when the block exits, including on error.

```python
async with client.workflows.analyze_file_temporary(
    file="night.edf", analysis_config_id="u-sleep-research-v1"
) as result:
    export(result.biomarkers)
# project, study, recording, and analysis no longer exist
```

### Score many files at once

```python
result = await client.workflows.analyze_files(
    files=["sub-01.edf", "sub-02.edf", "sub-03.edf"],
    analysis_config_id="u-sleep-research-v1",
)
for ok in result.succeeded:
    print(ok.file, ok.result.biomarkers)
for bad in result.failed:
    print(bad.file, bad.status, bad.error)   # per-file; never aborts the batch
```

### Use the low-level client

Every REST resource is a typed namespace on the client:

```python
from pathlib import Path

from yousleep_common.models import ProjectCreate, StudyCreate, AnalysisRequest

project = await client.projects.create(ProjectCreate(name="My Study 2026"))
study = await client.studies.create(project.id, StudyCreate(name="Subject 001"))
# `upload` takes a Path or an open binary file — not a str path.
recording = await client.recordings.upload(project.id, study.id, Path("night.edf"))
analysis = await client.analyses.submit(
    project.id, study.id, recording.id,
    AnalysisRequest(analysis_config_id="u-sleep-research-v1"),
)
analysis = await client.analyses.wait_for(
    project.id, study.id, recording.id, analysis.id, timeout=3600
)
events = await client.analyses.get_events(project.id, study.id, recording.id, analysis.id)
```

`wait_for` raises `ClientTimeoutError` — not the builtin `TimeoutError` — when
`timeout` elapses before a terminal status.

## Error handling & usage limits

All SDK errors derive from `YouSleepClientError`:

```python
from yousleep_common.client import (
    AnalysisWorkflowError,   # analysis ended failed/cancelled (carries logs)
    AuthenticationError,     # 401
    InsufficientCreditsError,  # 402 — not enough credits
    NotFoundError,           # 404
    QuotaExceededError,      # usage quota hit (projects/studies/analyses/hours)
    RateLimitError,          # 429 throttling (retried automatically first)
    ValidationError,         # 422
)
```

Quota errors are raised immediately rather than retried. Check limits and
current consumption up front:

```python
limits = await client.user.usage_limits()     # your account's usage limits (None = unlimited)
usage = await client.user.usage_detailed()    # your current usage
print(usage.storage.active, "/", limits.storage.active, "bytes")
```

In multi-file workflows, a quota hit on one file fails only that file's
outcome — the rest of the batch continues.

## Shared models

`yousleep_common.models` and `yousleep_common.types` are the platform's shared
contract — the same Pydantic models and enums used by the youSleep API server.
Import them for type-safe request building and response handling:

```python
from yousleep_common.models import AnalysisRequest, Event, StudyCreate
from yousleep_common.types import AnalysisStatus, AnalysisType, EventLabel
```

## Documentation

- [SDK guide](docs/sdk-guide.md) — installing, authenticating, the shape of the client
- [Workflows guide](docs/sdk-workflows.md) — the high-level helpers in depth
- [API reference](https://docs.yousleep.ai/reference/Common/) — generated from the
  source; serve it locally with `make docs-serve`

## Development

```bash
make install   # uv sync
make check     # ruff, mypy (strict), deptry, lock check
make test      # pytest
make verify-routes  # SDK ↔ OpenAPI spec coverage check
```

Releases are automated with python-semantic-release (Angular commit
convention).

## License

[Apache-2.0](LICENSE).

## Support

- [GitHub Issues](https://github.com/yousleep-ai/common/issues)
- support@yousleep.ai
