Metadata-Version: 2.5
Name: yousleep_common
Version: 14.1.0
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 |
| Presentation models | Server-computed tables and charts (formatted values, units, curated `summary` flags, status tokens, hover descriptions) so every renderer draws the same thing |
| 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             # AnalysisBiomarkersResponse | None
result.biomarkers.biomarkers  # Biomarkers: tst_min, tib_min, sleep_efficiency_pct, …
result.biomarkers.table       # TableModel | None — the server-defined presentation table
```

`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

- [Integration guide](docs/integration-guide.md) — the end-to-end path for an
  external system: credentials, ingestion, submission, polling, results, and the
  limits that shape all four
- [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

Those three pages live in the source repository and are **not shipped in the
package**, so the links above resolve on GitHub and not from PyPI. The generated
API reference is served from `docs.yousleep.ai`. Both that site and the
repository are **access-restricted**: if you are integrating against the platform
and cannot reach them, ask support rather than assuming the links are broken.

What does travel with the package is the code and its docstrings, so `help()`
and an IDE resolve every signature offline without any of the above.

## 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

- support@yousleep.ai — the channel for everyone, and the only one if you reached
  this package from PyPI
- [GitHub Issues](https://github.com/yousleep-ai/common/issues) — requires access
  to the `yousleep-ai` organisation
