Skip to content
Open
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
56 changes: 49 additions & 7 deletions strands_robots/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
- Clean separation between robot control and policy inference
- Direct policy injection for maximum flexibility
- Multi-camera support with rich configuration options
- MuJoCo simulation backend (no GPU required)

Lazy Loading:
Heavy imports (Robot, tools, Gr00tPolicy) are deferred until first access.
Heavy imports are deferred so ``import strands_robots`` stays fast when lerobot/torch
are installed but not yet needed.
Heavy imports (Robot, tools, Gr00tPolicy, Simulation) are deferred until
first access. Heavy imports are deferred so ``import strands_robots`` stays
fast when lerobot/torch/mujoco are installed but not yet needed.

Light-weight symbols (Policy, MockPolicy, create_policy) are available
immediately since they don't pull in torch/lerobot.
Expand All @@ -25,15 +26,31 @@
import warnings as _warnings
from typing import Any

# Light-weight imports - no torch / lerobot dependency
# ------------------------------------------------------------------
# Light-weight imports — no torch / lerobot / mujoco dependency
# ------------------------------------------------------------------
from strands_robots.policies import MockPolicy, Policy, create_policy # noqa: F401

# ------------------------------------------------------------------
# Lazy-loaded heavy symbols

# ------------------------------------------------------------------
# Maps public name -> (module_path, attribute_name)
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
# Hardware robot
"Robot": ("strands_robots.robot", "Robot"),
"list_robots": ("strands_robots.registry", "list_robots"),
# Policies
"Gr00tPolicy": ("strands_robots.policies.groot", "Gr00tPolicy"),
# Simulation (MuJoCo)
"Simulation": ("strands_robots.simulation", "Simulation"),
"create_simulation": ("strands_robots.simulation.factory", "create_simulation"),
"list_backends": ("strands_robots.simulation.factory", "list_backends"),
"register_backend": ("strands_robots.simulation.factory", "register_backend"),
"SimWorld": ("strands_robots.simulation", "SimWorld"),
"SimRobot": ("strands_robots.simulation", "SimRobot"),
"SimObject": ("strands_robots.simulation", "SimObject"),
"SimCamera": ("strands_robots.simulation", "SimCamera"),
# Tools
"gr00t_inference": ("strands_robots.tools.gr00t_inference", "gr00t_inference"),
"lerobot_calibrate": ("strands_robots.tools.lerobot_calibrate", "lerobot_calibrate"),
"lerobot_camera": ("strands_robots.tools.lerobot_camera", "lerobot_camera"),
Expand All @@ -50,6 +67,11 @@
# Lazy-loaded
"Robot",
"Gr00tPolicy",
"Simulation",
"SimWorld",
"SimRobot",
"SimObject",
"SimCamera",
"gr00t_inference",
"lerobot_camera",
"lerobot_teleoperate",
Expand All @@ -59,12 +81,32 @@
]


# Auto-configure MuJoCo GL backend for headless environments BEFORE any
# module imports mujoco at the top level. MuJoCo locks the OpenGL backend
# at import time, so MUJOCO_GL must be set first.
#
# WHY EAGER: This MUST run at module import time, not lazily, because:
# 1. MuJoCo reads MUJOCO_GL only on first `import mujoco`
# 2. Any downstream code doing `from strands_robots.simulation import ...`
# triggers mujoco import via the lazy-load chain
# 3. If we defer to first use, the env var would be set too late
# This is the canonical location — strands_robots/simulation/__init__.py
# intentionally does NOT duplicate this call.
try:
from strands_robots.simulation.mujoco.backend import _configure_gl_backend

_configure_gl_backend()
Comment thread
cagataycali marked this conversation as resolved.
except (ImportError, AttributeError, OSError):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eager import on every import strands_robots. The comment correctly explains why this can't be lazy (MuJoCo locks MUJOCO_GL at first import mujoco), but two improvements worth considering:

  1. Cheap guard — skip when mujoco isn't installed, so users without the sim extra don't pay the import-attempt cost:
    import importlib.util as _ilu
    if _ilu.find_spec("mujoco") is not None:
        try:
            from strands_robots.simulation.mujoco.backend import _configure_gl_backend
            _configure_gl_backend()
        except (ImportError, AttributeError, OSError):
            pass
  2. Narrow importsfrom ... import _configure_gl_backend already loads the whole backend module. If backend.py pulls heavy deps for non-GL reasons, consider extracting _configure_gl_backend into its own light module.

Not a blocker, but import strands_robots is on every user's hot path.

pass


def __getattr__(name: str) -> Any: # noqa: N807
"""Lazy-load heavy modules on first attribute access.

This avoids importing torch, lerobot, numpy, pyserial, etc. at
This avoids importing torch, lerobot, numpy, mujoco, pyserial, etc. at
``import strands_robots`` time. The first access to e.g.
``strands_robots.Robot`` triggers the real import.
``strands_robots.Robot`` or ``strands_robots.Simulation`` triggers the
real import.
"""
if name in _LAZY_IMPORTS:
module_path, attr_name = _LAZY_IMPORTS[name]
Expand Down
Loading
Loading