Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ef77e3e
fix(assets): change debug to warning for missing download module (C9)
cagataycali Apr 3, 2026
5717d21
fix(simulation): log cleanup errors in __del__ instead of silencing (…
cagataycali Apr 3, 2026
38949dd
refactor(simulation): optional methods raise NotImplementedError (C19)
cagataycali Apr 3, 2026
a813f23
fix(model-registry): log asset manager availability state (C26)
cagataycali Apr 3, 2026
536d438
refactor(model-registry): move lazy imports to top-level try/except (…
cagataycali Apr 3, 2026
af2a857
fix(factory): add overwrite protection to register_backend (C22, C23,…
cagataycali Apr 3, 2026
65299a6
refactor(simulation): rename SimulationBackend to SimEngine (C15)
cagataycali Apr 3, 2026
43fd024
fix(simulation): remove mujoco references not in this PR (C13, C14)
cagataycali Apr 3, 2026
9c14376
fix(model-registry): reorder resolution — local assets before default…
cagataycali Apr 3, 2026
6292187
refactor(assets): thin __init__.py, move logic to manager.py (C3, C4,…
cagataycali Apr 3, 2026
8229ce1
refactor(assets): consolidate to STRANDS_ASSETS_DIR, deprecate STRAND…
cagataycali Apr 3, 2026
a609002
feat(simulation): add models dataclasses and download_assets tool
cagataycali Apr 3, 2026
fe39ec9
refactor(model-registry): simplify and document search paths (C25)
cagataycali Apr 3, 2026
cd19f47
docs(simulation): document required vs optional methods, improve rend…
cagataycali Apr 3, 2026
27e969f
docs: add environment variables and cache directory sections to READM…
cagataycali Apr 3, 2026
16e9f4b
docs(simulation): clarify get_observation/send_action/run_policy as f…
cagataycali Apr 3, 2026
2217260
style: apply ruff formatting to all PR files
cagataycali Apr 3, 2026
75705e6
refactor: drop STRANDS_URDF_DIR entirely — no deprecation, just remove
cagataycali Apr 3, 2026
21857d5
style: fix ruff formatting in model_registry.py (missing blank line)
cagataycali Apr 4, 2026
d91a7b7
fix: resolve 25 mypy errors, remove SimulationBackend alias, fix log …
strands-agent Apr 4, 2026
6f711ee
fix: resolve mypy errors in assets/manager.py and tools/download_asse…
cagataycali Apr 4, 2026
1dafad1
fix: resolve 2 mypy errors — correct type-ignore code, add robot_desc…
cagataycali Apr 5, 2026
c6e437b
feat: expand robot registry to 68 robots, add user registration API, …
cagataycali Apr 6, 2026
ef75a5d
fix: add [sim] extra with robot_descriptions dependency
cagataycali Apr 6, 2026
55d4044
fix: add orientation, mesh_path to SimEngine.add_object + simplify ra…
cagataycali Apr 6, 2026
f391314
feat: MuJoCo simulation backend — AgentTool with 35 actions
cagataycali Apr 1, 2026
ded8977
fix: address all review comments — ABC, thread-safety, injection, cle…
cagataycali Apr 1, 2026
ada6095
fix: resolve lint errors — import ordering, format, strict param
cagataycali Apr 1, 2026
b25663e
fix: acquire _lock around MuJoCo data mutations + sanitize all XML names
cagataycali Apr 1, 2026
af9d1f6
ci: add MuJoCo system deps (libosmesa6-dev + MUJOCO_GL=osmesa)
cagataycali Apr 1, 2026
1b759a7
feat: add [sim] extra with mujoco dependency
cagataycali Apr 1, 2026
567fd00
fix: rename [sim] extra to [sim-mujoco] per review
Apr 2, 2026
768111d
fix: rebase on simulation-foundation — SimulationBackend→SimEngine, u…
cagataycali Apr 6, 2026
8b7c55d
fix: resolve all mypy errors — mixin overrides, Optional types, impor…
cagataycali Apr 6, 2026
0a8366d
fix: properly fix mypy errors instead of blanket suppression
cagataycali Apr 6, 2026
696b423
fix: zero mypy suppressions — proper type declarations instead of dis…
cagataycali Apr 6, 2026
57eb5d0
feat(sim): use require_optional for imageio in policy_runner
cagataycali Apr 6, 2026
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
7 changes: 7 additions & 0 deletions .github/workflows/test-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ jobs:
python-version: '3.12'
cache: 'pip'

- name: Install system dependencies (OpenGL for MuJoCo)
run: |
sudo apt-get update
sudo apt-get install -y libosmesa6-dev

- name: Install dependencies
run: |
pip install --no-cache-dir hatch
Expand All @@ -35,4 +40,6 @@ jobs:
run: hatch run lint

- name: Run tests
env:
MUJOCO_GL: osmesa
run: hatch run test -x --strict-markers
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,31 @@ while True:
agent.tool.gr00t_inference(action="stop", port=8000)
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `STRANDS_ASSETS_DIR` | Custom directory for robot model assets (MJCF, meshes) | `~/.strands_robots/assets/` |
| `GROOT_API_TOKEN` | API token for GR00T inference service | — |

### Cache Directory

Robot model assets (MJCF XML files and meshes) are cached in:

```
~/.strands_robots/
└── assets/ # Downloaded robot models (from robot_descriptions / MuJoCo Menagerie)
├── trs_so_arm100/
├── franka_emika_panda/
└── ...
```

To clear the cache: `rm -rf ~/.strands_robots/assets/`

To change the cache location: `export STRANDS_ASSETS_DIR=/path/to/custom/dir`

## Contributing

We welcome contributions! Please see:
Expand Down
28 changes: 27 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,19 @@ groot-service = [
lerobot = [
"lerobot>=0.5.0,<0.6.0",
]
sim = [
Copy link
Copy Markdown

@yinsong1986 yinsong1986 Apr 9, 2026

Choose a reason for hiding this comment

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

shall we keep sim as a placeholder dependency for later integration with Strands-Robots-Sim? maybe just put "robot_descriptions>=1.11.0,<2.0.0" into sim-mujoco? @cagataycali

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question. I think moving robot_descriptions into [sim-mujoco] makes sense for now — [sim] alone isn't useful to anyone today and having an empty placeholder extra is confusing.

When a second backend arrives (Isaac Sim, PyBullet, etc.), we can extract the shared asset dep into a [sim] base at that point — easy refactor since it's just pyproject.toml.

So the change would be:

sim-mujoco = [
    "mujoco>=3.0.0,<4.0.0",
    "robot_descriptions>=1.11.0,<2.0.0",
    "imageio>=2.28.0,<3.0.0",
    "imageio-ffmpeg>=0.4.0,<1.0.0",
]

And remove the [sim] extra entirely (plus update [all] to drop it).

Will push the fix once confirmed.


🤖 AI agent response. Strands Agents. Feedback welcome!

"robot_descriptions>=1.11.0,<2.0.0",
]
sim-mujoco = [
"mujoco>=3.0.0,<4.0.0",
"imageio>=2.28.0,<3.0.0",
"imageio-ffmpeg>=0.4.0,<1.0.0",
]
all = [
"strands-robots[groot-service]",
"strands-robots[lerobot]",
"strands-robots[sim]",
"strands-robots[sim-mujoco]",
]
dev = [
"pytest>=6.0,<9.0.0",
Expand Down Expand Up @@ -125,7 +135,7 @@ ignore_missing_imports = false

# Third-party libs without type stubs
[[tool.mypy.overrides]]
module = ["lerobot.*", "gr00t.*", "draccus.*", "msgpack.*", "zmq.*", "huggingface_hub.*", "serial.*", "psutil.*", "torch.*", "torchvision.*", "transformers.*", "einops.*"]
module = ["lerobot.*", "gr00t.*", "draccus.*", "msgpack.*", "zmq.*", "huggingface_hub.*", "serial.*", "psutil.*", "torch.*", "torchvision.*", "transformers.*", "einops.*", "robot_descriptions.*", "mujoco.*", "imageio.*"]
ignore_missing_imports = true

# @tool decorator injects runtime signatures mypy cannot check
Expand Down Expand Up @@ -158,6 +168,22 @@ module = ["strands_robots.registry.*"]
warn_return_any = false
disallow_untyped_defs = false

# MuJoCo simulation — mixins use cooperative self._world patterns
# attr-defined: Mixins access self._world/self._lock/etc. from Simulation (cooperative pattern)
# assignment: PEP 484 implicit Optional (= None on typed params)
# override: Subclass signatures extend base with extra params (orientation, mesh_path)
# misc: Multiple inheritance method resolution conflicts between mixin + ABC
[[tool.mypy.overrides]]
module = ["strands_robots.simulation.mujoco.*"]
disallow_untyped_defs = false
warn_return_any = false

# Async utils and dataset recorder — thin wrappers with dynamic types
[[tool.mypy.overrides]]
module = ["strands_robots._async_utils", "strands_robots.dataset_recorder"]
disallow_untyped_defs = false
warn_return_any = false

# Test files — relaxed type checking for mocks, fixtures, and test utilities
[[tool.mypy.overrides]]
module = ["tests.*", "tests_integ.*"]
Expand Down
31 changes: 31 additions & 0 deletions strands_robots/_async_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Async-to-sync helper for resolving coroutines in sync contexts."""

import asyncio
import concurrent.futures

# Module-level executor reused across calls to avoid creating threads at high frequency.
# A single worker is sufficient — we only need to offload one asyncio.run() at a time.
_EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=1, thread_name_prefix="strands_async")


def _resolve_coroutine(coro_or_result): # type: ignore[no-untyped-def]
"""Safely resolve a potentially-async result to a sync value.

Handles three cases:
1. Already a plain value → return as-is
2. Coroutine, no running loop → asyncio.run()
3. Coroutine, inside running loop → offload to reused thread

Args:
coro_or_result: Either a coroutine or an already-resolved value.

Returns:
The resolved (sync) value.
"""
if not asyncio.iscoroutine(coro_or_result):
return coro_or_result
try:
asyncio.get_running_loop()
return _EXECUTOR.submit(asyncio.run, coro_or_result).result()
except RuntimeError:
return asyncio.run(coro_or_result)
42 changes: 42 additions & 0 deletions strands_robots/assets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Robot Asset Manager for Strands Robots Simulation.

Assets are resolved from ``robot_descriptions`` package or downloaded from
MuJoCo Menagerie GitHub, cached in ``~/.strands_robots/assets/``.
Override with ``STRANDS_ASSETS_DIR`` env var.

Implementation lives in ``assets/manager.py`` — this file is thin exports only.
"""

from strands_robots.assets.manager import (
get_assets_dir,
get_robot_info,
get_search_paths,
list_available_robots,
resolve_model_dir,
resolve_model_path,
)
from strands_robots.registry import (
format_robot_table,
get_robot,
list_aliases,
list_robots,
list_robots_by_category,
)
from strands_robots.registry import (
resolve_name as resolve_robot_name,
)

__all__ = [
"resolve_model_path",
"resolve_model_dir",
"resolve_robot_name",
"get_robot_info",
"list_available_robots",
"list_robots_by_category",
"list_aliases",
"format_robot_table",
"get_assets_dir",
"get_search_paths",
"get_robot",
"list_robots",
]
18 changes: 18 additions & 0 deletions strands_robots/assets/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Download robot model assets — redirects to strands_robots.tools.download_assets.

The download tool is strands_robots/tools/download_assets.py and downloads to
~/.strands_robots/assets/ (user cache) instead of the bundled package dir.
"""

from strands_robots.tools.download_assets import (
_needs_download, # noqa: F401
download_assets,
download_robots,
get_user_assets_dir,
main,
)

__all__ = ["download_assets", "download_robots", "get_user_assets_dir", "main"]

if __name__ == "__main__":
main()
Loading
Loading