Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions packages/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Extension Packages

This folder contains Python package splits for provider-specific integrations.
Each package re-exports the relevant subset of the core `rfx-sdk` so users can
install only what they need.

- `rfx-sdk-sim`: simulation backends and simulation-specific controllers
- `rfx-sdk-go2`: Unitree Go2 integrations
- `rfx-sdk-lerobot`: LeRobot recorder/export helpers
| Package | Install | Provides |
|---------|---------|----------|
| `rfx-sdk-go2` | `pip install rfx-sdk-go2` | `Go2Robot`, `Go2Backend`, `Go2Env`, `make_go2` factory |
| `rfx-sdk-sim` | `pip install rfx-sdk-sim` | `SimRobot`, `MockRobot`, `BaseEnv`, `VecEnv`, `make_vec_env` |
| `rfx-sdk-lerobot` | `pip install rfx-sdk-lerobot` | `collect`, `Dataset`, `Recorder`, `LeRobotPackageWriter`, Hub helpers |

The base Python import remains `rfx` (published as `rfx-sdk`).
2 changes: 1 addition & 1 deletion packages/rfx-go2/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "rfx-sdk-go2"
version = "0.2.0"
description = "Unitree Go2 integrations for rfx"
requires-python = ">=3.13"
dependencies = ["rfx-sdk>=0.2.0"]
dependencies = ["rfx-sdk[teleop]>=0.2.0", "torch>=2.2"]

[tool.setuptools]
package-dir = {"" = "src"}
Expand Down
35 changes: 33 additions & 2 deletions packages/rfx-go2/src/rfx_go2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
"""rfx-sdk-go2 package scaffold."""
"""rfx-sdk-go2 — Unitree Go2 integrations for rfx.

__all__ = []
Convenience package that re-exports Go2-specific components from the core
``rfx-sdk`` so users can ``pip install rfx-sdk-go2`` for a focused install.

Example::

from rfx_go2 import Go2Robot, Go2Backend
robot = Go2Robot(ip_address="192.168.123.161")
obs = robot.observe()
"""

from __future__ import annotations

from rfx.envs import Go2Env

try:
from rfx.real.go2 import Go2Backend, Go2Robot
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
"rfx-sdk-go2 requires torch. Install with: pip install rfx-sdk[teleop]"
) from exc

try:
from rfx.robot.lerobot import go2 as make_go2
except ModuleNotFoundError:
make_go2 = None # type: ignore[assignment]

__all__ = [
"Go2Backend",
"Go2Env",
"Go2Robot",
"make_go2",
]
2 changes: 1 addition & 1 deletion packages/rfx-lerobot/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "rfx-sdk-lerobot"
version = "0.2.0"
description = "LeRobot integration package for rfx"
requires-python = ">=3.13"
dependencies = ["rfx-sdk>=0.2.0", "lerobot"]
dependencies = ["rfx-sdk[collection]>=0.2.0"]

[tool.setuptools]
package-dir = {"" = "src"}
Expand Down
30 changes: 28 additions & 2 deletions packages/rfx-lerobot/src/rfx_lerobot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
"""rfx-sdk-lerobot package scaffold."""
"""rfx-sdk-lerobot — LeRobot integration package for rfx.

__all__ = []
Convenience package that re-exports LeRobot data-collection and dataset
components from the core ``rfx-sdk`` so users can
``pip install rfx-sdk-lerobot`` for a focused install.

Example::

from rfx_lerobot import collect, Dataset
dataset = collect("so101", "my-org/demos", episodes=10)
dataset.push()
"""

from __future__ import annotations

from rfx.collection import Dataset, Recorder, collect, from_hub, open_dataset, pull, push
from rfx.teleop.lerobot_writer import LeRobotExportConfig, LeRobotPackageWriter

__all__ = [
"Dataset",
"LeRobotExportConfig",
"LeRobotPackageWriter",
"Recorder",
"collect",
"from_hub",
"open_dataset",
"pull",
"push",
]
2 changes: 1 addition & 1 deletion packages/rfx-sim/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "rfx-sdk-sim"
version = "0.2.0"
description = "Simulation adapters and controllers for rfx"
requires-python = ">=3.13"
dependencies = ["rfx-sdk>=0.2.0"]
dependencies = ["rfx-sdk[sim-mock]>=0.2.0", "torch>=2.2"]

[tool.setuptools]
package-dir = {"" = "src"}
Expand Down
32 changes: 30 additions & 2 deletions packages/rfx-sim/src/rfx_sim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
"""rfx-sdk-sim package scaffold."""
"""rfx-sdk-sim — Simulation adapters and controllers for rfx.

__all__ = []
Convenience package that re-exports simulation components from the core
``rfx-sdk`` so users can ``pip install rfx-sdk-sim`` for a focused install.

Example::

from rfx_sim import SimRobot, MockRobot
robot = SimRobot.from_config("so101.yaml", backend="mock", num_envs=16)
obs = robot.observe()
"""

from __future__ import annotations

from rfx.envs import BaseEnv, Box, VecEnv, make_vec_env

try:
from rfx.sim import MockRobot, SimRobot
except ModuleNotFoundError as exc:
raise ModuleNotFoundError(
"rfx-sdk-sim requires torch. Install with: pip install rfx-sdk[sim-mock]"
) from exc

__all__ = [
"BaseEnv",
"Box",
"MockRobot",
"SimRobot",
"VecEnv",
"make_vec_env",
]
10 changes: 6 additions & 4 deletions rfx/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
"""Tests for rfx.decorators module."""

from __future__ import annotations

import importlib.util
from typing import Any

import pytest

from rfx.decorators import MotorCommands, policy
from rfx.robot.config import JointConfig, RobotConfig

TORCH_AVAILABLE = importlib.util.find_spec("torch") is not None

# ---------------------------------------------------------------------------
# @policy decorator
# ---------------------------------------------------------------------------


class TestPolicyDecorator:

def test_policy_bare(self) -> None:
"""@rfx.policy without parentheses."""

Expand Down Expand Up @@ -83,7 +87,6 @@ def _make_config(joints: list[tuple[str, int]], action_dim: int = 6) -> RobotCon


class TestMotorCommands:

def test_empty(self) -> None:
cmd = MotorCommands()
assert cmd.positions == {}
Expand Down Expand Up @@ -111,8 +114,8 @@ def test_from_positions(self) -> None:
assert cmd.config is config


@pytest.mark.skipif(not TORCH_AVAILABLE, reason="torch is required")
class TestMotorCommandsToTensor:

def test_to_tensor_basic(self) -> None:
config = _make_config(SO101_JOINTS)
cmd = MotorCommands({"gripper": 0.8, "elbow": -0.3}, config=config)
Expand Down Expand Up @@ -146,7 +149,6 @@ def test_to_tensor_no_config_raises(self) -> None:


class TestMotorCommandsToList:

def test_to_list_basic(self) -> None:
config = _make_config(SO101_JOINTS)
cmd = MotorCommands({"gripper": 0.8, "elbow": -0.3}, config=config)
Expand Down
9 changes: 8 additions & 1 deletion rfx/tests/test_so101_auto_pair.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from __future__ import annotations

import importlib.util
from pathlib import Path

import rfx.real.so101 as so101_mod
import pytest

TORCH_AVAILABLE = importlib.util.find_spec("torch") is not None
pytestmark = pytest.mark.skipif(not TORCH_AVAILABLE, reason="torch is required")

if TORCH_AVAILABLE:
import rfx.real.so101 as so101_mod


def test_auto_pair_persists_identity_across_port_renumber(monkeypatch, tmp_path: Path) -> None:
Expand Down
3 changes: 3 additions & 0 deletions rfx/tests/test_zenoh_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

import importlib.util

import pytest


Expand Down Expand Up @@ -162,6 +164,7 @@ def zenoh(connect, listen, shared_memory, key_prefix):
assert captured["shared_memory"] is False


@pytest.mark.skipif(not importlib.util.find_spec("torch"), reason="torch is required")
def test_go2_dds_backend_deprecation_warning() -> None:
"""Using dds_backend= should emit FutureWarning."""
import warnings
Expand Down
Loading