Metadata-Version: 2.4
Name: dexos-lite
Version: 0.0.3
Summary: Python client for dexos lite model inference and robot control.
Author-email: dexmal <join-maas@dexmal.com>
Project-URL: Homepage, https://maas.dexmal.com
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
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: Programming Language :: Python :: 3.14
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: click>=8.1
Requires-Dist: imageio[ffmpeg]>=2.34
Requires-Dist: numpy>=1.23
Requires-Dist: opencv-python>=4.8
Requires-Dist: Pillow>=10
Requires-Dist: piper_sdk>=0.3.0
Requires-Dist: python-can>=3.3.4
Requires-Dist: pandas>=2.0
Requires-Dist: pyarrow>=14
Requires-Dist: pyrealsense2>=2.54
Requires-Dist: requests>=2.31
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"

# dexos_lite

Command-line client for Dexos lite dataset upload and robot inference.

## Installation

Install from PyPI:

```bash
pip3 install dexos-lite
```

Hardware-specific SDKs are loaded only when a robot controller starts. Install the SDK required by your robot before running robot inference.

## Common Options

`--api-key` can be passed as a command option or provided through an environment variable.

```bash
export DEXOS_API_KEY=ak_xxx
```

## Upload A Dataset

```bash
dexos-lite dataset upload /data/yellow_pepper \
  --api-key ak_xxx \
  --dataset-name yellow_pepper_dataset
```

Dataset uploads should use the `lerobot_v3` format. For the concrete layout, see the data format section.

Optional flags:

```bash
dexos-lite dataset upload /data/yellow_pepper \
  --dataset-name yellow_pepper_dataset \
  --dataset-format lerobot_v3 \
  --description "yellow pepper pick dataset"
```

The upload command validates the dataset, packages a directory as `tar.gz`, uploads it with progress display, retries the direct upload request, and prints a final success or failure message.

## Generate Robot Config

Generate `config.py` in the current directory:

```bash
dexos-lite robot config --robot-id mirror
```

Generate a config at a specific path:

```bash
dexos-lite robot config \
  --robot-id mirror \
  --path ./configs/mirror_config.py
```

Overwrite an existing config:

```bash
dexos-lite robot config \
  --robot-id mirror \
  --path ./config.py \
  --force
```

Supported `robot_id` values:

- `mirror`
- `cobot_magic`

Edit the generated config before inference to set controller ports, sensor serial numbers, and robot-specific options.
Inference requires at least one camera sensor image. Each image must be a JPEG or PNG data URL. Images larger than 448 * 448 are centered on a black square canvas and resized to 448 * 448 before the request is sent.

### Mirror Arm Service

For `mirror`, start the Mirror arm service before running inference.
Use the `mirror` documentation as the source of truth for exact service options.

```bash
conda activate airbot && sleep 1 && airbot_server -i can_left -p 50051
conda activate airbot && sleep 1 && airbot_server -i can_right -p 50053
```

## Run Robot Inference

```bash
dexos-lite robot infer \
  --api-key ak_xxx \
  --robot-config ./config.py \
  --prompt "pick up the red cube"
```

`--robot-config` accepts `path.py` or `path.py:attribute`. If no attribute is provided, `robot` is used.

`robot infer` runs for up to 600 seconds by default. Use `--time-limit` to set a different limit in seconds.

Before inference starts, the CLI calls `robot.start()` to start hardware resources, then calls `robot.reset_to_home()` to move the robot to its home pose.

When inference ends because of `Ctrl+C` or the time limit, the CLI asks for a score from 0 to 10 and an optional feedback message, then submits it to service. When inference is interrupted with `Ctrl+C`, the CLI calls `robot.reset_to_home()` again before closing the robot.

Use `Ctrl+C` to stop the inference loop.

Controller joint commands are smoothed with `DEXOS_INTERPOLATION_STEP=0.02` by
default. Set the all-uppercase environment variable to `0` to disable smoothing,
or to any positive float to change the joint-space interpolation step:

```bash
export DEXOS_INTERPOLATION_STEP=0
```

## Use A Custom Robot

`--robot-config` can point to any Python file that exposes a `BaseRobot` instance or factory. If no attribute is provided, `robot` is used.

Custom robots must implement `action_type`, `start()`, `get()`, `set(actions)`, `reset_to_home()`, and `close()`.

`get()` must return a dict with:

- `action`: required. JSON-serializable current robot state/action sent to service as top-level `action`; for common arm models this is usually a flat list such as `[j1, j2, j3, j4, j5, j6, gripper]`. Refer to the `/api/v1/action` [API docs](https://maas.dexmal.com/documentation#api-action) for action element ordering.
- `sensors`: required. Dict of camera name to base64-encoded JPEG or PNG data URL. `infer_robot` sends this to the service as top-level `images`. Provide at least one image. Images larger than 448 * 448 are centered on a black square canvas and resized to 448 * 448 before the request is sent. It supports at most three keys: `cam_high`, `cam_left_wrist`, and `cam_right_wrist`.

`infer_robot` sends the robot's `action_type` with each request. Use `ActionType.JOINT` for joint-space actions or `ActionType.EEF` for end-effector pose actions.

`set(actions)` receives one model action step from the inference response. If the model returns an action horizon, `infer_robot` calls `set()` once per step in order. The action layout and ordering are the same as the `action` returned by `get()`.

`reset_to_home()` is called once before inference starts and again when inference is interrupted with `Ctrl+C`. Custom robots should move the hardware to a safe home pose there; if there is no real home action, still return a JSON-serializable result such as `{"ok": True}`.

Create `custom_robot.py`:

```python
from __future__ import annotations

from typing import Any, Dict

from dexos_lite import ActionType
from dexos_lite.robot import BaseRobot


class CustomRobot(BaseRobot):
    @property
    def action_type(self) -> ActionType:
        return ActionType.JOINT

    def start(self) -> None:
        # Initialize SDKs, connect hardware, or start external services here.
        pass

    def get(self, *, sensor_timeout: float = 1.0) -> Dict[str, Any]:
        current_action = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        # Read cameras within sensor_timeout.
        # sensors requires at least one image and supports at most these three fields.
        sensors = {
            "cam_high": "data:image/jpeg;base64,...",
            # "cam_high": "data:image/png;base64,...",
            # "cam_left_wrist": "data:image/jpeg;base64,...",
            # "cam_right_wrist": "data:image/jpeg;base64,...",
        }
        return {"action": current_action, "sensors": sensors}

    def set(self, actions: Any) -> Dict[str, Any]:
        # Send actions to your robot SDK or hardware service here.
        return {"ok": True}

    def reset_to_home(self) -> Dict[str, Any]:
        # Move the robot to a safe home pose here.
        return {"ok": True}

    def close(self) -> None:
        pass


robot = CustomRobot()
```

Run inference with it:

```bash
dexos-lite robot infer \
  --api-key ak_xxx \
  --robot-config ./custom_robot.py:robot \
  --prompt "pick up the red cube"
```

You can also expose a factory, for example `def build_robot() -> CustomRobot: ...`, and pass `--robot-config ./custom_robot.py:build_robot`.
