Metadata-Version: 2.4
Name: nuuduu
Version: 0.6.0
Summary: Python SDK and CLI for Nuuduu Atlas — sync robotics training datasets (LeRobot, MCAP, HEVC) to your local machine.
Keywords: robotics,lerobot,pytorch,dataset,atlas,mcap,imitation-learning,physical-ai
Author: Nuuduu UAB
Author-email: Nuuduu UAB <info@nuuduu.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Classifier: Environment :: Console
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: nuuduu[cli] ; extra == 'all'
Requires-Dist: typer>=0.12 ; extra == 'cli'
Requires-Dist: rich>=13 ; extra == 'cli'
Requires-Dist: pytest>=8 ; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30 ; extra == 'dev'
Requires-Dist: typer>=0.12 ; extra == 'dev'
Requires-Dist: rich>=13 ; extra == 'dev'
Requires-Dist: build ; extra == 'dev'
Requires-Dist: twine ; extra == 'dev'
Requires-Python: >=3.10
Project-URL: Homepage, https://nuuduu.ai/atlas
Project-URL: Documentation, https://pypi.org/project/nuuduu/
Project-URL: Repository, https://bitbucket.org/nuuduu/nuuduu
Project-URL: Issues, https://bitbucket.org/nuuduu/nuuduu/issues
Project-URL: Changelog, https://bitbucket.org/nuuduu/nuuduu/src/main/CHANGELOG.md
Provides-Extra: all
Provides-Extra: cli
Provides-Extra: dev
Description-Content-Type: text/markdown

# Nuuduu

[![PyPI version](https://badge.fury.io/py/nuuduu.svg)](https://pypi.org/project/nuuduu/)
[![Python versions](https://img.shields.io/pypi/pyversions/nuuduu.svg)](https://pypi.org/project/nuuduu/)
[![Build status](https://img.shields.io/bitbucket/pipelines/nuuduu/nuuduu/main?style=flat-square&logo=bitbucket)](https://bitbucket.org/nuuduu/nuuduu/pipelines)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Nuuduu** is a Python SDK and CLI that gets robotics training data from the [Nuuduu Atlas](https://nuuduu.ai/atlas) library onto your machine as a **PyTorch-ready dataset** — and automatically requests new data collection when the library does not have enough.

| Step | What it does |
|---|---|
| **Search** | Find episodes in the Atlas library by task description and country |
| **Bundle** | Package matching episodes into a training dataset (LeRobot by default) |
| **Sync** | Download ready bundles to your local dataset directory |
| **Request** | Order new episodes to be collected when the library is short |

Use what Atlas already has, or combine `--min-episodes` on bundle to **bundle available episodes and request the rest in one command**.

| | URL |
|---|---|
| Atlas web app | https://nuuduu.ai/atlas |
| Atlas API (default) | https://nuuduu.ai/api/atlas |

The CLI talks to the API at `nuuduu.ai`. Manage your account and payment method in the [Atlas web app](https://nuuduu.ai/atlas).

Source repository: [bitbucket.org/nuuduu/nuuduu](https://bitbucket.org/nuuduu/nuuduu)

## Installation

```bash
# Library only
pip install nuuduu

# Library + CLI command (quote brackets so the shell does not treat them as globs)
pip install 'nuuduu[cli]'
```

Development install:

```bash
git clone git@bitbucket.org:nuuduu/nuuduu.git
cd nuuduu
pip install -e '.[dev]'
pytest
```

## How it works

```text
Atlas library                    Your machine
─────────────                    ────────────
  search  ──→  find episodes
  bundle  ──→  package as LeRobot / MCAP / HEVC dataset
  sync    ──→  download  ──→  PyTorch / LeRobot training dataset

  request ──→  collect new episodes (when library is short)
       ↑
       └── triggered automatically by bundle --min-episodes
```

**Typical path — library has enough data:**

```bash
nuuduu search --text "shirt folding"   # explore + see total price
nuuduu bundle --text "shirt folding"   # purchase + package
nuuduu sync                                     # download locally
```

**When you need more than the library has:**

```bash
nuuduu bundle --text "shirt folding" --country fi,ee --min-episodes 10000
# bundles 6 000 available episodes, requests collection of the remaining 4 000
nuuduu sync   # run again as bundles become ready
```

The default output format is **LeRobot v3.0**, ready for PyTorch training via [LeRobot](https://github.com/huggingface/lerobot) or your own data loader.

## Quick start (CLI)

```bash
nuuduu auth login

# 1. Search Atlas
nuuduu search --text "fold a t-shirt"

# 2. Get 1,000 episodes prepared for NVIDIA GR00T
# Existing episodes are used immediately and any shortfall is collected.
nuuduu bundle \
  --text "fold a t-shirt" \
  --min-episodes 1000 \
  --profile groot

# 3. Download the dataset somewhere explicit
nuuduu sync --dataset-dir ~/nuuduu-atlas/datasets

# 4. Train with NVIDIA GR00T
python gr00t/experiment/launch_finetune.py \
  --base-model-path nvidia/GR00T-N1.7-3B \
  --dataset-path ~/nuuduu-atlas/datasets/lerobot/<download-uuid> \
  ...
```

Example output:

```console
$ nuuduu auth login
✓ Logged in as researcher@acme.ai

$ nuuduu search --text "fold a t-shirt"
681 episodes found · $486.32 available now

$ nuuduu bundle --text "fold a t-shirt" \
    --min-episodes 1000 --profile groot
Available now: 681   To collect: 319
Estimated total: $742.00
Confirm purchase? [Y/n] y
✓ Bundle 7f3c2a91 created · LeRobot v3.0 · GR00T

$ nuuduu sync --dataset-dir ~/nuuduu-atlas/datasets
✓ 681 episodes synced
~/nuuduu-atlas/datasets/lerobot/7f3c2a91
```

Need episodes that are not in the library yet? Use `--min-episodes` to bundle what's available and request collection of the rest, or request directly:

```bash
nuuduu bundle --text "shirt folding" --min-episodes 10000 --country fi,ee
nuuduu request --text "pick and place red blocks" --episodes 1000 --country fi,ee
```

## Quick start (library)

```python
from nuuduu import NuuduuAtlas

atlas = NuuduuAtlas.from_config()

# Search the Atlas library
episodes = atlas.search_episodes(text="shirt folding", country="fi,ee", limit=100)
print(f"Found {len(episodes)} episodes")

# Bundle available episodes; automatically request collection for any shortfall
bundle = atlas.bundle_episodes(
    text="shirt folding",
    country="fi,ee",
    min_episodes=10000,
    confirm_request=True,
)
print(f"{bundle.bundled_episodes} bundled, {bundle.requested_episodes} requested")

# Download ready bundles to a local PyTorch-ready dataset
result = atlas.sync(format="lerobot")
for item in result.synced:
    print(item.path)  # e.g. .../lerobot/<uuid>/
```

## Authentication

Use the same email and password as your [Atlas web app](https://nuuduu.ai/atlas) account. The CLI authenticates against the API at `https://nuuduu.ai/api/atlas` with `type=api`.

Credentials are stored at `~/.config/nuuduu/config.toml` with **0600** permissions. The CLI refuses to read config files that are group- or world-readable.

| Method | Usage |
|---|---|
| Config file | `api_token = "..."` in `~/.config/nuuduu/config.toml` |
| Environment | `export NUUDUU_API_TOKEN="..."` |
| Programmatic | `NuuduuAtlas(token="...")` |

```bash
nuuduu auth login              # email + password → saves token
nuuduu auth logout             # clear saved token
nuuduu auth token set TOKEN    # set token manually
nuuduu auth status             # show masked token status
```

Tokens expire after 24 hours in production. On HTTP 401, run `nuuduu auth login` again.

## Payment

When you confirm a bundle or collection request in the CLI, **payment is charged automatically** to the card saved on your [Atlas account](https://nuuduu.ai/atlas). No browser step or extra checkout flow is required.

The only payment issues you may see:

| Situation | What to do |
|---|---|
| No payment method on file | Add a card at [nuuduu.ai/atlas](https://nuuduu.ai/atlas) |
| Card declined or charge failed | Update your payment method in the Atlas web app and retry |

`nuuduu search` shows the total price. `nuuduu bundle` and `nuuduu request` ask for confirmation before charging.

## Dataset directory

Resolved automatically via priority chain:

1. `--dataset-dir` CLI flag
2. `NUUDUU_DATASET_DIR` environment variable
3. `dataset_dir` in config file
4. `HF_LEROBOT_HOME/nuuduu-atlas` (LeRobot ecosystem default)
5. Project `.env`, training configs, or `./datasets/nuuduu-atlas`
6. Fallback: `~/nuuduu-atlas/datasets`

The dataset directory is created automatically on first sync if it does not exist.

```bash
nuuduu config show   # shows resolved path and source
```

Local layout:

```
{dataset_dir}/
  .nuuduu/manifest.json
  lerobot/{download_uuid}/     # LeRobot v3.0 dataset
  mcap/{download_uuid}/        # per-episode .mcap files
  hevc/{download_uuid}/        # per-episode .mp4 files
```

## CLI reference

```bash
nuuduu --help
nuuduu --version

nuuduu auth login
nuuduu auth logout
nuuduu auth token set TOKEN
nuuduu auth status

nuuduu config show
nuuduu config set KEY VALUE

nuuduu sync [--dataset-dir PATH] [--format lerobot|mcap|hevc|all] [--dry-run] [--verify]

nuuduu service-types [--json]

nuuduu search [--text QUERY] [--country fi,ee] [--service-types SLUG[,SLUG...]] [--limit N]
nuuduu bundle [--text QUERY] [--country fi,ee] [--service-types SLUG[,SLUG...]] [--limit N] \
  [--min-episodes N] [--format lerobot|mcap|hevc] \
  [--profile groot|openpi|smolvla|openvla|oxe] \
  [--uuids UUID[,UUID...]] [--wait] [--yes]

nuuduu request --text "TASK" --episodes N [--country fi,ee] [--yes]
nuuduu request list
```

Add `--json` to any command for machine-readable output.

### Shared search options

These options work the same on `search` and `bundle` (`request` supports `--text` and `--country` only):

| Option | Description |
|---|---|
| `--text` / `-t` | Semantic search query / task description |
| `--country` / `-c` | Comma-separated [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) codes (e.g. `fi,ee,de`); sent as `country` (not `countries`); lowercased before sending |
| `--service-types` | Comma-separated service type slugs; resolved to UUIDs via `/api/atlas/assignments` and sent as `assignment` on episode search |
| `--limit` / `-n` | Max episodes to search (defaults to 20) |

### Service types

List available service types (assignments) sorted alphabetically by slug:

```bash
nuuduu service-types
```

```text
folding          7f3c2a91-...
pick-and-place   a1b2c3d4-...
```

Filter search and bundle results by slug:

```bash
nuuduu search --text "fold a t-shirt" --service-types folding,pick-and-place
nuuduu bundle --text "fold a t-shirt" --service-types folding --min-episodes 100
```

`nuuduu search` prints a one-line summary with episode count and total price. `nuuduu bundle` shows a purchase preview and prompts for confirmation before charging your saved payment method.

### Bundle-specific options

| Option | Description |
|---|---|
| `--format` / `-f` | Output format: `lerobot` (default), `mcap`, or `hevc` |
| `--profile` | LeRobot export profile (see [Dataset profiles](#dataset-profiles)) |
| `--min-episodes` | Target episode count; bundles what's available, then quotes a collection request for any shortfall (requires `--text`) |
| `--uuids` / `-u` | Comma-separated episode UUIDs (skips search when you already know them) |
| `--wait` | Poll until the bundle job is ready |
| `--yes` / `-y` | Skip bundle purchase and collection request confirmation prompts |

### Dataset profiles

Use `--profile` on `nuuduu bundle` to prepare the dataset for a specific training stack:

| Profile ID | Name |
|---|---|
| `groot` | NVIDIA Isaac GR00T |
| `openpi` | Physical Intelligence π0 / π0.5 |
| `smolvla` | Hugging Face SmolVLA |
| `openvla` | OpenVLA / OFT |
| `oxe` | Octo / Open X-Embodiment |

## Collection requests

Order new data to be collected when the library does not have enough:

```bash
nuuduu request --text "pick and place red blocks" --episodes 1000 --country fi,ee
```

```
Available now: 681   To collect: 319
Estimated total: $742.00
Confirm purchase? [Y/n]
```

After confirmation, Atlas collects the episodes. Run `nuuduu sync` as bundles become ready.

Note: the collection request API may return 404 until the backend endpoint is fully live — the CLI shows a preview of the intended UX when that happens.

## Output formats

Bundles are written to your local dataset directory in one of these formats:

| Format | Contents | Best for |
|---|---|---|
| `lerobot` (default) | LeRobot v3.0 dataset (meta/, data/, videos/) | **PyTorch / LeRobot training** |
| `mcap` | Per-episode MCAP files | ROS 2 / MCAP tooling |
| `hevc` | Per-episode HEVC MP4 files | Video analysis pipelines |

## Integrity verification

When the Atlas API provides a `hash` field (SHA256 of the bundle zip), `nuuduu sync`:

1. Verifies the downloaded zip before extracting
2. Stores the hash in `.nuuduu/manifest.json`
3. Skips re-download when the local hash matches

```bash
nuuduu sync --verify   # re-check local bundles without downloading
```

## Library API

| Class / function | Purpose |
|---|---|
| `NuuduuAtlas` | High-level facade (recommended) |
| `AtlasClient` | Low-level HTTP API client |
| `SyncEngine` | Sync and verify engine |
| `NuuduuConfig` | Config load/save |
| `resolve_dataset_dir()` | Dataset path resolution |
| `EpisodeSearchOptions` | Shared search parameters |
| `BundleResult` | Result of `bundle_episodes()` (download + optional collection request) |

Key methods on `NuuduuAtlas`:

| Method | Purpose |
|---|---|
| `search_episodes(text, country, limit)` | Search ready episodes |
| `bundle_episodes(text, country, min_episodes, ...)` | Bundle + optional collection request |
| `request_episodes(task, episodes, country)` | Quote/confirm a collection request |
| `sync(format, ...)` | Download ready bundles locally |
| `login()` / `logout()` | Manage API credentials |

Exceptions: `AuthError`, `ApiError`, `IntegrityError`, `ConfigError`, `NotFoundError`

Progress callbacks for sync:

```python
from nuuduu.types import SyncProgressEvent

def on_progress(event: SyncProgressEvent) -> None:
    print(event.phase, event.bytes_done, event.bytes_total)

atlas.sync(on_progress=on_progress)
```

## Troubleshooting

| Problem | Solution |
|---|---|
| HTTP 401 | Run `nuuduu auth login` — token may have expired |
| Config permission error | Run `chmod 600 ~/.config/nuuduu/config.toml` |
| Payment failed / no payment method | Add or update your card at https://nuuduu.ai/atlas, then retry the bundle or request |
| SHA256 mismatch | Re-run `nuuduu sync` to re-download the bundle |
| Request API 404 | `nuuduu request` API not live yet — CLI shows the destination UX preview |
| `--min-episodes` needs `--text` | Provide a task description for the collection request |

## Legal

Copyright © 2026 Nuuduu UAB. Licensed under the [MIT License](LICENSE).

**Nuuduu®** is a registered trademark of Nuuduu UAB. All rights reserved.
The Nuuduu name and logo may not be used to imply endorsement or affiliation
without prior written permission from Nuuduu UAB.
